/* 
 * $Id: rorw.c,v 1.1 1992/01/31 20:13:15 ejb Exp $
 * $Source: /home/ejb/source/util/RCS/rorw.c,v $
 * $Author: ejb $
 *
 * This program is used to change a disk from readonly to read/write or 
 * vice-versa by changing the flag on the vfs structure in the kernel.
 * It does this my going through kernel memory.
 * 
 * It is very operating-system dependent as it modifies internal kernel 
 * data structures.
 */

#if !defined(lint) && !defined(SABER) || defined(RCS_HDRS)
static char *rcsid = "$Id: rorw.c,v 1.1 1992/01/31 20:13:15 ejb Exp $";
#endif /* !lint && !SABER || RCS_HDRS */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <nlist.h>
#include <sys/types.h>
#include <sys/time.h>		/* for vnode.h */
#include <sys/file.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/user.h>

#define KERNEL "/vmunix"
#define MEM "/dev/kmem"
#define UNAME "_uunix"

#define STAT_FLAG "-stat"
#define RO_FLAG "-ro"
#define RW_FLAG "-rw"

#ifndef __STDC__
#define const
#else
#define const
#endif /* __STDC__ */

typedef enum {a_stat, a_ro, a_rw} action_t;

extern int errno;
extern char *sys_errlist[];

static char *whoami;


#ifdef __STDC__
void usage(void)
#else
void usage()
#endif /* __STDC__ */
{
    fprintf(stderr, "usage: %s pathname [%s | %s | %s]\n", whoami,
	    STAT_FLAG, RO_FLAG, RW_FLAG);
    fprintf(stderr, "  pathname is the name of any directory on the ");
    fprintf(stderr, "filesystem you wish to change.\n");
    fprintf(stderr, "  %s causes the currente state to be printer.\n",
	    STAT_FLAG);
    fprintf(stderr, "  %s means to make the filesystem readonly.\n",
	    RO_FLAG);
    fprintf(stderr, "  %s means to make the filesystem read/write.\n",
	    RW_FLAG);
    exit(1);
}


#ifdef __STDC__
int open_kernel(void)
#else
int open_kernel()
#endif /* __STDC__ */
{
    int k;

    if ((k = open(MEM, O_RDWR, 0)) == -1) {
	fprintf(stderr, "%s: failure opening %s read/write: %s\n",
		whoami, MEM, sys_errlist[errno]);
	exit(1);
    }

    return (k);   
}

#ifdef __STDC__
void safe_lseek(int fd, int offset)
#else
void safe_lseek(fd, offset)
  int fd;
  int offset;
#endif /* __STDC__ */
{
    if (lseek(fd, offset, L_SET) == -1) {
	fprintf(stderr, "%s: lseek failed: %s\n", whoami, sys_errlist[errno]);
	exit(1);
    }
}


#ifdef __STDC__
void safe_read(int fd, char *buf, int len)
#else
void safe_read(fd, buf, len)
  int fd;
  char *buf;
  int len;
#endif /* __STDC__ */
{
    if (read(fd, buf, len) != len) {
	fprintf(stderr, "%s: read failed: %s\n", whoami, sys_errlist[errno]);
	exit(1);
    }
}


#ifdef __STDC__
void safe_write(int fd, char *buf, int len)
#else
void safe_write(fd, buf, len)
  int fd;
  char *buf;
  int len;
#endif /* __STDC__ */
{
    if (write(fd, buf, len) != len) {
	fprintf(stderr, "%s: write failed: %s\n", whoami, sys_errlist[errno]);
	exit(1);
    }
}



#ifdef __STDC__
void rorw(int k, action_t action)
#else
void rorw(k, action)
  int k;
  action_t action;
#endif /* __STDC__ */
{
    struct nlist n[2];
    short short_value;
    short u_cmask;
    int value;
    short pid;
    int vfs_flag;

    /* calculate offsets */
    const int u_cmask_offset = (int)&(((struct user *)0)->u_cmask);
    const int u_cdir_offset = (int)&(((struct user *)0)->u_cdir);
    const int v_vfsp_offset = (int)&(((struct vnode *)0)->v_vfsp);
    const int vfs_flag_offset = (int)&(((struct vfs *)0)->vfs_flag);

    /* Get umask for sanity checking purposes */
    u_cmask = umask(0);
    (void) umask(u_cmask);


    /* Look up the symbol as defined in UNAME in the kernel */
    n[0].n_name = UNAME;
    n[1].n_name = NULL;

    switch (nlist(KERNEL, n)) {
      case -1:
	fprintf(stderr, "%s: failure opening kernel symbol table: %s\n", 
		whoami, sys_errlist[errno]);
	exit(1);
	break;
      case 0:
	value = n[0].n_value;
	break;
      default:
	fprintf(stderr, "%s: unable to find %s in %s.\n", whoami, n[0].n_name,
		KERNEL);
	exit(1);
	break;
    }

    /* value == offset of uunix */
    /* seek to uunix and read value */
    safe_lseek(k, value);
    safe_read(k, &value, sizeof(value));

    /* value == offset of *uunix */
    /* seek to uunix->u_cmask and read value */
    safe_lseek(k, value + u_cmask_offset);
    safe_read(k, &short_value, sizeof(short_value));

    /* SANITY CHECK: make sure this is the actual umask */
    if (u_cmask != short_value) {
	fprintf(stderr, 
		"%s: umask from kernel (%o) does not match umask (%o)!\n",
		whoami, short_value, u_cmask);
	exit(2);
    }

    /* value == offset of *uunix */
    /* seek to u.u_cdir and read value */
    safe_lseek(k, value + u_cdir_offset);
    safe_read(k, &value, sizeof(value));

    /* value == offset of u.u_cdir */
    /* seek to u.u_cdir->v_vfsp and read value */
    safe_lseek(k, value + v_vfsp_offset);
    safe_read(k, &value, sizeof(value));

    /* Set value to offset to u.u_cdir->v_vfsp->vfs_flag */
    value += vfs_flag_offset;

    /* value == offset to u.u_cdir->v_vfsp */
    /* seek to u.u_cdir->v_vfsp->vfs_flag and read vfs_flag */
    safe_lseek(k, value);
    safe_read(k, &vfs_flag, sizeof(vfs_flag));

    printf("The value of vfs_flag is 0x%08x\n", vfs_flag);

    switch (action) {
      case a_stat:
	break;
      case a_ro:
	vfs_flag |= VFS_RDONLY;
	break;
      case a_rw:
	vfs_flag &= ~VFS_RDONLY;
	break;
      default:
	fprintf(stderr, "%s: internal error: default reached in rorw!\n",
		whoami);
	exit(3);
    }

    if (action != a_stat) {
	/* seek to vfs_flag */
	safe_lseek(k, value);
    
	/* write new value */
	safe_write(k, &vfs_flag, sizeof(vfs_flag));

	printf("The value vfs_flag has been changed to 0x%08x.\n", vfs_flag);
    }
}


#ifdef __STDC__
main(int argc, char *argv[])
#else
main(argc, argv)
  int argc;
  char *argv[];
#endif /* __STDC__ */
{
    char *pathname;
    char *flag;
    action_t action;
    int k;

    if ((whoami = strrchr(argv[0], '/')) == NULL)
	whoami = argv[0];
    else
	whoami++;

    if (argc != 3)
	usage();

    pathname = argv[1];
    flag = argv[2];

    if (strcmp(flag, STAT_FLAG) == 0)
	action = a_stat;
    else if (strcmp(flag, RO_FLAG) == 0)
	action = a_ro;
    else if (strcmp(flag, RW_FLAG) == 0)
	action = a_rw;
    else
	usage();

    /* First, attempt to cd to the given directory. */

    if (chdir(pathname) == -1) {
	fprintf(stderr, "%s: cd %s failed: ", whoami, pathname);
	perror(NULL);
	exit(1);
    }

    k = open_kernel();
    rorw(k, action);

    (void) close(k);

    return(0);
}
