v21i100: An Automounter for NFS systems, Part12/13
Rich Salz
rsalz at uunet.uu.net
Wed Apr 11 23:26:07 AEST 1990
Submitted-by: Jan-Simon Pendry <jsp at 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 at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list