/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:mount.c 12.0$ */
/* $ACIS:mount.c 12.0$ */
/* $Source: /ibm/acis/usr/src/etc/RCS/mount.c,v $ */

#ifndef lint
static char *rcsid = "$Header:mount.c 12.0$";
#endif

#include <sys/nfs_defines.h>
#ifndef VFS

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)mount.c	5.2 (Berkeley) 11/21/85";
#endif not lint

/*
 * mount
 */
#include <sys/param.h>

#include <stdio.h>
#include <fstab.h>
#include <mtab.h>
#include <errno.h>

#define	DNMAX	(sizeof (mtab[0].m_dname) - 1)
#define	PNMAX	(sizeof (mtab[0].m_path) - 1)

struct	mtab mtab[NMOUNT];

int	all;
int	ro;
int	fake;
int	verbose;
char	*index(), *rindex();
char *progname;

main(argc, argv)
	int argc;
	char **argv;
{
	register struct mtab *mp;
	register char *np;
	int mf;
	char *type = FSTAB_RW;
	progname = argv[0];

	mf = open("/etc/mtab", 0);
	read(mf, (char *)mtab, sizeof (mtab));
	if (argc == 1) {
		for (mp = mtab; mp < &mtab[NMOUNT]; mp++)
			if (mp->m_path[0] != '\0')
				prmtab(mp);
		exit(0);
	}
top:
	if (argc > 1) {
		if (!strcmp(argv[1], "-a")) {
			all++;
			argc--, argv++;
			goto top;
		}
		if (!strcmp(argv[1], "-r")) {
			type = FSTAB_RO;
			argc--, argv++;
			goto top;
		}
		if (!strcmp(argv[1], "-f")) {
			fake++;
			argc--, argv++;
			goto top;
		}
		if (!strcmp(argv[1], "-v")) {
			verbose++;
			argc--, argv++;
			goto top;
		}
	}
	if (all) {
		struct fstab *fsp;

		if (argc > 1)
			goto argcnt;
		close(2); dup(1);
		if (setfsent() == 0)
			perror(FSTAB), exit(1);
		while ((fsp = getfsent()) != 0) {
			if (strcmp(fsp->fs_file, "/") == 0)
				continue;
			if (strcmp(fsp->fs_type, FSTAB_RO) &&
			    strcmp(fsp->fs_type, FSTAB_RW) &&
			    strcmp(fsp->fs_type, FSTAB_RQ))
				continue;
			mountfs(fsp->fs_spec, fsp->fs_file, fsp->fs_type);
		}
		exit(0);
	}
	if (argc == 2) {
		struct fstab *fs;

		if (setfsent() == 0)
			perror(FSTAB), exit(1);
		fs = getfsfile(argv[1]);
		if (fs == NULL)
			goto argcnt;
		if (strcmp(fs->fs_type, FSTAB_RO) &&
		    strcmp(fs->fs_type, FSTAB_RW) &&
		    strcmp(fs->fs_type, FSTAB_RQ))
			goto argcnt;
		mountfs(fs->fs_spec, fs->fs_file, fs->fs_type);
		exit(0);
	}
	if (argc != 3) {
argcnt:
		fprintf(stderr,
    "usage: %s [ -a ] [ -r ] [ -f ] [ -v ] [ special dir ] [ dir ]\n",progname);
		exit(1);
	}
	mountfs(argv[1], argv[2], type);
}

prmtab(mp)
	register struct mtab *mp;
{

	printf("%s on %s", mp->m_dname, mp->m_path);
	if (strcmp(mp->m_type, FSTAB_RO) == 0)
		printf("\t(read-only)");
	if (strcmp(mp->m_type, FSTAB_RQ) == 0)
		printf("\t(with quotas)");
	putchar('\n');
}

mountfs(spec, name, type)
	char *spec, *name, *type;
{
	register char *np;
	register struct mtab *mp;
	int mf;

	if (!fake) {
		if (mount(spec, name, strcmp(type, FSTAB_RO) == 0) < 0) {
			extern int errno;
			char *cp;

			fprintf(stderr, "%s on ", spec);
			switch (errno) {

			case EMFILE:
				cp = "Mount table full";
				break;

			case EINVAL:
				cp = "Bogus super block";
				break;

			default:
				perror(name);
				return;
			}
			fprintf(stderr, "%s: %s\n", name, cp);
			return;
		}
		/* we don't do quotas.... */
		if (strcmp(type, FSTAB_RQ) == 0)
			type = FSTAB_RW;
	}
	np = index(spec, '\0');
	while (*--np == '/')
		*np = '\0';
	np = rindex(spec, '/');
	if (np) {
		*np++ = '\0';
		spec = np;
	}
	for (mp = mtab; mp < &mtab[NMOUNT]; mp++)
		if (strcmp(mp->m_dname, spec) == 0)
			goto replace;
	for (mp = mtab; mp < &mtab[NMOUNT]; mp++)
		if (mp->m_path[0] == '\0')
			goto replace;
	return;
replace:
	strncpy(mp->m_dname, spec, DNMAX);
	mp->m_dname[DNMAX] = '\0';
	strncpy(mp->m_path, name, PNMAX);
	mp->m_path[PNMAX] = '\0';
	strcpy(mp->m_type, type);
	if (verbose)
		prmtab(mp);
	mp = mtab + NMOUNT - 1;
	while (mp > mtab && mp->m_path[0] == '\0')
		--mp;
	mf = creat("/etc/mtab", 0644);
	write(mf, (char *)mtab, (mp - mtab + 1) * sizeof (struct mtab));
	close(mf);
	return;
}

#else !VFS

/* @(#)mount.c	1.2 87/07/23 3.2/4.3NFSSRC */
/* @(#)mount.c	1.2 86/11/06 NFSSRC */
#ifndef lint
static char *sccsid = "@(#)mount.c	4.10 (Berkeley) 5/28/83";
#endif

/*
 * Copyright (c) 1985 Sun Microsystems, Inc.
 */

#ifndef NFS
#define NFS
#endif !NFS

#ifndef AFS
#define AFS
#endif !AFS

#ifndef DFS
#define DFS
#endif !DFS

/*
 * mount
 */
#include <sys/param.h>
#include <rpc/rpc.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <nfs/nfs.h>
#include <rpcsvc/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netdb.h>
#include <stdio.h>
#include <mntent.h>
#include <sys/mount.h>
#include <afs/afs.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <dfs/dfs.h>
#include <grp.h>
#include <pwd.h>

int	ro = 0;
int	quota = 0;
int	fake = 0;
int	freq = 1;
int	passno = 2;
int	all = 0;
int	verbose = 0;
int	printed = 0;


#define	NRETRY	10000	/* number of times to retry a mount request */
#define	BGSLEEP	5	/* initial sleep time for background mount in seconds */
#define MAXSLEEP 120	/* max sleep time for background mount in seconds */
/*
 * Fake errno for RPC failures that don't map to real errno's
 */
#define ERPC	(10000)

extern int errno;

char	*index(), *rindex();
char	host[MNTMAXSTR];
char	name[MNTMAXSTR];
char	dir[MNTMAXSTR];
char	type[MNTMAXSTR];
char	opts[MNTMAXSTR];

/*
 * Structure used to build a mount tree.  The tree is traversed to do
 * the mounts and catch dependancies
 */
struct mnttree {
	struct mntent *mt_mnt;
	struct mnttree *mt_sib;
	struct mnttree *mt_kid;
};
struct mnttree *maketree();
char	*getopt();
char	*xmalloc();

main(argc, argv)
	int argc;
	char **argv;
{
	struct mntent mnt;
	struct mntent *mntp;
	struct hostent *hp;
	FILE *mnttab;
	char *options;
	char *colon,*comma,*slash,*dash;
	struct mnttree *mtree;

	if (argc == 1) {
		mnttab = setmntent(MOUNTED, "r");
		while ((mntp = getmntent(mnttab)) != NULL) {
			if (strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0) {
				continue;
			}
			printent(mntp);
		}
		endmntent(mnttab);
		exit(0);
	}

	close(2);
	if (dup2(1, 2) < 0) {
		perror("dup");
		exit(1);
	}

	opts[0] = '\0';
	type[0] = '\0';

	/*
	 * Set options
	 */
	while (argc > 1 && argv[1][0] == '-') {
		options = &argv[1][1];
		while (*options) {
			switch (*options) {
			case 'a':
				all++;
				break;
			case 'f':
				fake++;
				break;
			case 'o':
				if (argc < 3) {
					usage();
				}
				strcpy(opts, argv[2]);
				argv++;
				argc--;
				break;
			case 'p':
				if (argc != 2) {
					usage();
				}
				printmtab(stdout);
				exit(0);
			case 'q':
				quota++;
				break;
			case 'r':
				ro++;
				break;
			case 't':
				if (argc < 3) {
					usage();
				}
				if (strcmp(argv[2], MNTTYPE_42) == 0 || strcmp(argv[2], MNTTYPE_43) == 0)
					strcpy(type, MNTTYPE_UFS);
				else
					strcpy(type, argv[2]);
				argv++;
				argc--;
				break;
			case 'v':
				verbose++;
				break;
			default:
				fprintf(stderr, "mount: unknown option: %c\n",
				    *options);
				usage();
			}
			options++;
		}
		argc--, argv++;
	}

	if (geteuid() != 0) {
		fprintf(stderr, "Must be root to use mount\n");
		exit(1);
	}

	if (all) {
		if (argc != 1) {
			usage();
		}
		mnttab = setmntent(MNTTAB, "r");
		if (mnttab == NULL) {
			fprintf(stderr, "mount: ");
			perror(MNTTAB);
			exit(1);
		}
		mtree = NULL;
		while ((mntp = getmntent(mnttab)) != NULL) {
			if (strcmp(mntp->mnt_type, MNTTYPE_42) == 0 || strcmp(mntp->mnt_type, MNTTYPE_43) == 0)
				mntp->mnt_type = MNTTYPE_UFS;
			if ((strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0) ||
			    (strcmp(mntp->mnt_type, MNTTYPE_SWAP) == 0) ||
			    hasmntopt(mntp, MNTOPT_NOAUTO) ||
			    (strcmp(mntp->mnt_dir, "/") == 0) ) {
				continue;
			}
			if (type[0] != '\0' &&
			    strcmp(mntp->mnt_type, type) != 0) {
				continue;
			}
			mtree = maketree(mtree, mntp);
		}
		endmntent(mnttab);
		mounttree(mtree);
		exit(0);
	}

	/*
	 * Command looks like: mount <dev>|<dir>
	 * we walk through /etc/fstab til we match either fsname or dir.
	 */
	if (argc == 2) {
		mnttab = setmntent(MNTTAB, "r");
		if (mnttab == NULL) {
			fprintf(stderr, "mount: ");
			perror(MNTTAB);
			exit(1);
		}
		while ((mntp = getmntent(mnttab)) != NULL) {
			if ((strcmp(mntp->mnt_type, MNTTYPE_IGNORE) == 0) ||
			    (strcmp(mntp->mnt_type, MNTTYPE_SWAP) == 0) ) {
				continue;
			}
			if ((strcmp(mntp->mnt_fsname, argv[1]) == 0) ||
			    (strcmp(mntp->mnt_dir, argv[1]) == 0) ) {
				if (opts[0] != '\0') {
					char *topts;
					/*
					 * override fstab with command line
					 * options
					 */
					topts = mntp->mnt_opts;
					mntp->mnt_opts = opts;
					mounttree(maketree(NULL, mntp));
					mntp->mnt_opts = topts;
				} else {
					mounttree(maketree(NULL, mntp));
				}
				exit(0);
			}
		}
		fprintf(stderr, "mount: %s not found in %s\n", argv[1], MNTTAB);
		exit(0);
	}

	if (argc != 3) {
		usage();
	}
	strcpy(dir, argv[2]);
	strcpy(name, argv[1]);

	/*
	 * Check for file system names of the form
	 *     host:path
	 * make these type nfs
	 */
	colon = index(name, ':');
	if (colon) {
		if (type[0] != '\0' && strcmp(type, "nfs") != 0) {
			fprintf(stderr,"%s: %s; must use type nfs\n",
			    "mount: remote file system", name);
			usage();
		}
		strcpy(type, MNTTYPE_NFS);
	}
	/*
	 * now chech for comma separated lists (afs) file systems.
	 */
	comma = index(name,',');
	if (comma) {
		if (type[0] != '\0' && strcmp(type, "afs") != 0) {
			fprintf(stderr,"%s: %s; must use type afs\n",
			    "mount: remote servers", name);
			usage();
		}
		strcpy(type, MNTTYPE_AFS);
	}
	/*
	 * if type is '0', either -t has been specified  or no colons or
	 * comma's appear in the name. This cannot be an nfs file system. 
	 * It still can be ufs, dfs, or afs (a single server specified).
	 * It's reasonable to expect dfs file systems to specify -t dfs or
	 * specify '-''s. It should also be reasonable to expect 1) afs servers
	 * names do not have any slashes '/', and 2) if name is a valid host
	 * name, it specifies a server.
	 */
	if (type[0] == '\0') {
		/*
		 * now check for '/' and '-' (dfs).
		 */
		slash = index(name,'/');
		if (slash) {
			/* we are not an afs file system! */
			dash = index(name,'-');
			if (dash) {
				/* we must be a dfs file system */
				strcpy(type, MNTTYPE_DFS);
			} else {
				/*
				 * we still could be a dfs file system,
				 * but most likely we're not.
				 */
				strcpy(type, MNTTYPE_UFS);
			}
		} else {
			/*
			 * if 'name' is not a hostname, this can't be afs..
			 */
			if ((hp = gethostbyname(name)) == NULL) {
				/* not afs...  */
				dash = index(name,'-');
				if (dash) {
					/* we must be a dfs file system */
					strcpy(type, MNTTYPE_DFS);
				} else {
					/*
					 * again, we still could be a dfs file
					 *  system, but most likely we're not.
				 	 */
					strcpy(type, MNTTYPE_UFS);
				}
			} else {
				/* we still could be a ufs, or dfs filesystem,
				 * but it's reasable to expect users not to
				 * name host as 'hd0h' or 'fd0' or 'hd0a'...
				 */
				strcpy(type, MNTTYPE_AFS);
			}
		}
	}
	if (dir[0] != '/') {
		fprintf(stderr, "mount: directory path must begin with '/'\n");
		exit(1);
	}

	if (opts[0] == '\0') {
		strcpy(opts, ro ? MNTOPT_RO : MNTOPT_RW);
		if (strcmp(type, MNTTYPE_UFS) == 0) {
			strcat(opts, ",");
			strcat(opts, quota ? MNTOPT_QUOTA : MNTOPT_NOQUOTA);
		}
	}

	if (strcmp(type, MNTTYPE_NFS) == 0) {
		passno = 0;
		freq = 0;
	}

	mnt.mnt_fsname = name;
	mnt.mnt_dir = dir;
	mnt.mnt_type = type;
	mnt.mnt_opts = opts;
	mnt.mnt_freq = freq;
	mnt.mnt_passno = passno;
	mounttree(maketree(NULL, &mnt));
}

/*
 * attempt to mount file system, return errno or 0
 */
mountfs(print, mnt)
	int print;
	struct mntent *mnt;
{
	int error;
	extern int errno;
	int type = -1;
	int flags = 0;
	static char opts[1024];
	char *optp, *optend;
	union data {
		struct ufs_args	ufs_args;
		struct nfs_args nfs_args;
		struct afs_args afs_args;
		struct dfs_args dfs_args;
#ifdef PCFS
		struct pc_args pc_args;
#endif
	} data;

	if (mounted(mnt)) {
		if (print) {
			fprintf(stderr, "mount: %s already mounted\n",
			    mnt->mnt_fsname);
		}
		return (0);
	}
	if (fake) {
		addtomtab(mnt);
		return (0);
	}
	if (strcmp(mnt->mnt_type, MNTTYPE_UFS) == 0 || strcmp(mnt->mnt_type, MNTTYPE_42) == 0 || strcmp(mnt->mnt_type, MNTTYPE_43) == 0) {
		type = MOUNT_UFS;
		error = mount_ufs(mnt, &data.ufs_args);
	} else if (strcmp(mnt->mnt_type, MNTTYPE_NFS) == 0) {
		type = MOUNT_NFS;
		error = mount_nfs(mnt, &data.nfs_args);
	} else if (strcmp(mnt->mnt_type, MNTTYPE_AFS) == 0) {
		type = MOUNT_AFS;
		error = mount_afs(mnt, &data.afs_args);
	} else if (strcmp(mnt->mnt_type, MNTTYPE_DFS) == 0) {
		type = MOUNT_DFS;
		error = mount_dfs(mnt, &data.dfs_args);
#ifdef PCFS
	} else if (strcmp(mnt->mnt_type, MNTTYPE_PC) == 0) {
		type = MOUNT_PC;
		error = mount_pc(mnt, &data.pc_args);
#endif
	} else {
		fprintf(stderr,
		    "mount: unknown file system type %s\n",
		    mnt->mnt_type);
		error = EINVAL;
	}

	if (error) {
		return (error);
	}

	flags |= (hasmntopt(mnt, MNTOPT_RO) == NULL) ? 0 : M_RDONLY;
	flags |= (hasmntopt(mnt, MNTOPT_NOSUID) == NULL) ? 0 : M_NOSUID;
#ifdef DEBUG
	fprintf(stderr, "mount: about to call mount() syscall\n");
	fprintf(stderr, "mount: type=%d mnt_dir=%s flags=0x%x\n", type, mnt->mnt_dir, flags);
#endif DEBUG
	if (mount(type, mnt->mnt_dir, flags, &data) < 0) {
		if (print) {
			fprintf(stderr, "mount: %s on ", mnt->mnt_fsname);
			perror(mnt->mnt_dir);
		}
		return (errno);
	}
	if ((optp = hasmntopt(mnt, MNTOPT_QUOTA)) != NULL) {
		/*
		 * cut out quota option and put in noquota option, for mtab
		 */
		optend = index(optp, ',');
		if (optp != mnt->mnt_opts)
			optp--;			/* back up to ',' */
		if (optend == NULL)
			*optp = '\0';
		else
			while (*optp++ = *optend++)
				;
		sprintf(opts, "%s,%s", mnt->mnt_opts, MNTOPT_NOQUOTA);
		mnt->mnt_opts = opts;
	}
	addtomtab(mnt);
	return (0);
}

mount_ufs(mnt, args)
	struct mntent *mnt;
	struct ufs_args *args;
{
	static char name[MNTMAXSTR];

	strcpy(name, mnt->mnt_fsname);
	args->fspec = name;
	
	return (0);
}

mount_dfs(mnt, args)
	struct mntent *mnt;
	struct dfs_args *args;
{
	static char name[MNTMAXSTR];

	strcpy(name, mnt->mnt_fsname);
	args->fspec = name;
	args->mnt_flags = 0;
	args->def_uid = args->def_gid = 0;
	if (hasmntopt(mnt,"user") != NULL) {
		char *user;
		struct passwd *pw,*getpwnam();
		user = getopt(mnt,"user");
		pw = getpwnam(user);

		if (pw == NULL) {
			fprintf(stderr,"Couldn't find user %s\n",user);
		} else {
			args->def_uid = pw->pw_uid;
		}
    	}
	if (hasmntopt(mnt,"group") != NULL) {
		char *group;
		struct group *gr,*getgrnam();
		group = getopt(mnt,"group");
		gr = getgrnam(group);

		if (gr == NULL) {
			fprintf(stderr,"Couldn't find group %s\n",group);
		} else {
			args->def_gid = gr->gr_gid;
		}
    	}
	if (hasmntopt(mnt,"dst") != NULL) {
		struct	tm *tm,*localtime();
		struct	timeval current;
		struct 	timezone zone;

		gettimeofday(&current,&zone);
		tm = localtime(&current.tv_sec);
		if (tm->tm_isdst) {
			args->mnt_flags |= DFS_DST;
		}
	}
	/* hide dos files marked hidden or system */
	if (hasmntopt(mnt,"hide") != NULL) {
		args->mnt_flags |= DFS_HIDE_HIDDEN;
	}
	/* allow the default user/group to be changed with chown */
	if (hasmntopt(mnt,"change_user") != NULL) {
		args->mnt_flags |= DFS_CHANGE_USER;
	}
	if (hasmntopt(mnt,"change_group") != NULL) {
		args->mnt_flags |= DFS_CHANGE_GROUP;
	}
	if (hasmntopt(mnt,"change_all") != NULL) {
		args->mnt_flags |= (DFS_CHANGE_USER|DFS_CHANGE_GROUP);
	}
	/* display dos file system as upper case rather than lower case */
	if (hasmntopt(mnt,"upper") != NULL) {
		args->mnt_flags |= DFS_UPPER;
	}
	/* force the unix name to match exactly (don't drop characters on
	 * mapping. If upper is defined, make case sensitive matches.
	 */
	if (hasmntopt(mnt,"nomap") != NULL) {
		args->mnt_flags |= DFS_NOMAP_NAME;
	}
	/* the next several determine default accesses. */
	if (hasmntopt(mnt,"noexecute") != NULL) {
		args->mnt_flags |= DFS_NOEXECUTE;
	}
	if (hasmntopt(mnt,"user_only") != NULL) {
		args->mnt_flags |= DFS_USER;
	}
	if (hasmntopt(mnt,"group_only") != NULL) {
		args->mnt_flags |= DFS_GROUP;
	}
	
	return (0);
}

mount_nfs(mnt, args)
	struct mntent *mnt;
	struct nfs_args *args;
{
	static struct sockaddr_in sin;
	struct hostent *hp;
	static struct fhstatus fhs;
	char *cp;
	char *hostp = host;
	char *path;
	int s;
	struct timeval timeout;
	CLIENT *client;
	enum clnt_stat rpc_stat;
	u_short port;

	cp = mnt->mnt_fsname;
	while ((*hostp = *cp) != ':') {
		if (*cp == '\0') {
			fprintf(stderr,
			    "mount: nfs file system; use host:path\n");
			return (1);
		}
		hostp++;
		cp++;
	}
	*hostp = '\0';
	path = ++cp;
	/*
	 * Get server's address
	 */
	if ((hp = gethostbyname(host)) == NULL) {
		/*
		 * XXX
		 * Failure may be due to yellow pages, try again
		 */
		if ((hp = gethostbyname(host)) == NULL) {
			fprintf(stderr,
			    "mount: %s not in hosts database\n", host);
			return (1);
		}
	}

	args->flags = 0;
	if (hasmntopt(mnt, MNTOPT_SOFT) != NULL) {
		args->flags |= NFSMNT_SOFT;
	}
	if (hasmntopt(mnt, MNTOPT_INTR) != NULL) {
		args->flags |= NFSMNT_INT;
	}

	/*
	 * get fhandle of remote path from server's mountd
	 */
	bzero(&sin, sizeof(sin));
	bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
	sin.sin_family = AF_INET;
	timeout.tv_usec = 0;
	timeout.tv_sec = 20;
	s = RPC_ANYSOCK;
#ifdef DEBUG
	fprintf(stderr, "mount: clntudp_create()\n");
#endif DEBUG
	if ((client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS,
	    timeout, &s)) == NULL) {
#ifdef DEBUG
	fprintf(stderr, "mount: clntudp_create() failed\n");
#endif DEBUG
		if (!printed) {
			fprintf(stderr, "mount: %s server not responding",
			    mnt->mnt_fsname);
			clnt_pcreateerror("");
			printed = 1;
		}
		return (ETIMEDOUT);
	}
	if (! bindresvport(s)) {
		fprintf(stderr,"Warning: mount: cannot do local bind\n");
	}
	client->cl_auth = authunix_create_default();
	timeout.tv_usec = 0;
	timeout.tv_sec = 20;
#ifdef DEBUG
	fprintf(stderr, "mount: clnt_call()\n");
#endif DEBUG
	rpc_stat = clnt_call(client, MOUNTPROC_MNT, xdr_path, &path,
	    xdr_fhstatus, &fhs, timeout);
	errno = 0;
	if (rpc_stat != RPC_SUCCESS) {
#ifdef DEBUG
	fprintf(stderr, "mount: clnt_call() failed (rpc_stat = %d)\n", rpc_stat);
#endif DEBUG
		if (!printed) {
			fprintf(stderr, "mount: %s server not responding",
			    mnt->mnt_fsname);
			clnt_perror(client, "");
			printed = 1;
		}
		switch (rpc_stat) {
		case RPC_TIMEDOUT:
		case RPC_PMAPFAILURE:
		case RPC_PROGNOTREGISTERED:
			errno = ETIMEDOUT;
			break;
		case RPC_AUTHERROR:
			errno = EACCES;
			break;
		default:
			errno = ERPC;
			break;
		}
	}
	close(s);
	clnt_destroy(client);
	if (errno) {
		return(errno);
	}

	if (errno = fhs.fhs_status) {
		if (errno == EACCES) {
			fprintf(stderr, "mount: access denied for %s:%s\n",
			    host, path);
		} else {
			fprintf(stderr, "mount: ");
			perror(mnt->mnt_fsname);
		}
		return (errno);
	}
	if (printed) {
		fprintf(stderr, "mount: %s server ok\n", mnt->mnt_fsname);
		printed = 0;
	}

	/*
	 * set mount args
	 */
	args->fh = &fhs.fhs_fh;
	args->hostname = host;
	args->flags |= NFSMNT_HOSTNAME;
	if (args->rsize = nopt(mnt, "rsize")) {
		args->flags |= NFSMNT_RSIZE;
	}
	if (args->wsize = nopt(mnt, "wsize")) {
		args->flags |= NFSMNT_WSIZE;
	}
	if (args->timeo = nopt(mnt, "timeo")) {
		args->flags |= NFSMNT_TIMEO;
	}
	if (args->retrans = nopt(mnt, "retrans")) {
		args->flags |= NFSMNT_RETRANS;
	}
	if (port = nopt(mnt, "port")) {
		sin.sin_port = htons(port);
	} else {
		sin.sin_port = htons(NFS_PORT);	/* XXX should use portmapper */
	}
	args->addr = &sin;

	/*
	 * should clean up mnt ops to not contain defaults
	 */
	return (0);
}

/*
 * cache scan required by mount_afs
 */
static ScanCache(apath, afiles, astates, ainodes)
    register long afiles;
    char *apath;
    register char *astates;
    long *ainodes; {
    DIR *dir;
    register struct direct *td;
    long code;
    long temp;
    
    dir = opendir(apath);
    if (!dir) return 2;
    
    /* otherwise scan for V files */
    while (td = readdir(dir)) {
	if (!strncmp(td->d_name, "Cache", 5)) {
	    /* Cache* */
	    if (strcmp(td->d_name, "CacheItems") != 0) {
		unlink(td->d_name);
	    }
	}
	else if (td->d_name[0] == 'V') {
	    if (strcmp(td->d_name, "VolumeItems") != 0) {
		/* not /VolumeInfo, we should treat as V<number> */
		code = sscanf(td->d_name, "V%d", &temp);
		if (code == 1) {
		    if (temp >= 0 && temp < afiles) {
			astates[temp] = 1;	/* means found the file */
			ainodes[temp] = td->d_ino;	/* remember for later */
		    }
		    else unlink(td->d_name);
		}
		else unlink(td->d_name);
	    }
	}
    }
    closedir(dir);
    return 0;
}

static ParseHosts(alist, ahosts)
    char *alist;
    long *ahosts; {
    char tname[200];
    register char *sp, *ep;
    register struct hostent *th;
    sp = alist;
    while (sp) {
	ep = index(sp, ',');
	if (ep == (char *) 0) {
	    /* this is it */
	    strcpy(tname, sp);
	    sp = (char *) 0;
	}
	else {
	    bzero(tname, sizeof(tname));
	    strncpy(tname, sp, ep-sp);
	    sp = ep+1;
	}
	th = gethostbyname(tname);
	if (!th) {
	    printf("could not find %s in host table.\n", tname);
	    return(1);
	}
	bcopy(th->h_addr, ahosts, sizeof(long));
	ahosts++;
    }
    *ahosts++ = 0;
    return 0;
}

int
Get_afs_free(cacheDir)
	char	*cacheDir;
{
	DIR	*dirp = opendir(cacheDir);
	int	current_size = 0;
	struct	direct	*dp;
	struct	stat	stb;
	struct  statfs  fs;

	if (dirp == 0) {
		perror(cacheDir);
		return(-1);
	}

	/* get the size already used by andrew */
	chdir(cacheDir);
	while (dp = readdir(dirp)) {
		if (!strcmp(dp->d_name,".") || !strcmp(dp->d_name, ".."))
			continue;
		if (lstat(dp->d_name, &stb) < 0) {
			perror(dp->d_name);
		} else {
			current_size += howmany(dbtob(stb.st_blocks), 1024);
		}
	}
	close(dirp);

	/* now get the free space in the file system */
	if (statfs(cacheDir,&fs) < 0) {
		perror(cacheDir);
		return(-1);
	}
	return(((fs.f_bavail*fs.f_bsize)/1024) + current_size);
}

mount_afs(mnt, args)
	struct mntent *mnt;
	struct afs_args *args;
{
    long hosts[8];
    struct stat tstat;
    register long i, code;
    char *cacheDir;
    long blocks, files, max_cache_size;
    char *fileStates;
    long *fileInodes;
    FILE *vstab;
    char pbuffer[1024], tbuffer[200];
    int time;
   
    args->type = AFS_MAIN;

    /* setup defaults */
    bzero(hosts, sizeof(hosts));
    cacheDir = "/usr/venuscache";
    blocks = 3000;
    max_cache_size = 30 * 1024;
    time = 1;

    /* parse command line options, overriding defaults */
    code = ParseHosts(mnt->mnt_fsname, hosts);
    if (code) {
	printf("could not parse host list\n");
        return(1);
    }
    if (hasmntopt(mnt,"cache") != NULL) {
    	cacheDir=getopt(mnt,"cache");
    }
    if (hasmntopt(mnt,"cachesize") != NULL) {
	blocks = nopt(mnt,"cachesize");
    } else {
	if ((blocks = Get_afs_free(cacheDir)) < 0) {
		return(1);
	}
	if (blocks < 2048) { /* 2 Meg */
		printf("Not enough free space for vice cache %d\n",blocks);
		return(1);
	}
	if (hasmntopt(mnt,"cache_reserve") != NULL) {
		blocks -= nopt(mnt,"cache_reserve");
	} else if (hasmntopt(mnt,"cache%") != NULL) {
		blocks = (blocks * nopt(mnt,"cache%"))/100;
	} else {
		blocks = ((blocks - 1024)*97)/100;
	}
    }
    if (hasmntopt(mnt,"max_cache_size") != NULL) {
	max_cache_size = nopt(mnt,"max_cache_size");
    }
    if (blocks > max_cache_size) {
	printf("truncating cache size from %d to %d\n",blocks,max_cache_size);
	blocks = max_cache_size;
    }
    if (blocks < 2048) {
	printf("specified cache size too small (%d) rounding up to 2 Meg\n",
		blocks);
	blocks = 2048;
    }

    if (hasmntopt(mnt,"notime") != NULL) {
        time = 0;
    }

    /*
     * make a new /etc/vstab for other vice clients (should go away, and
     * the clients should look in /etc/fstab).
     */
    if ((vstab = fopen("/etc/vstab","w")) == NULL) {
		perror("/etc/vstab");
    } else {
		fprintf(vstab,"%s::%s:%s:%d:%d\n",mnt->mnt_dir,mnt->mnt_fsname,
			cacheDir,blocks,!time);
		fclose(vstab);
    }
    files = blocks/12;
    if (hasmntopt(mnt,"cache_files") != NULL) {
	files = nopt(mnt,"cache_files");
    }
    /* compute derived data */
    if (files <  500) files =  500;
    else if (files > 3200) files = 3200;
    fileStates = (char *) malloc(files * sizeof(char));
    bzero(fileStates, files * sizeof(char));
    fileInodes = (long *) malloc(files * sizeof(long));
    bzero(fileInodes, files * sizeof(long));
    
    /* before forking, scan cache dir */
    code = ScanCache(cacheDir, files, fileStates, fileInodes);
    if (code) {
	fprintf(stderr,
	     "Could not initialize cache (%s) from user mode(%d)\n", cacheDir,
									   code);
	return(1);
    }

    code = fork();
    if (code == 0) {
	/* child process, start R */
	syscall(AFSCALL, AFSOP_START_R);
	exit(1);
    }

    code = fork();
    if (code == 0) {
	syscall(AFSCALL, AFSOP_START_CALLBACK);
	exit(1);
    }
    
    code = fork();
    if (code == 0) {
	syscall(AFSCALL, AFSOP_START_RFTP);
	exit(1);
    }
    
    code = fork();
    if (code == 0) {
	syscall(AFSCALL, AFSOP_START_AFS);
	exit(1);
    }
    
    code = fork();
    if (code == 0) {
	syscall(AFSCALL, AFSOP_START_BKG);
	exit(1);
    }
    
    /* now add the primary cell definition */
    syscall(AFSCALL, AFSOP_ADDCELL, hosts, "localcell", 10);
    
    /* now init the cache */
    syscall(AFSCALL, AFSOP_CACHEINIT, 300, files, blocks);
    
    /* tell venus the name of the cache info file */
    strcpy(pbuffer, cacheDir);
    strcat(pbuffer, "/CacheItems");
    code = open(pbuffer, O_RDWR | O_CREAT, 0666);
    close(code);
    syscall(AFSCALL, AFSOP_CACHEINFO, pbuffer);
    
    /* tell venus the name of the volume info file */
    strcpy(pbuffer, cacheDir);
    strcat(pbuffer, "/VolumeItems");
    code = open(pbuffer, O_RDWR | O_CREAT | O_TRUNC, 0666);
    close(code);
    syscall(AFSCALL, AFSOP_VOLUMEINFO, pbuffer);

    /* bogus way of getting some files created */
    for(i=0;i<files;i++) {
	if (fileStates[i] == 0) {
	    /* file does not exist */
	    strcpy(pbuffer, cacheDir);
	    sprintf(tbuffer, "/V%d", i);
	    strcat(pbuffer, tbuffer);
	    code = open(pbuffer, O_RDWR | O_CREAT | O_TRUNC, 0666);
	    if (code < 0) {
		fprintf(stderr,"could not create file %s\n", pbuffer);
		return(1);
	    }
	    fstat(code, &tstat);
	    close(code);
	    fileInodes[i] = tstat.st_ino;
	    fileStates[i] = 1;	/* file exists now */
	}
	syscall(AFSCALL, AFSOP_CACHEINODE, fileInodes[i]);
    }
    
    /* now start the thing, parm is whether or not to set the time */
    syscall(AFSCALL, AFSOP_GO, time);
    return(0);
}

#ifdef PCFS
mount_pc(mnt, args)
	struct mntent *mnt;
	struct pc_args *args;
{
	args->fspec = mnt->mnt_fsname;
	return (0);
}
#endif

printent(mnt)
	struct mntent *mnt;
{
	fprintf(stdout, "%s on %s type %s (%s)\n",
	    mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts);
}

printmtab(outp)
	FILE *outp;
{
	FILE *mnttab;
	struct mntent *mntp;
	int maxfsname = 0;
	int maxdir = 0;
	int maxtype = 0;
	int maxopts = 0;

	/*
	 * first go through and find the max width of each field
	 */
	mnttab = setmntent(MOUNTED, "r");
	while ((mntp = getmntent(mnttab)) != NULL) {
		if (strlen(mntp->mnt_fsname) > maxfsname) {
			maxfsname = strlen(mntp->mnt_fsname);
		}
		if (strlen(mntp->mnt_dir) > maxdir) {
			maxdir = strlen(mntp->mnt_dir);
		}
		if (strlen(mntp->mnt_type) > maxtype) {
			maxtype = strlen(mntp->mnt_type);
		}
		if (strlen(mntp->mnt_opts) > maxopts) {
			maxopts = strlen(mntp->mnt_opts);
		}
	}
	endmntent(mnttab);

	/*
	 * now print them oput in pretty format
	 */
	mnttab = setmntent(MOUNTED, "r");
	while ((mntp = getmntent(mnttab)) != NULL) {
		fprintf(outp, "%-*s", maxfsname+1, mntp->mnt_fsname);
		fprintf(outp, "%-*s", maxdir+1, mntp->mnt_dir);
		fprintf(outp, "%-*s", maxtype+1, mntp->mnt_type);
		fprintf(outp, "%-*s", maxopts+1, mntp->mnt_opts);
		fprintf(outp, " %d %d\n", mntp->mnt_freq, mntp->mnt_passno);
	}
	endmntent(mnttab);
	return (0);
}

/*
 * Check to see if mntck is already mounted.
 * We have to be careful because getmntent modifies its static struct.
 */
mounted(mntck)
	struct mntent *mntck;
{
	int found = 0;
	struct mntent *mnt, mntsave;
	FILE *mnttab;

	mnttab = setmntent(MOUNTED, "r");
	if (mnttab == NULL) {
		fprintf(stderr, "mount: ");
		perror(MOUNTED);
		exit(1);
	}
	mntcp(mntck, &mntsave);
	while ((mnt = getmntent(mnttab)) != NULL) {
		if (strcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0) {
			continue;
		}
		if ((strcmp(mntsave.mnt_fsname, mnt->mnt_fsname) == 0) &&
		    (strcmp(mntsave.mnt_dir, mnt->mnt_dir) == 0) ) {
			found = 1;
			break;
		}
	}
	endmntent(mnttab);
	*mntck = mntsave;
	return (found);
}

mntcp(mnt1, mnt2)
	struct mntent *mnt1, *mnt2;
{
	static char fsname[128], dir[128], type[128], opts[128];

	mnt2->mnt_fsname = fsname;
	strcpy(fsname, mnt1->mnt_fsname);
	mnt2->mnt_dir = dir;
	strcpy(dir, mnt1->mnt_dir);
	mnt2->mnt_type = type;
	strcpy(type, mnt1->mnt_type);
	mnt2->mnt_opts = opts;
	strcpy(opts, mnt1->mnt_opts);
	mnt2->mnt_freq = mnt1->mnt_freq;
	mnt2->mnt_passno = mnt1->mnt_passno;
}

/*
 * Return the value of a string option of the form foo=string, if
 * option is not found, return 0.
 */
char *
getopt(mnt, opt)
	struct mntent *mnt;
	char *opt;
{
	char *val = 0;
	char *end;
	char *str;

	if (str = hasmntopt(mnt, opt)) {
		if (val = index(str,'=')) {
			if (end = index(val,',')) {
				char *v2;
				int len=(end-val);
			        fflush(stdout);
				v2 = (char *)xmalloc(len);
				bcopy(val+1,v2,len);
				v2[len-1] = 0;
				return(v2);
			}
			return(val+1);
		} else {
			fprintf(stderr, "mount: bad string option '%s'\n",
			    str);
		}
	}
	return (val);
}

/*
 * Return the value of a numeric option of the form foo=x, if
 * option is not found or is malformed, return 0.
 */
nopt(mnt, opt)
	struct mntent *mnt;
	char *opt;
{
	int val = 0;
	char *equal;
	char *str;

	if (str = hasmntopt(mnt, opt)) {
		if (equal = index(str, '=')) {
			val = atoi(&equal[1]);
		} else {
			fprintf(stderr, "mount: bad numeric option '%s'\n",
			    str);
		}
	}
	return (val);
}

/*
 * update /etc/mtab
 */
addtomtab(mnt)
	struct mntent *mnt;
{
	FILE *mnted;

	mnted = setmntent(MOUNTED, "r+");
	if (mnted == NULL) {
		fprintf(stderr, "mount: ");
		perror(MOUNTED);
		exit(1);
	}
	if (addmntent(mnted, mnt)) {
		fprintf(stderr, "mount: ");
		perror(MOUNTED);
		exit(1);
	}
	endmntent(mnted);

	if (verbose) {
		fprintf(stdout, "%s mounted on %s\n",
		    mnt->mnt_fsname, mnt->mnt_dir);
	}
}

char *
xmalloc(size)
	int size;
{
	char *ret;
	
	if ((ret = (char *)malloc(size)) == NULL) {
		fprintf(stderr, "umount: ran out of memory!\n");
		exit(1);
	}
	return (ret);
}

struct mntent *
mntdup(mnt)
	struct mntent *mnt;
{
	struct mntent *new;

	new = (struct mntent *)xmalloc(sizeof(*new));

	new->mnt_fsname = (char *)xmalloc(strlen(mnt->mnt_fsname) + 1);
	strcpy(new->mnt_fsname, mnt->mnt_fsname);

	new->mnt_dir = (char *)xmalloc(strlen(mnt->mnt_dir) + 1);
	strcpy(new->mnt_dir, mnt->mnt_dir);

	new->mnt_type = (char *)xmalloc(strlen(mnt->mnt_type) + 1);
	strcpy(new->mnt_type, mnt->mnt_type);

	new->mnt_opts = (char *)xmalloc(strlen(mnt->mnt_opts) + 1);
	strcpy(new->mnt_opts, mnt->mnt_opts);

	new->mnt_freq = mnt->mnt_freq;
	new->mnt_passno = mnt->mnt_passno;

	return (new);
}

/*
 * Build the mount dependency tree
 */
struct mnttree *
maketree(mt, mnt)
	struct mnttree *mt;
	struct mntent *mnt;
{
	if (mt == NULL) {
		mt = (struct mnttree *)xmalloc(sizeof (struct mnttree));
		mt->mt_mnt = mntdup(mnt);
		mt->mt_sib = NULL;
		mt->mt_kid = NULL;
	} else {
		if (substr(mt->mt_mnt->mnt_dir, mnt->mnt_dir)) {
			mt->mt_kid = maketree(mt->mt_kid, mnt);
		} else {
			mt->mt_sib = maketree(mt->mt_sib, mnt);
		}
	}
	return (mt);
}

printtree(mt)
	struct mnttree *mt;
{
	if (mt) {
		printtree(mt->mt_sib);
		printf("   %s\n", mt->mt_mnt->mnt_dir);
		printtree(mt->mt_kid);
	}
}

mounttree(mt)
	struct mnttree *mt;
{
	int error;
	int slptime;
	int forked;
	int retry;
	int firsttry;

	if (mt) {
		mounttree(mt->mt_sib);
		forked = 0;
		printed = 0;
		firsttry = 1;
		slptime = BGSLEEP;
		retry = nopt(mt->mt_mnt, "retry");
		if (retry == 0) {
			retry = NRETRY;
		}

		do {
			error = mountfs(!forked, mt->mt_mnt);
			if (error != ETIMEDOUT && error != ENETDOWN &&
			    error != ENETUNREACH && error != ENOBUFS &&
			    error != ECONNREFUSED && error != ECONNABORTED) {
				break;
			}
			if (!forked && hasmntopt(mt->mt_mnt, "bg")) {
				fprintf(stderr, "mount: backgrounding\n");
				fprintf(stderr, "   %s\n", mt->mt_mnt->mnt_dir);
				printtree(mt->mt_kid);
				if (fork()) {
					return;
				} else {
					forked = 1;
				}
			}
			if (!forked && firsttry) {
				fprintf(stderr, "mount: retrying\n");
				fprintf(stderr, "   %s\n", mt->mt_mnt->mnt_dir);
				printtree(mt->mt_kid);
				firsttry = 0;
			}
			sleep(slptime);
			slptime = MIN(slptime << 1, MAXSLEEP);
		} while (retry--);

		if (!error) {
			mounttree(mt->mt_kid);
		} else {
			fprintf(stderr, "Can't mount %s on  %s:\n", mt->mt_mnt->mnt_fsname, mt->mt_mnt->mnt_dir);
			printtree(mt->mt_kid);
		}
		if (forked) {
			exit(0);
		}
	}
}

printsp(n)
	int n;
{
	while (n--) {
		printf(" ");
	}
}

/*
 * Returns true if s1 is a pathname substring of s2.
 */
substr(s1, s2)
	char *s1;
	char *s2;
{
	while (*s1 == *s2) {
		s1++;
		s2++;
	}
	if (*s1 == '\0' && *s2 == '/') {
		return (1);
	}
	return (0);
}

bindresvport(sd)
	int sd;
{
 
	u_short port;
	struct sockaddr_in sin;
	int err = -1;

#	define MAX_PRIV (IPPORT_RESERVED-1)
#	define MIN_PRIV (IPPORT_RESERVED/2)

	get_myaddress(&sin);
	sin.sin_family = AF_INET;
	for (port = MAX_PRIV; err && port >= MIN_PRIV; port--) {
		sin.sin_port = htons(port);
		err = bind(sd,&sin,sizeof(sin));
	}
	return (err == 0);
}
 

usage()
{
	fprintf(stdout,
	    "Usage: mount [-ravpfto [type|option]] ... [fsname] [dir]\n");
	exit(1);
}

#endif !VFS
