[comp.sources.unix] v21i100: An Automounter for NFS systems, Part12/13

rsalz@uunet.uu.net (Rich Salz) (04/11/90)

Submitted-by: Jan-Simon Pendry <jsp@doc.ic.ac.uk>
Posting-number: Volume 21, Issue 100
Archive-name: amd/part12

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 12 (of 13)."
# Contents:  afs_ops.c
# Wrapped by rsalz@papaya.bbn.com on Tue Apr 10 15:12:16 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'afs_ops.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'afs_ops.c'\"
else
echo shar: Extracting \"'afs_ops.c'\" \(34481 characters\)
sed "s/^X//" >'afs_ops.c' <<'END_OF_FILE'
X/*
X * $Id: afs_ops.c,v 5.1.1.3 90/01/11 16:58:01 jsp Exp Locker: jsp $
X *
X * Copyright (c) 1990 Jan-Simon Pendry
X * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Jan-Simon Pendry at Imperial College, London.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by Imperial College of Science, Technology and Medicine, London, UK.
X * The names of the College and University may not be used to endorse
X * or promote products derived from this software without specific
X * prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X *	%W% (Berkeley) %G%
X */
X
X#include "am.h"
X
X#define NFS
X#define NFSCLIENT
X
X#include <sys/stat.h>
X#ifdef NFS_3
Xtypedef nfs_fh fhandle_t;
X#endif
X#ifdef NFS_HDR
X#include NFS_HDR
X#endif
X#include <sys/mount.h>
X#include "mount.h"
X
X/*
X * Automount file system
X */
X
X/*
X * Interval between forced retries of a mount.
X */
X#define RETRY_INTERVAL	2
X
X/*
X * AFS needs nothing in particular.
X */
Xstatic int afs_match(fo)
Xam_opts *fo;
X{
X	char *p = fo->opt_rfs;
X	if (!fo->opt_rfs) {
X		plog(XLOG_USER, "auto: no mount point named (rfs:=)");
X		return 0;
X	}
X	if (!fo->opt_fs) {
X		plog(XLOG_USER, "auto: no map named (fs:=)");
X		return 0;
X	}
X	/*
X	 * Swap round fs:= and rfs:= options
X	 * ... historical (jsp)
X	 */
X	fo->opt_rfs = fo->opt_fs;
X	fo->opt_fs = p;
X	/*
X	 * fs_mtab turns out to be the name of the mount map
X	 */
X	fo->fs_mtab = strealloc(fo->fs_mtab, fo->opt_rfs ? fo->opt_rfs : ".");
X	return 1;
X}
X
Xstatic int afs_init(mf)
Xmntfs *mf;
X{
X	/*
X	 * Fill in attribute fields
X	 */
X	mf->mf_fattr.type = NFDIR;
X	mf->mf_fattr.mode = NFSMODE_DIR | 0555;
X	mf->mf_fattr.nlink = 2;
X	mf->mf_fattr.size = 512;
X
X	return 0;
X}
X
X/*
X * Mount the an automounter directory.
X * The automounter is connected into the system
X * as a user-level NFS server.  mount_afs constructs
X * the necessary NFS parameters to be given to the
X * kernel so that it will talk back to us.
X */
Xstatic int mount_afs(dir, fs_name, opts)
Xchar *dir;
Xchar *fs_name;
Xchar *opts;
X{
X	struct nfs_args nfs_args;
X	struct mntent mnt;
X	int retry;
X	struct sockaddr_in sin;
X	unsigned short port;
X	int flags;
X	extern nfs_fh *root_fh();
X	char fs_hostname[MAXHOSTNAMELEN+MAXPATHLEN+1];
X
X	MTYPE_TYPE type = MOUNT_TYPE_NFS;
X
X	bzero((voidp) &nfs_args, sizeof(nfs_args));	/* Paranoid */
X
X	mnt.mnt_dir = dir;
X	mnt.mnt_fsname = fs_name;
X	mnt.mnt_type = MNTTYPE_AUTO;
X	mnt.mnt_opts = opts;
X	mnt.mnt_freq = 0;
X	mnt.mnt_passno = 0;
X
X	retry = hasmntval(&mnt, "retry");
X	if (retry <= 0)
X		retry = 2;	/* XXX */
X
X	/*
X	 * get fhandle of remote path for automount point
X	 */
X	nfs_args.fh = (NFS_FH_TYPE) root_fh(fs_name);
X
X	if (!nfs_args.fh) {
X		plog(XLOG_FATAL, "Can't find root file handle for %s", fs_name);
X		return EINVAL;
X	}
X
X	/*
X	 * Create sockaddr to point to the local machine.  127.0.0.1
X	 * is not used since that will not work in HP-UX clusters and
X	 * this is no more expensive.
X	 */
X	bzero((voidp) &sin, sizeof(sin));
X	sin.sin_family = AF_INET;
X	sin.sin_addr = myipaddr;
X	if (port = hasmntval(&mnt, "port")) {
X		sin.sin_port = htons(port);
X	} else {
X		plog(XLOG_ERROR, "no port number specified for %s", fs_name);
X		return EINVAL;
X	}
X
X	/*
X	 * set mount args
X	 */
X	nfs_args.addr = &sin;
X
X	/*
X	 * Make a ``hostname'' string for the kernel
X	 */
X#ifdef SHORT_MOUNT_NAME
X	sprintf(fs_hostname, "amd:%d", mypid);
X#else
X	sprintf(fs_hostname, "pid%d@%s:%s", mypid, hostname, dir);
X#endif
X	nfs_args.hostname = fs_hostname;
X	nfs_args.flags |= NFSMNT_HOSTNAME;
X#ifdef HOSTNAMESZ
X	/*
X	 * Most kernels have a name length restriction.
X	 */
X	if (strlen(fs_hostname) >= HOSTNAMESZ)
X		strcpy(fs_hostname + HOSTNAMESZ - 3, "..");
X#endif
X
X	/*
X	 * Parse a subset of the standard nfs options.  The
X	 * others are probably irrelevant for this application
X	 */
X	if (nfs_args.timeo = hasmntval(&mnt, "timeo"))
X		nfs_args.flags |= NFSMNT_TIMEO;
X
X	if (nfs_args.retrans = hasmntval(&mnt, "retrans"))
X		nfs_args.flags |= NFSMNT_RETRANS;
X
X#if defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX)
X	/*
X	 * Don't cache attributes - they are changing under
X	 * the kernel's feet...
X	 */
X	nfs_args.acregmin = nfs_args.acregmax = 1;
X	nfs_args.flags |= NFSMNT_ACREGMIN|NFSMNT_ACREGMAX;
X#endif
X	/*
X	 * These two are constructed internally by the calling routine
X	 */
X	if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL)
X		nfs_args.flags |= NFSMNT_SOFT;
X
X#ifdef MNTOPT_INTR
X	if (hasmntopt(&mnt, MNTOPT_INTR) != NULL)
X		nfs_args.flags |= NFSMNT_INT;
X#endif
X
X	flags = compute_mount_flags(&mnt);
X#ifdef ULTRIX_HACK
X	nfs_args.gfs_flags = flags;
X	flags &= M_RDONLY;
X	if (flags & M_RDONLY)
X		nfs_args.flags |= NFSMNT_RONLY;
X#endif
X	return mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
X}
X
Xstatic int afs_mount(mp)
Xam_node *mp;
X{
X	mntfs *mf = mp->am_mnt;
X
X	/*
X	 * There are two cases to consider...
X	 */
X	if (mp->am_parent && mp->am_parent->am_parent) {
X		/*
X		 * If this am_node has a parent which is not the root node, in
X		 * which case we are supplying a pseudo-directory, in which
X		 * case no action is needed.  Pseudo-directories are used to
X		 * provide some structure to the automounted directories instead
X		 * of putting them all in the top-level automount directory.
X		 */
X		mp->am_parent->am_mnt->mf_fattr.nlink++;
X		/*
X		 * Info field of . means use parent's info field.
X		 */
X		if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
X			mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
X		/*
X		 * Compute prefix:
X		 *
X		 * If there is an option prefix then use that else
X		 * If the parent had a prefix then use that with name
X		 *	of this node appended else
X		 * Use the name of this node.
X		 *
X		 * That means if you want no prefix you must say so
X		 * in the map.
X		 */
X		if (mf->mf_fo->opt_pref) {
X			/*
X			 * the prefix specified as an option
X			 */
X			mp->am_pref = strdup(mf->mf_fo->opt_pref);
X		} else {
X			/*
X			 * else the parent's prefix
X			 * followed by the name
X			 * followed by /
X			 */
X			char *ppref = mp->am_parent->am_pref;
X			if (ppref == 0)
X				ppref = "";
X			mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
X		}
X	} else {
X		/*
X		 * Otherwise, we are mounting the automounter.  In which case
X		 * we need to make sure the mount directory exists, construct
X		 * the mount options and call the mount_afs routine.
X		 */
X		struct stat stb;
X		char opts[256];
X		int error;
X
X		/*
X		 * Top-level mount - so make
X		 * sure the mount point exists
X		 * and is a directory.
X		 */
X		error = mkdirs(mp->am_path, 0555);
X		if (error)
X			return error;
X		mp->am_flags |= AMF_MKPATH;
X
X		if (stat(mp->am_path, &stb) < 0) {
X			return errno;
X		} else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
X			plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
X			return ENOTDIR;
X		}
X
X		mf->mf_mount = strealloc(mf->mf_mount, mp->am_path);
X
X		/*
X		 * Construct some mount options
X		 */
X		sprintf(opts,
X#ifdef MNTOPT_INTR
X			"%s,%s,%s=%d,%s=%d,%s=%d,%sdirect",
X			MNTOPT_INTR,
X#else
X			"%s,%s=%d,%s=%d,%s=%d,%sdirect",
X#endif
X#ifdef AUTOMOUNT_RO
X			MNTOPT_RO,	/* You don't really want this... */
X#else
X			"rw",
X#endif
X			"port", nfs_port,
X			"timeo", afs_timeo,
X			"retrans", afs_retrans,
X			mf->mf_ops == &afs_ops ? "in" : "");
X
X		error = mount_afs(mp->am_path, mp->am_name, opts);
X		if (error) {
X			errno = error;
X			plog(XLOG_FATAL, "mount_afs: %m");
X			return error;
X		}
X		mp->am_name = pid_fsname;
X	}
X
X	/*
X	 * Build a new map cache for this node, or re-use
X	 * an existing cache for the same map.
X	 */
X	{ char *cache;
X	  if (mf->mf_fo->opt_cache)
X	  	cache = mf->mf_fo->opt_cache;
X	  else
X	  	cache = "none";
X	  mf->mf_private = (voidp) mapc_find(mf->mf_info, cache);
X	  mf->mf_prfree = mapc_free;
X	}
X
X	return 0;
X}
X
X/*
X * Unmount an automount node
X */
Xstatic int afs_umount(mp)
Xam_node *mp;
X{
X	int error;
X
X	/*
X	 * If this is a pseudo-directory then just adjust the link count
X	 * in the parent, otherwise call the generic unmount routine
X	 */
X	if (!mp->am_parent) {
X		error = 0;
X	} else if (mp->am_parent && mp->am_parent->am_parent) {
X		--mp->am_parent->am_mnt->mf_fattr.nlink;
X		error = 0;
X	} else {
X		struct stat stb;
Xagain:
X		/*
X		 * The lstat is needed if this mount is type=direct.
X		 * When that happens, the kernel cache gets confused
X		 * between the underlying type (dir) and the mounted
X		 * type (link) and so needs to be re-synced before
X		 * the unmount.  This is all because the unmount system
X		 * call follows links and so can't actually unmount
X		 * a link (stupid!).  It was noted that doing and ls -ld
X		 * of the mount point to see why things were not working
X		 * actually fixed the problem - so simulate an ls -ld here.
X		 */
X		if (lstat(mp->am_path, &stb) < 0) {
X#ifdef DEBUG
X			dlog("lstat(%s): %m", mp->am_path);
X#endif
X		}
X		error = UMOUNT_FS(mp->am_path);
X		if (error == EBUSY) {
X			plog(XLOG_WARNING, "afs_unmount retrying %s in 1s", mp->am_path);
X			sleep(1);	/* XXX */
X			goto again;
X		}
X	}
X
X	return error;
X}
X
X/*
X * Unmount an automount node
X */
Xstatic void afs_umounted(mp)
Xam_node *mp;
X{
X	/*
X	 * If this is a pseudo-directory then just adjust the link count
X	 * in the parent, otherwise call the generic unmount routine
X	 */
X	if (mp->am_parent && mp->am_parent->am_parent)
X		--mp->am_parent->am_mnt->mf_fattr.nlink;
X}
X
X/*
X * Mounting a file system may take a significant period of time.  The
X * problem is that if this is done in the main process thread then
X * the entire automounter could be blocked, possibly hanging lots of
X * processes on the system.  Instead we use a continuation scheme to
X * allow mounts to be attempted in a sub-process.  When the sub-process
X * exits we pick up the exit status (by convention a UN*X error number)
X * and continue in a notifier.  The notifier gets handed a data structure
X * and can then determine whether the mount was successful or not.  If
X * not, it updates the data structure and tries again until there are no
X * more ways to try the mount, or some other permanent error occurs.
X * In the mean time no RPC reply is sent, even after the mount is succesful.
X * We rely on the RPC retry mechanism to resend the lookup request which
X * can then be handled.
X */
X
X
Xstruct continuation {
X	char **ivec;		/* Current mount info */
X	am_node *mp;		/* Node we are trying to mount */
X	char *key;		/* Map key */
X	char *info;		/* Info string */
X	char **xivec;		/* Saved strsplit vector */
X	char *opts;		/* Mount options */
X	am_opts fs_opts;	/* Filesystem options */
X	char *def_opts;		/* Default options */
X	int retry;		/* Try again? */
X	int tried;		/* Have we tried any yet? */
X	time_t start;		/* Time we started this mount */
X	int callout;		/* Callout identifier */
X};
X
X/*
X * Discard an old continuation
X */
Xstatic void free_continuation(cp)
Xstruct continuation *cp;
X{
X	if (cp->callout)
X		untimeout(cp->callout);
X	free((voidp) cp->key);
X	free((voidp) cp->xivec);
X	free((voidp) cp->info);
X	free((voidp) cp->opts);
X	free((voidp) cp->def_opts);
X	free_opts(&cp->fs_opts);
X	free((voidp) cp);
X}
X
Xstatic int afs_bgmount P((struct continuation*, int));
X
X/*
X * Discard the underlying mount point and replace
X * with a reference to an error filesystem.
X */
Xstatic void assign_error_mntfs(mp)
Xam_node *mp;
X{
X	if (mp->am_error > 0) {
X		/*
X		 * Save the old error code
X		 */
X		int error = mp->am_error;
X		/*
X		 * Discard the old filesystem
X		 */
X		free_mntfs(mp->am_mnt);
X		/*
X		 * Allocate a new error reference
X		 */
X		mp->am_mnt = new_mntfs();
X		/*
X		 * Put back the error code
X		 */
X		mp->am_mnt->mf_error = error;
X		mp->am_mnt->mf_flags |= MFF_ERROR;
X		/*
X		 * Zero the error in the mount point
X		 */
X		mp->am_error = 0;
X	}
X}
X
X/*
X * The continuation function.  This is called by
X * the task notifier when a background mount attempt
X * completes.
X */
Xstatic void afs_cont(rc, term, closure)
Xint rc;
Xint term;
Xvoidp closure;
X{
X	struct continuation *cp = (struct continuation *) closure;
X	mntfs *mf = cp->mp->am_mnt;
X
X	/*
X	 * Definitely not trying to mount at the moment
X	 */
X	mf->mf_flags &= ~MFF_MOUNTING;
X	/*
X	 * While we are mounting - try to avoid race conditions
X	 */
X	new_ttl(cp->mp);
X
X	/*
X	 * Wakeup anything waiting for this mount
X	 */
X	wakeup((voidp) mf);
X
X	/*
X	 * Check for termination signal or exit status...
X	 */
X	if (rc || term) {
X		if (term) {
X			/*
X			 * Not sure what to do for an error code.
X			 */
X			mf->mf_error = EIO;	/* XXX ? */
X			mf->mf_flags |= MFF_ERROR;
X			plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
X		} else {
X			/*
X			 * Check for exit status...
X			 */
X			mf->mf_error = rc;
X			mf->mf_flags |= MFF_ERROR;
X			errno = rc;	/* XXX */
X			plog(XLOG_ERROR, "%s: mount (afs_cont): %m", cp->mp->am_path);
X		}
X
X		/*
X		 * If we get here then that attempt didn't work, so
X		 * move the info vector pointer along by one and
X		 * call the background mount routine again
X		 */
X		amd_stats.d_merr++;
X		cp->ivec++;
X		(void) afs_bgmount(cp, 0);
X		assign_error_mntfs(cp->mp);
X	} else {
X		/*
X		 * The mount worked.
X		 */
X		am_mounted(cp->mp);
X		free_continuation(cp);
X	}
X
X	reschedule_timeout_mp();
X}
X
X/*
X * Retry a mount
X */
X/*ARGSUSED*/
Xstatic void afs_retry(rc, term, closure)
Xint rc;
Xint term;
Xvoidp closure;
X{
X	struct continuation *cp = (struct continuation *) closure;
X	int error = 0;
X
X#ifdef DEBUG
X	dlog("Commencing retry for mount of %s", cp->mp->am_path);
X#endif
X
X	if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
X		/*
X		 * The entire mount has timed out.
X		 * Set the error code and skip past
X		 * all the info vectors so that
X		 * afs_bgmount will not have any more
X		 * ways to try the mount, so causing
X		 * an error.
X		 */
X		plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
X		error = ETIMEDOUT;
X		new_ttl(cp->mp);
X		while (*cp->ivec)
X			cp->ivec++;
X	}
X
X	(void) afs_bgmount(cp, error);
X	reschedule_timeout_mp();
X}
X
X/*
X * Try to mount a file system.  Can be called
X * directly or in a sub-process by run_task
X */
Xstatic int try_mount(mp)
Xam_node *mp;
X{
X	/*
X	 * Mount it!
X	 */
X	int error;
X
X	error = mount_node(mp);
X#ifdef DEBUG
X	if (error) {
X		errno = error;
X		dlog("afs call to mount_node failed: %m");
X	}
X#endif
X	return error;
X}
X
X/*
X * Pick a file system to try mounting and
X * do that in the background if necessary
X *
XFor each location:
X	if it is new -defaults then
X		extract and process
X		continue;
X	fi
X	if it is a cut then
X		if a location has been tried then
X			break;
X		fi
X		continue;
X	fi
X	parse mount location
X	discard previous mount location if required
X	find matching mounted filesystem
X	if not applicable then
X		this_error = No such file or directory
X		continue
X	fi
X	if the filesystem failed to be mounted then
X		this_error = error from filesystem
X	elif the filesystem is mounting or unmounting then
X		this_error = -1
X	elif the fileserver is down then
X		this_error = -1
X	elif the filesystem is already mounted
X		this_error = 0
X		break
X	fi
X	if no error on this mount then
X		this_error = initialise mount point
X	fi
X	if no error on this mount and mount is delayed then
X		this_error = -1
X	fi
X	if this_error < 0 then
X		retry = true
X	fi
X	if no error on this mount then
X		make mount point if required
X	fi
X	if no error on this mount then
X		if mount in background then
X			run mount in background
X			return -1
X		else
X			this_error = mount in foreground
X		fi
X	fi
X	if an error occured on this mount then
X		update stats
X		save error in mount point
X	fi
Xendfor
X */
X
Xstatic int afs_bgmount(cp, mpe)
Xstruct continuation *cp;
Xint mpe;
X{
X	mntfs *mf = cp->mp->am_mnt;	/* Current mntfs */
X	mntfs *mf_retry = 0;		/* First mntfs which needed retrying */
X	int this_error = -1;		/* Per-mount error */
X	int hard_error = -1;
X	int mp_error = mpe;
X
X	/*
X	 * Try to mount each location.
X	 * At the end:
X	 * hard_error == 0 indicates something was mounted.
X	 * hard_error > 0 indicates everything failed with a hard error
X	 * hard_error < 0 indicates nothing could be mounted now
X	 */
X	for (; this_error && *cp->ivec; cp->ivec++) {
X		am_ops *p;
X		am_node *mp = cp->mp;
X		char *link_dir;
X		int dont_retry;
X
X		if (hard_error < 0)
X			hard_error = this_error;
X
X		this_error = -1;
X
X		if (**cp->ivec == '-') {
X			/*
X			 * Pick up new defaults
X			 */
X			if (cp->opts && *cp->opts)
X				cp->def_opts = str3cat(cp->def_opts, cp->opts, ";", *cp->ivec+1);
X			else
X				cp->def_opts = strealloc(cp->def_opts, *cp->ivec+1);
X#ifdef DEBUG
X			dlog("Setting def_opts to \"%s\"", cp->def_opts);
X#endif
X			continue;
X		}
X
X		/*
X		 * If a mount has been attempted, and we find
X		 * a cut then don't try any more locations.
X		 */
X		if (strcmp(*cp->ivec, "/") == 0 || strcmp(*cp->ivec, "||") == 0) {
X			if (cp->tried) {
X#ifdef DEBUG
X				dlog("Cut: not trying any more locations for %s",
X					mp->am_path);
X#endif
X				break;
X			}
X			continue;
X		}
X
X#ifdef SUNOS4_COMPAT
X		/*
X		 * By default, you only get this bit on SunOS4.
X		 * If you want this anyway, then define SUNOS4_COMPAT
X		 * in the relevant "os-blah.h" file.
X		 *
X		 * We make the observation that if the local key line contains
X		 * no '=' signs then either it is sick, or it is a SunOS4-style
X		 * "host:fs[:link]" line.  In the latter case the am_opts field
X		 * is also assumed to be in old-style, so you can't mix & match.
X		 * You can use ${} expansions for the fs and link bits though...
X		 *
X		 * Actually, this doesn't really cover all the possibilities for
X		 * the latest SunOS automounter and it is debatable whether there
X		 * is any point bothering.
X		 */
X		if (strchr(*cp->ivec, '=') == 0)
X			p = sunos4_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
X		else
X#endif /* SUNOS4_COMPAT */
X			p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
X
X		/*
X		 * Find a mounted filesystem for this node.
X		 */
X		mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, cp->fs_opts.opt_fs,
X						cp->fs_opts.fs_mtab, cp->opts);
X
X		p = mf->mf_ops;
X#ifdef DEBUG
X		dlog("Got a hit with %s", p->fs_type);
X#endif
X		/*
X		 * Note whether this is a real mount attempt
X		 */
X		if (p == &efs_ops) {
X			plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
X			if (this_error <= 0)
X				this_error = ENOENT;
X			continue;
X		} else {
X			if (cp->fs_opts.fs_mtab) {
X				plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
X					cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
X			}
X			cp->tried = TRUE;
X		}
X
X		this_error = 0;
X		dont_retry = FALSE;
X
X		if (mp->am_link) {
X			free(mp->am_link);
X			mp->am_link = 0;
X		}
X
X		link_dir = mf->mf_fo->opt_sublink;
X
X		if (link_dir && *link_dir) {
X			if (*link_dir == '/') {
X				mp->am_link = strdup(link_dir);
X			} else {
X				mp->am_link = str3cat((char *) 0,
X					mf->mf_fo->opt_fs, "/", link_dir);
X			}
X		}
X
X		if (mf->mf_error > 0) {
X			this_error = mf->mf_error;
X		} else if (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)) {
X			/*
X			 * Still mounting - retry later
X			 */
X#ifdef DEBUG
X			dlog("Duplicate pending fstype %s mount", p->fs_type);
X#endif
X			this_error = -1;
X		} else if (FSRV_ISDOWN(mf->mf_server)) {
X			/*
X			 * Would just mount from the same place
X			 * as a hung mount - so give up
X			 */
X#ifdef DEBUG
X			dlog("%s is already hung - giving up", mf->mf_mount);
X#endif
X			mp_error = EWOULDBLOCK;
X			dont_retry = TRUE;
X			this_error = -1;
X		} else if (mf->mf_flags & MFF_MOUNTED) {
X#ifdef DEBUG
X			dlog("duplicate mount of \"%s\" ...", mf->mf_info);
X#endif
X			this_error = 0;
X			break;
X		}
X
X		/*
X		 * Will usually need to play around with the mount nodes
X		 * file attribute structure.  This must be done here.
X		 */
X		if (!this_error) {
X			/*
X			 * Fill in attribute fields
X			 */
X			mf->mf_fattr.type = NFLNK;
X			mf->mf_fattr.mode = NFSMODE_LNK | 0777;
X			mf->mf_fattr.nlink = 1;
X			mf->mf_fattr.size = MAXPATHLEN / 4;	/* Conservative */
X			mf->mf_fattr.fileid = mp->am_gen;
X
X			if (p->fs_init)
X				this_error = (*p->fs_init)(mf);
X		}
X
X		if (!this_error && mf->mf_fo->opt_delay) {
X			/*
X			 * If there is a delay timer on the mount
X			 * then don't try to mount if the timer
X			 * has not expired.
X			 */
X			int i = atoi(mf->mf_fo->opt_delay);
X			if (i > 0 && (cp->start + i) < clocktime()) {
X#ifdef DEBUG
X				dlog("Mount of %s delayed by %ds", mf->mf_mount, i);
X#endif
X				this_error = -1;
X			}
X		}
X
X		if (this_error < 0 && !dont_retry) {
X			if (!mf_retry)
X				mf_retry = dup_mntfs(mf);
X			cp->retry = TRUE;
X		}
X
X		if (!this_error) {
X			/*
X			 * If the directory is not yet made and
X			 * it needs to be made, then make it!
X			 */
X			 if (!(mf->mf_flags & MFF_MKMNT) &&
X				 	p->fs_flags & FS_MKMNT) {
X				this_error = mkdirs(mf->mf_mount, 0555);
X				if (!this_error)
X					mf->mf_flags |= MFF_MKMNT;
X			}
X		}
X
X		if (!this_error)
X		if (p->fs_flags & FS_MBACKGROUND) {
X			mf->mf_flags |= MFF_MOUNTING;	/*XXX*/
X#ifdef DEBUG
X			dlog("backgrounding mount of \"%s\"", mf->mf_info);
X#endif
X			if (cp->callout) {
X				untimeout(cp->callout);
X				cp->callout = 0;
X			}
X			run_task(try_mount, (voidp) mp, afs_cont, (voidp) cp);
X			if (mf_retry) free_mntfs(mf_retry);
X			return -1;
X		} else {
X#ifdef DEBUG
X			dlog("foreground mount of \"%s\" ...", mf->mf_info);
X#endif
X			this_error = try_mount(mp);
X		}
X
X		if (this_error >= 0) {
X			if (this_error > 0) {
X				amd_stats.d_merr++;
X				if (mf != mf_retry) {
X					mf->mf_error = this_error;
X					mf->mf_flags |= MFF_ERROR;
X				}
X			}
X			/*
X			 * Wakeup anything waiting for this mount
X			 */
X			wakeup((voidp) mf);
X		}
X	}
X
X	if (this_error && cp->retry) {
X		free_mntfs(mf);
X		mf = cp->mp->am_mnt = mf_retry;
X		/*
X		 * Not retrying again (so far)
X		 */
X		cp->retry = FALSE;
X		cp->tried = FALSE;
X		/*
X		 * Start at the beginning.
X		 * Rewind the location vector and
X		 * reset the default options.
X		 */
X		cp->ivec = cp->xivec;
X		cp->def_opts = strealloc(cp->def_opts, cp->opts);
X		/*
X		 * Arrange that afs_bgmount is called
X		 * after anything else happens.
X		 */
X#ifdef DEBUG
X		dlog("Arranging to retry mount of %s", cp->mp->am_path);
X#endif
X		sched_task(afs_retry, (voidp) cp, (voidp) mf);
X		if (cp->callout)
X			untimeout(cp->callout);
X		cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
X
X		cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
X
X		/*
X		 * Not done yet - so don't return anything
X		 */
X		return -1;
X	}
X
X	/*
X	 * Discard handle on duff filesystem.
X	 * This should never happen since it
X	 * should be caught by the case above.
X	 */
X	if (mf_retry) {
X		plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
X		free_mntfs(mf_retry);
X	}
X
X	if (hard_error < 0 || !this_error)
X		hard_error = this_error;
X
X	/*
X	 * If we get here, then either the mount succeeded or
X	 * there is no more mount information available.
X	 */
X	if (hard_error < 0 && mp_error)
X		hard_error = cp->mp->am_error = mp_error;
X	if (hard_error > 0) {
X		/*
X		 * Set a small(ish) timeout on an error node if
X		 * the error was not a time out.
X		 */
X		switch (hard_error) {
X		case ETIMEDOUT:
X		case EWOULDBLOCK:
X			cp->mp->am_timeo = 5;
X			break;
X		default:
X			cp->mp->am_timeo = 17;
X			break;
X		}
X		cp->mp->am_timeo_w = 0;
X	}
X
X	/*
X	 * Make sure that the error value in the mntfs has a
X	 * reasonable value.
X	 */
X	if (mf->mf_error < 0) {
X		mf->mf_error = hard_error;
X		if (hard_error)
X			mf->mf_flags |= MFF_ERROR;
X	}
X
X	/*
X	 * In any case we don't need the continuation any more
X	 */
X	free_continuation(cp);
X
X	return hard_error;
X}
X
X/*
X * Automount interface to RPC lookup routine
X */
Xstatic am_node *afs_lookuppn(mp, fname, error_return, op)
Xam_node *mp;
Xchar *fname;
Xint *error_return;
Xint op;
X{
X#define ereturn(x) { *error_return = x; return 0; }
X
X	/*
X	 * Find the corresponding entry and return
X	 * the file handle for it.
X	 */
X	am_node *ap, *new_mp, *ap_hung;
X	char *info;			/* Mount info - where to get the file system */
X	char **ivec, **xivec;		/* Split version of info */
X	char *opts;			/* Mount options */
X	int error = 0;			/* Error so far */
X	char path_name[MAXPATHLEN];	/* General path name buffer */
X	char *pfname;			/* Path for database lookup */
X	struct continuation *cp;	/* Continuation structure if we need to mount */
X	int in_progress = 0;		/* # of (un)mount in progress */
X	char *dflts;
X	mntfs *mf;
X
X#ifdef DEBUG
X	dlog("in afs_lookuppn");
X#endif
X
X	/*
X	 * If the server is shutting down
X	 * then don't return information
X	 * about the mount point.
X	 */
X	if (amd_state == Finishing) {
X#ifdef DEBUG
X		dlog("%s/%s mount ignored - going down",
X			mp->am_path, fname);
X#endif
X		ereturn(ENOENT);
X	}
X
X	/*
X	 * Handle special case of "." and ".."
X	 */
X	if (fname[0] == '.') {
X		if (fname[1] == '\0')
X			return mp;	/* "." is the current node */
X		if (fname[1] == '.' && fname[2] == '\0') {
X			if (mp->am_parent) {
X#ifdef DEBUG
X				dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
X#endif
X				return mp->am_parent;	/* ".." is the parent node */
X			}
X			ereturn(ESTALE);
X		}
X	}
X
X	/*
X	 * Check for valid key name.
X	 * If it is invalid then pretend it doesn't exist.
X	 */
X	if (!valid_key(fname)) {
X		plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
X		ereturn(ENOENT);
X	}
X
X	/*
X	 * Expand key name.
X	 * fname is now a private copy.
X	 */
X	fname = expand_key(fname);
X
X	for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
X		/*
X		 * Otherwise search children of this node
X		 */
X		if (FSTREQ(ap->am_name, fname)) {
X			mf = ap->am_mnt;
X			if (ap->am_error) {
X				error = ap->am_error;
X				continue;
X			}
X
X			/*
X			 * If the error code is undefined then it must be
X			 * in progress.
X			 */
X			if (mf->mf_error < 0)
X				goto in_progrss;
X
X			/*
X			 * Check for a hung node
X			 */
X			if (FSRV_ISDOWN(mf->mf_server)) {
X				ap_hung = ap;
X				continue;
X			}
X
X			/*
X			 * If there was a previous error with this node
X			 * then return that error code.
X			 */
X			if (mf->mf_flags & MFF_ERROR) {
X				error = mf->mf_error;
X				continue;
X			}
X
X			if (!(mf->mf_flags & MFF_MOUNTED) /*|| (mf->mf_flags & MFF_UNMOUNTING)*/) {
Xin_progrss:
X				/*
X				 * If the fs is not mounted or it is unmounting then there
X				 * is a background (un)mount in progress.  In this case
X				 * we just drop the RPC request (return nil) and
X				 * wait for a retry, by which time the (un)mount may
X				 * have completed.
X				 */
X#ifdef DEBUG
X				dlog("ignoring mount of %s in %s -- in progress",
X					fname, mf->mf_mount);
X#endif
X				in_progress++;
X				continue;
X			}
X
X			/*
X			 * Otherwise we have a hit: return the current mount point.
X			 */
X#ifdef DEBUG
X			dlog("matched %s in %s", fname, ap->am_path);
X#endif
X			free(fname);
X			return ap;
X		}
X	}
X
X	if (in_progress) {
X#ifdef DEBUG
X		dlog("Waiting while %d mount(s) in progress", in_progress);
X#endif
X		free(fname);
X		ereturn(-1);
X	}
X
X	/*
X	 * If an error occured then return it.
X	 */
X	if (error) {
X#ifdef DEBUG
X		errno = error; /* XXX */
X		dlog("Returning error: %m", error);
X#endif
X		free(fname);
X		ereturn(error);
X	}
X
X	/*
X	 * If doing a delete then don't create again!
X	 */
X	switch (op) {
X	case VLOOK_DELETE:
X		ereturn(ENOENT);
X		break;
X
X	case VLOOK_CREATE:
X		break;
X
X	default:
X		plog(XLOG_FATAL, "Unknown op to afs_lookuppn: 0x%x", op);
X		ereturn(EINVAL);
X		break;
X	}
X
X	/*
X	 * If the server is going down then just return,
X	 * don't try to mount any more file systems
X	 */
X	if ((int)amd_state >= (int)Finishing) {
X#ifdef DEBUG
X		dlog("not found - server going down anyway");
X#endif
X		free(fname);
X		ereturn(ENOENT);
X	}
X
X	/*
X	 * If we get there then this is a reference to an,
X	 * as yet, unknown name so we need to search the mount
X	 * map for it.
X	 */
X	if (mp->am_pref) {
X		sprintf(path_name, "%s%s", mp->am_pref, fname);
X		pfname = path_name;
X	} else {
X		pfname = fname;
X	}
X
X	mf = mp->am_mnt;
X
X#ifdef DEBUG
X	dlog("will search map info in %s to find %s", mf->mf_info, pfname);
X#endif
X	/*
X	 * Consult the oracle for some mount information.
X	 * info is malloc'ed and belongs to this routine.
X	 * It ends up being free'd in free_continuation().
X	 *
X	 * Note that this may return -1 indicating that information
X	 * is not yet available.
X	 */
X	error = mapc_search((mnt_map*) mf->mf_private, pfname, &info);
X	if (error) {
X		plog(XLOG_MAP, "No map entry for %s", pfname);
X		free(fname);
X		ereturn(error);
X	}
X
X#ifdef DEBUG
X	dlog("mount info is %s", info);
X#endif
X
X	/*
X	 * Split info into an argument vector.
X	 * The vector is malloc'ed and belongs to
X	 * this routine.  It is free'd in free_continuation()
X	 */
X	xivec = ivec = strsplit(info, '\"');
X
X	/*
X	 * Default error code...
X	 */
X	if (ap_hung)
X		error = EWOULDBLOCK;
X	else
X		error = ENOENT;
X
X	/*
X	 * Allocate a new map
X	 */
X	new_mp = exported_ap_alloc();
X	if (new_mp == 0) {
X		free((voidp) xivec);
X		free((voidp) info);
X		free((voidp) fname);
X		ereturn(ENOSPC);
X	}
X
X	if (mf->mf_opts)
X		opts = mf->mf_opts;
X	else
X		opts = "";
X
X	opts = strdup(opts);
X
X#ifdef DEBUG
X	dlog("searching for /defaults entry");
X#endif
X	if (mapc_search((mnt_map*) mf->mf_private, "/defaults", &dflts) == 0) {
X	  	char *dfl;
X		char **rvec;
X#ifdef DEBUG
X		dlog("/defaults gave %s", dflts);
X#endif
X		if (*dflts == '-')
X			dfl = dflts+1;
X		else
X			dfl = dflts;
X
X		/*
X		 * Chop the defaults up
X		 */
X		rvec = strsplit(dfl, '\"');
X		/*
X		 * Extract first value
X		 */
X		dfl = rvec[0];
X
X		/*
X		 * Log error if there were other values
X		 */
X		if (rvec[1]) {
X#ifdef DEBUG
X			dlog("/defaults chopped into %s", dfl);
X#endif
X			plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
X		}
X
X		/*
X		 * Don't need info vector any more
X		 */
X		free((voidp) rvec);
X
X		/*
X		 * If there were any values at all...
X		 */
X		if (dfl) {
X			/*
X			 * Prepend to existing defaults if they exist,
X			 * otherwise just use these defaults.
X			 */
X			if (*opts && *dfl) {
X				char *nopts = (char *) xmalloc(strlen(opts)+strlen(dfl)+2);
X				sprintf(nopts, "%s;%s", dfl, opts);
X				free(opts);
X				opts = nopts;
X			} else if (*dfl) {
X				opts = strealloc(opts, dfl);
X			}
X		}
X		free(dflts);
X	}
X
X	/*
X	 * Fill it in
X	 */
X	init_map(new_mp, fname);
X
X	/*
X	 * Put it in the table
X	 */
X	insert_am(new_mp, mp);
X
X	/*
X	 * Fill in some other fields,
X	 * path and mount point
X	 */
X	new_mp->am_path = str3cat(new_mp->am_path, mp->am_path, *fname == '/' ? "" : "/", fname);
X
X#ifdef DEBUG
X	dlog("setting path to %s", new_mp->am_path);
X#endif
X
X	/*
X	 * Take private copy of pfname
X	 */
X	pfname = strdup(pfname);
X
X	/*
X	 * Construct a continuation
X	 */
X	cp = ALLOC(continuation);
X	cp->mp = new_mp;
X	cp->xivec = xivec;
X	cp->ivec = ivec;
X	cp->info = info;
X	cp->key = pfname;
X	cp->opts = opts;
X	cp->retry = FALSE;
X	cp->tried = FALSE;
X	cp->start = clocktime();
X	cp->def_opts = strdup(opts);
X	bzero((voidp) &cp->fs_opts, sizeof(cp->fs_opts));
X
X	/*
X	 * Try and mount the file system
X	 * If this succeeds immediately (possible
X	 * for a ufs file system) then return
X	 * the attributes, otherwise just
X	 * return an error.
X	 */
X	error = afs_bgmount(cp, error);
X	reschedule_timeout_mp();
X	if (!error) {
X		free(fname);
X		return new_mp;
X	}
X
X	assign_error_mntfs(cp->mp);
X
X	free(fname);
X
X	ereturn(error);
X#undef ereturn
X}
X
X/*
X * Locate next node in sibling list which is mounted
X * and is not an error node.
X */
Xstatic am_node *next_nonerror_node(xp)
Xam_node *xp;
X{
X	mntfs *mf;
X
X	/*
X	 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
X	 * Fixes a race condition when mounting direct automounts.
X	 * Also fixes a problem when doing a readdir on a directory
X	 * containing hung automounts.
X	 */
X	while (xp &&
X	       (!(mf = xp->am_mnt) ||			/* No mounted filesystem */
X	        mf->mf_error != 0 ||			/* There was a mntfs error */
X	        xp->am_error != 0 ||			/* There was a mount error */
X	        !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
X	        (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
X		)
X		xp = xp->am_osib;
X
X	return xp;
X}
X
Xstatic int afs_readdir(mp, cookie, dp, ep)
Xam_node *mp;
Xnfscookie cookie;
Xstruct dirlist *dp;
Xstruct entry *ep;
X{
X	unsigned int gen = *(unsigned int*) cookie;
X	am_node *xp;
X
X	dp->eof = FALSE;
X
X	if (gen == 0) {
X		/*
X		 * In the default instance (which is used to
X		 * start a search) we return "." and "..".
X		 *
X		 * This assumes that the count is big enough
X		 * to allow both "." and ".." to be returned in
X		 * a single packet.  If it isn't (which would
X		 * be fairly unbelievable) then tough.
X		 */
X#ifdef DEBUG
X		dlog("default search");
X#endif
X		xp = next_nonerror_node(mp->am_child);
X		dp->entries = ep;
X
X		/* construct "." */
X		ep[0].fileid = mp->am_gen;
X		ep[0].name = ".";
X		ep[0].nextentry = &ep[1];
X		*(unsigned int *) ep[0].cookie = 0;
X
X		/* construct ".." */
X		if (mp->am_parent)
X			ep[1].fileid = mp->am_parent->am_gen;
X		else
X			ep[1].fileid = mp->am_gen;
X		ep[1].name = "..";
X		ep[1].nextentry = 0;
X		*(unsigned int *) ep[1].cookie =
X			xp ? xp->am_gen : ~(unsigned int)0;
X
X		return 0;
X	}
X
X#ifdef DEBUG
X	dlog("real child");
X#endif
X
X	if (gen == ~(unsigned int)0) {
X#ifdef DEBUG
X		dlog("End of readdir in %s", mp->am_path);
X#endif
X		dp->eof = TRUE;
X		dp->entries = 0;
X		return 0;
X	}
X
X	xp = mp->am_child;
X	while (xp && xp->am_gen != gen)
X		xp = xp->am_osib;
X
X	if (xp) {
X		am_node *xp_next = next_nonerror_node(xp->am_osib);
X
X		if (xp_next) {
X			*(unsigned int *) ep->cookie = xp_next->am_gen;
X		} else {
X			*(unsigned int *) ep->cookie = ~(unsigned int)0;
X		}
X
X		ep->fileid = xp->am_gen;
X		ep->name = xp->am_name;
X
X		ep->nextentry = 0;
X		dp->entries = ep;
X
X		return 0;
X	}
X
X	return ESTALE;
X
X}
X
Xstatic char *dfs_readlink(mp, error_return)
Xam_node *mp;
Xint *error_return;
X{
X	am_node *xp;
X	int rc = 0;
X
X	xp = next_nonerror_node(mp->am_child);
X	if (!xp)
X		xp = afs_lookuppn(mp, mp->am_path+1, &rc, VLOOK_CREATE);
X
X	if (xp) {
X		new_ttl(xp);	/* (7/12/89) from Rein Tollevik */
X		return xp->am_link ? xp->am_link : xp->am_mnt->mf_mount;
X	}
X	if (amd_state == Finishing)
X		return "/tmp";
X	*error_return = rc;
X	return 0;
X}
X
X/*
X * Ops structure
X */
Xam_ops afs_ops = {
X	MNTTYPE_AUTO,
X	afs_match,
X	afs_init,
X	afs_mount,
X	afs_umount,
X	afs_lookuppn,
X	afs_readdir,
X	0, /* afs_readlink */
X	0, /* afs_mounted */
X	afs_umounted,
X	find_afs_srvr,
X	FS_NOTIMEOUT|FS_UBACKGROUND|FS_AMQINFO,
X	&afs_srvr_list
X};
X
Xam_ops dfs_ops = {
X	"direct",
X	afs_match,
X	0, /* dfs_init */
X	afs_mount,
X	afs_umount,
X	efs_lookuppn,
X	efs_readdir,
X	dfs_readlink,
X	0, /* afs_mounted */
X	afs_umounted,
X	find_afs_srvr,
X	FS_NOTIMEOUT|FS_UBACKGROUND|FS_AMQINFO,
X	&afs_srvr_list
X};
END_OF_FILE
if test 34481 -ne `wc -c <'afs_ops.c'`; then
    echo shar: \"'afs_ops.c'\" unpacked with wrong size!
fi
# end of 'afs_ops.c'
fi
echo shar: End of archive 12 \(of 13\).
cp /dev/null ark12isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 13 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.