/***
Copyright (c) 1991, 1992 by Frank J. Edwards
All rights reserved.  (However, I'm flexible.  Go ahead and ask. :-)
***/

#ident "$Id: mount.c,v 1.10 1992/12/02 02:47:22 root Exp root $";

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/fstyp.h>
#include <sys/fsid.h>
#include <sys/mount.h>
#include <sys/mnttab.h>

#include "ados_vfs.h"		/* For flag bits */

#define CNULL	((char *)0)

#define DOS0	0x444F5300
#define DOS1	0x444F5301
#define DOS3	0x444F5303
#define DOS5	0x444F5305

char *tokens[] = {
    "ro", "rw",
    "f", "first",
    "l", "length",
    "r", "root",
    "flags",
    "hardlinks", "h",
    "softlinks", "s",
    "type", "t",
    CNULL,
};

extern int optind;
extern char *optarg;
char *prog;

void usage(int ier, char *fmt, ...)
{
    if (fmt && *fmt) {
	va_list args;

	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	va_end(args);
    }
    fprintf(stderr, "Usage:  %s [-o options] special mntdir\n", prog);
    fprintf(stderr, "valid options are:  ro, rw, f[irst]=#, l[ength]=#,\n");
    fprintf(stderr, "r[oot]=#, flags=#, h[ardlinks], s[oftlinks], t[ype].\n");
    exit( ier );
    /* NOTREACHED */
}

int main(int argc, char **argv)
{
    struct mntparam { int len; int root; int dos; } mntparam;
    int err, c, idx;
    int flags = 0;
    int fs_flag = DIR_CACHING, first = 0, len = 0;
    char *options, *value = CNULL, *spcl, *mntp;

    if (prog = strrchr(*argv, '/')) prog++;
    else prog = *argv;

    if (argc < 3)
	usage(10, NULL);

    idx = sysfs(GETFSIND, "ados");
    if (idx < 0) {
	perror("Can't obtain fstype for 'ados'");
	exit( 10 );
    }
    while ((c = getopt(argc, argv, "ro:")) != EOF) {
	switch (c) {
	case 'r' :
	    flags |= MS_RDONLY; break;
	case 'o' :
	    options = optarg;
	    while (options && *options) {
		err = getsubopt(&options, tokens, &value);
		switch (err) {
		case 0 :		/* Long-hand option setting */
		    flags |= MS_RDONLY; break;
		    break;
		case 2 :		/* First data block, default is 2 */
		case 3 :
		    if (!value)
			usage(10, "Missing value for first block.\n");
		    first = atol(value);
		    break;
		case 4 :		/* Last data block, no default. */
		case 5 :
		    if (value) {	/* Use -1 to scan the whole drive */
			len = atol(value);
			break;
		    }
		    usage(10, "Missing value for length.\n");
		    /* NOTREACHED */
		case 6 :		/* rb=# or root=# */
		case 7 :
		    if (!value)
			usage(10, "Missing value for root block.\n");
		    first = atol(value) * 2;
		    len = 1;
		    break;
		case 8 :		/* flags=# */
		    if (!value)
			usage(10, "Missing value for flags.\n");
		    fs_flag = atol(value);
		    break;
		case 1 :		/* read-write (unimplemented) */
		case 9 :		/* hardlinks (unimplemented) */
		case 10:		/* softlinks (unimplemented) */
		case 11:		/* type (unimplemented) */
		default :
		    usage(10, "Invalid parameter:  %s\n", value);
		    /* NOTREACHED */
		}
	    }
	}
    }
    spcl = argv[optind];
    mntp = argv[optind+1];
    if (first == 0) {
	err = open(spcl, O_RDONLY, 0);
	if (err < 0) {
	    usage(10, "Cannot open filesystem device (\"%s\").\n", spcl);
	    /* NOTREACHED */
	}
	first = 2;
	if (sizeof(c) == read(err, &c, sizeof(c))) {
	    switch (c) {
	    case DOS0 : fs_flag = DIR_CACHING | SMALL_BLOCK; break;
	    case DOS1 : fs_flag = DIR_CACHING; break;
	    case DOS3 : fs_flag = DIR_CACHING; break;
	    case DOS5 : fs_flag = DIR_CACHING; break;
	    default : usage(10, "Unknown filesystem type (0x%0x)\n", c);
		    /* NOTREACHED */
	    }
	}
	close(err);
    }
    mntparam.dos  = fs_flag;
    mntparam.root = (len - 1 + first) / 2;
    mntparam.len  = len;
    err = mount(spcl, mntp, flags | MS_DATA, idx, &mntparam, sizeof(mntparam));
    if (err) {
	usage(10, "failed: %s on %s: %s\n", spcl, mntp, strerror(errno));
	/* NOTREACHED */
    } else {
	/*
	 *  Create entry in /etc/mnttab.
	 *
	 *  The mount front-end does the creat("/etc/.mnt.lock", 0600) if
	 *  it doesn't already exist, and then fcntl(F_SETLKW) on it.
	 *  When the lock is released, no one else is accessing the mnttab
	 *  file and this binary will be executed.
	 */
	FILE *out = fopen(MNTTAB ".temp", "w");
	FILE *in  = fopen(MNTTAB, "r");
	struct mnttab mnt;
	char options[48], chartime[12];

	sprintf(options, "%s,nosuid,flags=%d,length=%d",
	    flags & MS_RDONLY ? "ro" : "rw",
	    fs_flag, len);
	sprintf(chartime, "%d", time(0));

	while (getmntent(in, &mnt) >= 0)
	    putmntent(out, &mnt);
	mnt.mnt_special = argv[optind];
	mnt.mnt_mountp  = argv[optind+1];
	mnt.mnt_fstype  = "ados";
	mnt.mnt_mntopts = options;
	mnt.mnt_time    = chartime;
	putmntent(out, &mnt);
	fclose(out);
	fclose(in);
	rename(MNTTAB ".temp", MNTTAB);
    }
    return( 0 );
}
