summaryrefslogtreecommitdiffstats
path: root/contrib/amd/amd/amfs_auto.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/amd/amd/amfs_auto.c')
-rw-r--r--contrib/amd/amd/amfs_auto.c1524
1 files changed, 31 insertions, 1493 deletions
diff --git a/contrib/amd/amd/amfs_auto.c b/contrib/amd/amd/amfs_auto.c
index ad05393..75a451f 100644
--- a/contrib/amd/amd/amfs_auto.c
+++ b/contrib/amd/amd/amfs_auto.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997-2004 Erez Zadok
+ * Copyright (c) 1997-2006 Erez Zadok
* Copyright (c) 1990 Jan-Simon Pendry
* Copyright (c) 1990 Imperial College of Science, Technology & Medicine
* Copyright (c) 1990 The Regents of the University of California.
@@ -36,9 +36,8 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * %W% (Berkeley) %G%
*
- * $Id: amfs_auto.c,v 1.9.2.12 2004/01/06 03:15:16 ezk Exp $
+ * File: am-utils/amd/amfs_auto.c
*
*/
@@ -55,9 +54,7 @@
/****************************************************************************
*** MACROS ***
****************************************************************************/
-#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
-#define DOT_DOT_COOKIE (u_int) 1
/****************************************************************************
*** STRUCTURES ***
@@ -67,10 +64,7 @@
/****************************************************************************
*** FORWARD DEFINITIONS ***
****************************************************************************/
-static int amfs_auto_bgmount(struct continuation *cp, int mpe);
-static int amfs_auto_mount(am_node *mp);
-static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
-static void amfs_auto_umounted(am_node *mp);
+static int amfs_auto_mount(am_node *mp, mntfs *mf);
/****************************************************************************
@@ -79,19 +73,22 @@ static void amfs_auto_umounted(am_node *mp);
am_ops amfs_auto_ops =
{
"auto",
- amfs_auto_match,
+ amfs_generic_match,
0, /* amfs_auto_init */
amfs_auto_mount,
- 0,
- amfs_auto_umount,
- 0,
- amfs_auto_lookuppn,
- amfs_auto_readdir,
+ amfs_generic_umount,
+ amfs_generic_lookup_child,
+ amfs_generic_mount_child,
+ amfs_generic_readdir,
0, /* amfs_auto_readlink */
- 0, /* amfs_auto_mounted */
- amfs_auto_umounted,
- find_amfs_auto_srvr,
- FS_AMQINFO | FS_DIRECTORY
+ amfs_generic_mounted,
+ 0, /* amfs_auto_umounted */
+ amfs_generic_find_srvr,
+ 0, /* amfs_auto_get_wchan */
+ FS_AMQINFO | FS_DIRECTORY,
+#ifdef HAVE_FS_AUTOFS
+ AUTOFS_AUTO_FS_FLAGS,
+#endif /* HAVE_FS_AUTOFS */
};
@@ -99,65 +96,11 @@ am_ops amfs_auto_ops =
*** FUNCTIONS ***
****************************************************************************/
/*
- * AMFS_AUTO needs nothing in particular.
- */
-char *
-amfs_auto_match(am_opts *fo)
-{
- char *p = fo->opt_rfs;
-
- if (!fo->opt_rfs) {
- plog(XLOG_USER, "auto: no mount point named (rfs:=)");
- return 0;
- }
- if (!fo->opt_fs) {
- plog(XLOG_USER, "auto: no map named (fs:=)");
- return 0;
- }
-
- /*
- * Swap round fs:= and rfs:= options
- * ... historical (jsp)
- */
- fo->opt_rfs = fo->opt_fs;
- fo->opt_fs = p;
-
- /*
- * mtab entry turns out to be the name of the mount map
- */
- return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
-}
-
-
-
-
-/*
- * Build a new map cache for this node, or re-use
- * an existing cache for the same map.
- */
-void
-amfs_auto_mkcacheref(mntfs *mf)
-{
- char *cache;
-
- if (mf->mf_fo && mf->mf_fo->opt_cache)
- cache = mf->mf_fo->opt_cache;
- else
- cache = "none";
- mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
- mf->mf_fo->opt_maptype);
- mf->mf_prfree = mapc_free;
-}
-
-
-/*
* Mount a sub-mount
*/
static int
-amfs_auto_mount(am_node *mp)
+amfs_auto_mount(am_node *mp, mntfs *mf)
{
- mntfs *mf = mp->am_mnt;
-
/*
* Pseudo-directories are used to provide some structure
* to the automounted directories instead
@@ -207,1432 +150,27 @@ amfs_auto_mount(am_node *mp)
mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
}
- /*
- * Attach a map cache
- */
- amfs_auto_mkcacheref(mf);
-
- return 0;
-}
-
-
-
-
-/*
- * Unmount an automount sub-node
- */
-int
-amfs_auto_umount(am_node *mp)
-{
- return 0;
-}
-
-
-/*
- * Unmount an automount node
- */
-static void
-amfs_auto_umounted(am_node *mp)
-{
- /*
- * If this is a pseudo-directory then just adjust the link count
- * in the parent, otherwise call the generic unmount routine
- */
- if (mp->am_parent && mp->am_parent->am_parent)
- --mp->am_parent->am_fattr.na_nlink;
-}
-
-
-/*
- * Discard an old continuation
- */
-void
-free_continuation(struct continuation *cp)
-{
- if (cp->callout)
- untimeout(cp->callout);
- XFREE(cp->key);
- XFREE(cp->xivec);
- XFREE(cp->info);
- XFREE(cp->auto_opts);
- XFREE(cp->def_opts);
- free_opts(&cp->fs_opts);
- XFREE(cp);
-}
+#ifdef HAVE_FS_AUTOFS
+ if (mf->mf_flags & MFF_IS_AUTOFS) {
+ char opts[SIZEOF_OPTS];
+ int error;
+ autofs_get_opts(opts, sizeof(opts), mp->am_autofs_fh);
-/*
- * Discard the underlying mount point and replace
- * with a reference to an error filesystem.
- */
-void
-assign_error_mntfs(am_node *mp)
-{
- if (mp->am_error > 0) {
- /*
- * Save the old error code
- */
- int error = mp->am_error;
- if (error <= 0)
- error = mp->am_mnt->mf_error;
- /*
- * Discard the old filesystem
- */
- free_mntfs(mp->am_mnt);
- /*
- * Allocate a new error reference
- */
- mp->am_mnt = new_mntfs();
- /*
- * Put back the error code
- */
- mp->am_mnt->mf_error = error;
- mp->am_mnt->mf_flags |= MFF_ERROR;
- /*
- * Zero the error in the mount point
- */
- mp->am_error = 0;
- }
-}
-
-
-/*
- * The continuation function. This is called by
- * the task notifier when a background mount attempt
- * completes.
- */
-void
-amfs_auto_cont(int rc, int term, voidp closure)
-{
- struct continuation *cp = (struct continuation *) closure;
- mntfs *mf = cp->mp->am_mnt;
-
- /*
- * Definitely not trying to mount at the moment
- */
- mf->mf_flags &= ~MFF_MOUNTING;
-
- /*
- * While we are mounting - try to avoid race conditions
- */
- new_ttl(cp->mp);
-
- /*
- * Wakeup anything waiting for this mount
- */
- wakeup((voidp) mf);
-
- /*
- * Check for termination signal or exit status...
- */
- if (rc || term) {
- am_node *xmp;
-
- if (term) {
- /*
- * Not sure what to do for an error code.
- */
- mf->mf_error = EIO; /* XXX ? */
- mf->mf_flags |= MFF_ERROR;
- plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
- } else {
- /*
- * Check for exit status...
- */
- mf->mf_error = rc;
- mf->mf_flags |= MFF_ERROR;
- errno = rc; /* XXX */
- if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
- plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
+ /* now do the mount */
+ error = amfs_mount(mp, mf, opts);
+ if (error) {
+ errno = error;
+ plog(XLOG_FATAL, "amfs_auto_mount: amfs_mount failed: %m");
+ return error;
}
-
- /*
- * If we get here then that attempt didn't work, so
- * move the info vector pointer along by one and
- * call the background mount routine again
- */
- amd_stats.d_merr++;
- cp->ivec++;
- xmp = cp->mp;
- (void) amfs_auto_bgmount(cp, 0);
- assign_error_mntfs(xmp);
- } else {
- /*
- * The mount worked.
- */
- am_mounted(cp->mp);
- free_continuation(cp);
- }
-
- reschedule_timeout_mp();
-}
-
-
-/*
- * Retry a mount
- */
-void
-amfs_auto_retry(int rc, int term, voidp closure)
-{
- struct continuation *cp = (struct continuation *) closure;
- int error = 0;
-
-#ifdef DEBUG
- dlog("Commencing retry for mount of %s", cp->mp->am_path);
-#endif /* DEBUG */
-
- new_ttl(cp->mp);
-
- if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
- /*
- * The entire mount has timed out. Set the error code and skip past all
- * the info vectors so that amfs_auto_bgmount will not have any more
- * ways to try the mount, so causing an error.
- */
- plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
- error = ETIMEDOUT;
- while (*cp->ivec)
- cp->ivec++;
- /* explicitly forbid further retries after timeout */
- cp->retry = FALSE;
- }
- if (error || !IN_PROGRESS(cp)) {
- (void) amfs_auto_bgmount(cp, error);
- }
- reschedule_timeout_mp();
-}
-
-
-/*
- * Try to mount a file system. Can be called
- * directly or in a sub-process by run_task.
- */
-int
-try_mount(voidp mvp)
-{
- int error = 0;
- am_node *mp = (am_node *) mvp;
- mntfs *mf = mp->am_mnt;
-
- /*
- * If the directory is not yet made and it needs to be made, then make it!
- * This may be run in a background process in which case the flag setting
- * won't be noticed later - but it is set anyway just after run_task is
- * called. It should probably go away totally...
- */
- if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
- error = mkdirs(mf->mf_mount, 0555);
- if (!error)
- mf->mf_flags |= MFF_MKMNT;
- }
-
- /*
- * Mount it!
- */
- error = mount_node(mp);
-
-#ifdef DEBUG
- if (error > 0) {
- errno = error;
- dlog("amfs_auto: call to mount_node(%s) failed: %m", mp->am_path);
- }
-#endif /* DEBUG */
-
- return error;
-}
-
-
-/*
- * Pick a file system to try mounting and
- * do that in the background if necessary
- *
-For each location:
- if it is new -defaults then
- extract and process
- continue;
- fi
- if it is a cut then
- if a location has been tried then
- break;
- fi
- continue;
- fi
- parse mount location
- discard previous mount location if required
- find matching mounted filesystem
- if not applicable then
- this_error = No such file or directory
- continue
- fi
- if the filesystem failed to be mounted then
- this_error = error from filesystem
- elif the filesystem is mounting or unmounting then
- this_error = -1
- elif the fileserver is down then
- this_error = -1
- elif the filesystem is already mounted
- this_error = 0
- break
- fi
- if no error on this mount then
- this_error = initialize mount point
- fi
- if no error on this mount and mount is delayed then
- this_error = -1
- fi
- if this_error < 0 then
- retry = true
- fi
- if no error on this mount then
- make mount point if required
- fi
- if no error on this mount then
- if mount in background then
- run mount in background
- return -1
- else
- this_error = mount in foreground
- fi
- fi
- if an error occurred on this mount then
- update stats
- save error in mount point
- fi
-endfor
- */
-static int
-amfs_auto_bgmount(struct continuation *cp, int mpe)
-{
- mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
- mntfs *mf_retry = 0; /* First mntfs which needed retrying */
- int this_error = -1; /* Per-mount error */
- int hard_error = -1;
- int mp_error = mpe;
-
- /*
- * Try to mount each location.
- * At the end:
- * hard_error == 0 indicates something was mounted.
- * hard_error > 0 indicates everything failed with a hard error
- * hard_error < 0 indicates nothing could be mounted now
- */
- for (; this_error && *cp->ivec; cp->ivec++) {
- am_ops *p;
- am_node *mp = cp->mp;
- char *link_dir;
- int dont_retry;
-
- if (hard_error < 0)
- hard_error = this_error;
-
- this_error = -1;
-
- if (**cp->ivec == '-') {
- /*
- * Pick up new defaults
- */
- if (cp->auto_opts && *cp->auto_opts)
- cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
- else
- cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
-#ifdef DEBUG
- dlog("Setting def_opts to \"%s\"", cp->def_opts);
-#endif /* DEBUG */
- continue;
- }
- /*
- * If a mount has been attempted, and we find
- * a cut then don't try any more locations.
- */
- if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
- if (cp->tried) {
-#ifdef DEBUG
- dlog("Cut: not trying any more locations for %s",
- mp->am_path);
-#endif /* DEBUG */
- break;
- }
- continue;
- }
-
- /* match the operators */
- p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
-
- /*
- * Find a mounted filesystem for this node.
- */
- mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
- cp->fs_opts.opt_fs,
- cp->fs_opts.fs_mtab,
- cp->auto_opts,
- cp->fs_opts.opt_opts,
- cp->fs_opts.opt_remopts);
-
- p = mf->mf_ops;
-#ifdef DEBUG
- dlog("Got a hit with %s", p->fs_type);
-#endif /* DEBUG */
-
- /*
- * Note whether this is a real mount attempt
- */
- if (p == &amfs_error_ops) {
- plog(XLOG_MAP, "Map entry %s for %s did not match", *cp->ivec, mp->am_path);
- if (this_error <= 0)
- this_error = ENOENT;
- continue;
- } else {
- if (cp->fs_opts.fs_mtab) {
- plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
- cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
- }
- cp->tried = TRUE;
- }
-
- this_error = 0;
- dont_retry = FALSE;
-
- if (mp->am_link) {
- XFREE(mp->am_link);
- mp->am_link = 0;
- }
- link_dir = mf->mf_fo->opt_sublink;
-
- if (link_dir && *link_dir) {
- if (*link_dir == '/') {
- mp->am_link = strdup(link_dir);
- } else {
- /*
- * Try getting fs option from continuation, not mountpoint!
- * Don't try logging the string from mf, since it may be bad!
- */
- if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
- plog(XLOG_ERROR, "use %s instead of 0x%lx",
- cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs);
-
- mp->am_link = str3cat((char *) 0,
- cp->fs_opts.opt_fs, "/", link_dir);
-
- normalize_slash(mp->am_link);
- }
- }
-
- if (mf->mf_error > 0) {
- this_error = mf->mf_error;
- } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
- /*
- * Still mounting - retry later
- */
-#ifdef DEBUG
- dlog("Duplicate pending mount fstype %s", p->fs_type);
-#endif /* DEBUG */
- this_error = -1;
- } else if (FSRV_ISDOWN(mf->mf_server)) {
- /*
- * Would just mount from the same place
- * as a hung mount - so give up
- */
-#ifdef DEBUG
- dlog("%s is already hung - giving up", mf->mf_mount);
-#endif /* DEBUG */
- mp_error = EWOULDBLOCK;
- dont_retry = TRUE;
- this_error = -1;
- } else if (mf->mf_flags & MFF_MOUNTED) {
-#ifdef DEBUG
- dlog("duplicate mount of \"%s\" ...", mf->mf_info);
-#endif /* DEBUG */
-
- /*
- * Just call mounted()
- */
- am_mounted(mp);
-
- this_error = 0;
- break;
- }
-
- /*
- * Will usually need to play around with the mount nodes
- * file attribute structure. This must be done here.
- * Try and get things initialized, even if the fileserver
- * is not known to be up. In the common case this will
- * progress things faster.
- */
- if (!this_error) {
- /*
- * Fill in attribute fields.
- */
- if (mf->mf_ops->fs_flags & FS_DIRECTORY)
- mk_fattr(mp, NFDIR);
- else
- mk_fattr(mp, NFLNK);
-
- if (p->fs_init)
- this_error = (*p->fs_init) (mf);
- }
-
- /*
- * Make sure the fileserver is UP before doing any more work
- */
- if (!FSRV_ISUP(mf->mf_server)) {
-#ifdef DEBUG
- dlog("waiting for server %s to become available", mf->mf_server->fs_host);
-#endif /* DEBUG */
- this_error = -1;
- }
-
- if (!this_error && mf->mf_fo->opt_delay) {
- /*
- * If there is a delay timer on the mount
- * then don't try to mount if the timer
- * has not expired.
- */
- int i = atoi(mf->mf_fo->opt_delay);
- if (i > 0 && clocktime() < (cp->start + i)) {
-#ifdef DEBUG
- dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start));
-#endif /* DEBUG */
- this_error = -1;
- }
- }
-
- if (this_error < 0 && !dont_retry) {
- if (!mf_retry)
- mf_retry = dup_mntfs(mf);
- cp->retry = TRUE;
-#ifdef DEBUG
- dlog("will retry ...\n");
-#endif /* DEBUG */
- break;
- }
-
- if (!this_error) {
- if (p->fs_flags & FS_MBACKGROUND) {
- mf->mf_flags |= MFF_MOUNTING; /* XXX */
-#ifdef DEBUG
- dlog("backgrounding mount of \"%s\"", mf->mf_mount);
-#endif /* DEBUG */
- if (cp->callout) {
- untimeout(cp->callout);
- cp->callout = 0;
- }
-
- /* actually run the task, backgrounding as necessary */
- run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
-
- mf->mf_flags |= MFF_MKMNT; /* XXX */
- if (mf_retry)
- free_mntfs(mf_retry);
- return -1;
- } else {
-#ifdef DEBUG
- dlog("foreground mount of \"%s\" ...", mf->mf_info);
-#endif /* DEBUG */
- this_error = try_mount((voidp) mp);
- if (this_error < 0) {
- if (!mf_retry)
- mf_retry = dup_mntfs(mf);
- cp->retry = TRUE;
- }
- }
- }
-
- if (this_error >= 0) {
- if (this_error > 0) {
- amd_stats.d_merr++;
- if (mf != mf_retry) {
- mf->mf_error = this_error;
- mf->mf_flags |= MFF_ERROR;
- }
- }
-
- /*
- * Wakeup anything waiting for this mount
- */
- wakeup((voidp) mf);
- }
- }
-
- if (this_error && cp->retry) {
- free_mntfs(mf);
- mf = cp->mp->am_mnt = mf_retry;
- /*
- * Not retrying again (so far)
- */
- cp->retry = FALSE;
- cp->tried = FALSE;
- /*
- * Start at the beginning.
- * Rewind the location vector and
- * reset the default options.
- */
-#ifdef DEBUG
- dlog("(skipping rewind)\n");
-#endif /* DEBUG */
- /*
- * Arrange that amfs_auto_bgmount is called
- * after anything else happens.
- */
-#ifdef DEBUG
- dlog("Arranging to retry mount of %s", cp->mp->am_path);
-#endif /* DEBUG */
- sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
- if (cp->callout)
- untimeout(cp->callout);
- cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
-
- cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
-
- /*
- * Not done yet - so don't return anything
- */
- return -1;
- }
-
- if (hard_error < 0 || this_error == 0)
- hard_error = this_error;
-
- /*
- * Discard handle on duff filesystem.
- * This should never happen since it
- * should be caught by the case above.
- */
- if (mf_retry) {
- if (hard_error)
- plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
- free_mntfs(mf_retry);
}
+#endif /* HAVE_FS_AUTOFS */
/*
- * If we get here, then either the mount succeeded or
- * there is no more mount information available.
- */
- if (hard_error < 0 && mp_error)
- hard_error = cp->mp->am_error = mp_error;
- if (hard_error > 0) {
- /*
- * Set a small(ish) timeout on an error node if
- * the error was not a time out.
- */
- switch (hard_error) {
- case ETIMEDOUT:
- case EWOULDBLOCK:
- cp->mp->am_timeo = 17;
- break;
- default:
- cp->mp->am_timeo = 5;
- break;
- }
- new_ttl(cp->mp);
- }
-
- /*
- * Make sure that the error value in the mntfs has a
- * reasonable value.
- */
- if (mf->mf_error < 0) {
- mf->mf_error = hard_error;
- if (hard_error)
- mf->mf_flags |= MFF_ERROR;
- }
-
- /*
- * In any case we don't need the continuation any more
- */
- free_continuation(cp);
-
- return hard_error;
-}
-
-
-/*
- * Automount interface to RPC lookup routine
- * Find the corresponding entry and return
- * the file handle for it.
- */
-am_node *
-amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
-{
- am_node *ap, *new_mp, *ap_hung;
- char *info; /* Mount info - where to get the file system */
- char **ivec, **xivec; /* Split version of info */
- char *auto_opts; /* Automount options */
- int error = 0; /* Error so far */
- char path_name[MAXPATHLEN]; /* General path name buffer */
- char *pfname; /* Path for database lookup */
- struct continuation *cp; /* Continuation structure if need to mount */
- int in_progress = 0; /* # of (un)mount in progress */
- char *dflts;
- mntfs *mf;
-
-#ifdef DEBUG
- dlog("in amfs_auto_lookuppn");
-#endif /* DEBUG */
-
- /*
- * If the server is shutting down
- * then don't return information
- * about the mount point.
- */
- if (amd_state == Finishing) {
-#ifdef DEBUG
- if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
- dlog("%s mount ignored - going down", fname);
- } else {
- dlog("%s/%s mount ignored - going down", mp->am_path, fname);
- }
-#endif /* DEBUG */
- ereturn(ENOENT);
- }
-
- /*
- * Handle special case of "." and ".."
- */
- if (fname[0] == '.') {
- if (fname[1] == '\0')
- return mp; /* "." is the current node */
- if (fname[1] == '.' && fname[2] == '\0') {
- if (mp->am_parent) {
-#ifdef DEBUG
- dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
-#endif /* DEBUG */
- return mp->am_parent; /* ".." is the parent node */
- }
- ereturn(ESTALE);
- }
- }
-
- /*
- * Check for valid key name.
- * If it is invalid then pretend it doesn't exist.
- */
- if (!valid_key(fname)) {
- plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
- ereturn(ENOENT);
- }
-
- /*
- * Expand key name.
- * fname is now a private copy.
- */
- fname = expand_key(fname);
-
- for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
- /*
- * Otherwise search children of this node
- */
- if (FSTREQ(ap->am_name, fname)) {
- mf = ap->am_mnt;
- if (ap->am_error) {
- error = ap->am_error;
- continue;
- }
- /*
- * If the error code is undefined then it must be
- * in progress.
- */
- if (mf->mf_error < 0)
- goto in_progrss;
-
- /*
- * Check for a hung node
- */
- if (FSRV_ISDOWN(mf->mf_server)) {
-#ifdef DEBUG
- dlog("server hung");
-#endif /* DEBUG */
- error = ap->am_error;
- ap_hung = ap;
- continue;
- }
- /*
- * If there was a previous error with this node
- * then return that error code.
- */
- if (mf->mf_flags & MFF_ERROR) {
- error = mf->mf_error;
- continue;
- }
- if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
- in_progrss:
- /*
- * If the fs is not mounted or it is unmounting then there
- * is a background (un)mount in progress. In this case
- * we just drop the RPC request (return nil) and
- * wait for a retry, by which time the (un)mount may
- * have completed.
- */
-#ifdef DEBUG
- dlog("ignoring mount of %s in %s -- flags (%x) in progress",
- fname, mf->mf_mount, mf->mf_flags);
-#endif /* DEBUG */
- in_progress++;
- continue;
- }
-
- /*
- * Otherwise we have a hit: return the current mount point.
- */
-#ifdef DEBUG
- dlog("matched %s in %s", fname, ap->am_path);
-#endif /* DEBUG */
- XFREE(fname);
- return ap;
- }
- }
-
- if (in_progress) {
-#ifdef DEBUG
- dlog("Waiting while %d mount(s) in progress", in_progress);
-#endif /* DEBUG */
- XFREE(fname);
- ereturn(-1);
- }
-
- /*
- * If an error occurred then return it.
- */
- if (error) {
-#ifdef DEBUG
- errno = error; /* XXX */
- dlog("Returning error: %m");
-#endif /* DEBUG */
- XFREE(fname);
- ereturn(error);
- }
-
- /*
- * If doing a delete then don't create again!
- */
- switch (op) {
- case VLOOK_DELETE:
- ereturn(ENOENT);
-
- case VLOOK_CREATE:
- break;
-
- default:
- plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
- ereturn(EINVAL);
- }
-
- /*
- * If the server is going down then just return,
- * don't try to mount any more file systems
- */
- if ((int) amd_state >= (int) Finishing) {
-#ifdef DEBUG
- dlog("not found - server going down anyway");
-#endif /* DEBUG */
- XFREE(fname);
- ereturn(ENOENT);
- }
-
- /*
- * If we get there then this is a reference to an,
- * as yet, unknown name so we need to search the mount
- * map for it.
- */
- if (mp->am_pref) {
- sprintf(path_name, "%s%s", mp->am_pref, fname);
- pfname = path_name;
- } else {
- pfname = fname;
- }
-
- mf = mp->am_mnt;
-
-#ifdef DEBUG
- dlog("will search map info in %s to find %s", mf->mf_info, pfname);
-#endif /* DEBUG */
- /*
- * Consult the oracle for some mount information.
- * info is malloc'ed and belongs to this routine.
- * It ends up being free'd in free_continuation().
- *
- * Note that this may return -1 indicating that information
- * is not yet available.
- */
- error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
- if (error) {
- if (error > 0)
- plog(XLOG_MAP, "No map entry for %s", pfname);
- else
- plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
- XFREE(fname);
- ereturn(error);
- }
-#ifdef DEBUG
- dlog("mount info is %s", info);
-#endif /* DEBUG */
-
- /*
- * Split info into an argument vector.
- * The vector is malloc'ed and belongs to
- * this routine. It is free'd in free_continuation()
- */
- xivec = ivec = strsplit(info, ' ', '\"');
-
- /*
- * Default error code...
- */
- if (ap_hung)
- error = EWOULDBLOCK;
- else
- error = ENOENT;
-
- /*
- * Allocate a new map
- */
- new_mp = exported_ap_alloc();
- if (new_mp == 0) {
- XFREE(xivec);
- XFREE(info);
- XFREE(fname);
- ereturn(ENOSPC);
- }
- if (mf->mf_auto)
- auto_opts = mf->mf_auto;
- else
- auto_opts = "";
-
- auto_opts = strdup(auto_opts);
-
-#ifdef DEBUG
- dlog("searching for /defaults entry");
-#endif /* DEBUG */
- if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
- char *dfl;
- char **rvec;
-#ifdef DEBUG
- dlog("/defaults gave %s", dflts);
-#endif /* DEBUG */
- if (*dflts == '-')
- dfl = dflts + 1;
- else
- dfl = dflts;
-
- /*
- * Chop the defaults up
- */
- rvec = strsplit(dfl, ' ', '\"');
-
- if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
- /*
- * Pick whichever first entry matched the list of selectors.
- * Strip the selectors from the string, and assign to dfl the
- * rest of the string.
- */
- if (rvec) {
- am_opts ap;
- am_ops *pt;
- char **sp = rvec;
- while (*sp) { /* loop until you find something, if any */
- memset((char *) &ap, 0, sizeof(am_opts));
- /*
- * This next routine cause many spurious "expansion of ... is"
- * messages, which are ignored, b/c all we need out of this
- * routine is to match selectors. These spurious messages may
- * be wrong, esp. if they try to expand ${key} b/c it will
- * get expanded to "/defaults"
- */
- pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
- mp->am_parent->am_mnt->mf_info);
- free_opts(&ap); /* don't leak */
- if (pt == &amfs_error_ops) {
- plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
- } else {
- dfl = strip_selectors(*sp, "/defaults");
- plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
- break;
- }
- ++sp;
- }
- }
- } else { /* not enable_default_selectors */
- /*
- * Extract first value
- */
- dfl = rvec[0];
- }
-
- /*
- * If there were any values at all...
- */
- if (dfl) {
- /*
- * Log error if there were other values
- */
- if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
-# ifdef DEBUG
- dlog("/defaults chopped into %s", dfl);
-# endif /* DEBUG */
- plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
- }
-
- /*
- * Prepend to existing defaults if they exist,
- * otherwise just use these defaults.
- */
- if (*auto_opts && *dfl) {
- char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
- sprintf(nopts, "%s;%s", dfl, auto_opts);
- XFREE(auto_opts);
- auto_opts = nopts;
- } else if (*dfl) {
- auto_opts = strealloc(auto_opts, dfl);
- }
- }
- XFREE(dflts);
- /*
- * Don't need info vector any more
- */
- XFREE(rvec);
- }
-
- /*
- * Fill it in
- */
- init_map(new_mp, fname);
-
- /*
- * Put it in the table
- */
- insert_am(new_mp, mp);
-
- /*
- * Fill in some other fields,
- * path and mount point.
- *
- * bugfix: do not prepend old am_path if direct map
- * <wls@astro.umd.edu> William Sebok
- */
- new_mp->am_path = str3cat(new_mp->am_path,
- mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
- *fname == '/' ? "" : "/", fname);
-
-#ifdef DEBUG
- dlog("setting path to %s", new_mp->am_path);
-#endif /* DEBUG */
-
- /*
- * Take private copy of pfname
- */
- pfname = strdup(pfname);
-
- /*
- * Construct a continuation
- */
- cp = ALLOC(struct continuation);
- cp->callout = 0;
- cp->mp = new_mp;
- cp->xivec = xivec;
- cp->ivec = ivec;
- cp->info = info;
- cp->key = pfname;
- cp->auto_opts = auto_opts;
- cp->retry = FALSE;
- cp->tried = FALSE;
- cp->start = clocktime();
- cp->def_opts = strdup(auto_opts);
- memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
-
- /*
- * Try and mount the file system. If this succeeds immediately (possible
- * for a ufs file system) then return the attributes, otherwise just
- * return an error.
- */
- error = amfs_auto_bgmount(cp, error);
- reschedule_timeout_mp();
- if (!error) {
- XFREE(fname);
- return new_mp;
- }
-
- /*
- * Code for quick reply. If nfs_program_2_transp is set, then
- * its the transp that's been passed down from nfs_program_2().
- * If new_mp->am_transp is not already set, set it by copying in
- * nfs_program_2_transp. Once am_transp is set, quick_reply() can
- * use it to send a reply to the client that requested this mount.
- */
- if (nfs_program_2_transp && !new_mp->am_transp) {
- new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
- *(new_mp->am_transp) = *nfs_program_2_transp;
- }
- if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
- new_mp->am_error = error;
-
- assign_error_mntfs(new_mp);
-
- XFREE(fname);
-
- ereturn(error);
-}
-
-
-/*
- * Locate next node in sibling list which is mounted
- * and is not an error node.
- */
-am_node *
-next_nonerror_node(am_node *xp)
-{
- mntfs *mf;
-
- /*
- * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
- * Fixes a race condition when mounting direct automounts.
- * Also fixes a problem when doing a readdir on a directory
- * containing hung automounts.
- */
- while (xp &&
- (!(mf = xp->am_mnt) || /* No mounted filesystem */
- mf->mf_error != 0 || /* There was a mntfs error */
- xp->am_error != 0 || /* There was a mount error */
- !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
- (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
- )
- xp = xp->am_osib;
-
- return xp;
-}
-
-
-/*
- * This readdir function which call a special version of it that allows
- * browsing if browsable_dirs=yes was set on the map.
- */
-int
-amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
-{
- u_int gen = *(u_int *) cookie;
- am_node *xp;
- mntent_t mnt;
-#ifdef DEBUG
- nfsentry *ne;
- static int j;
-#endif /* DEBUG */
-
- dp->dl_eof = FALSE; /* assume readdir not done */
-
- /* check if map is browsable */
- if (mp->am_mnt && mp->am_mnt->mf_mopts) {
- mnt.mnt_opts = mp->am_mnt->mf_mopts;
- if (hasmntopt(&mnt, "fullybrowsable"))
- return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
- if (hasmntopt(&mnt, "browsable"))
- return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
- }
-
- /* when gen is 0, we start reading from the beginning of the directory */
- if (gen == 0) {
- /*
- * In the default instance (which is used to start a search) we return
- * "." and "..".
- *
- * This assumes that the count is big enough to allow both "." and ".."
- * to be returned in a single packet. If it isn't (which would be
- * fairly unbelievable) then tough.
- */
-#ifdef DEBUG
- dlog("amfs_auto_readdir: default search");
-#endif /* DEBUG */
- /*
- * Check for enough room. This is extremely approximate but is more
- * than enough space. Really need 2 times:
- * 4byte fileid
- * 4byte cookie
- * 4byte name length
- * 4byte name
- * plus the dirlist structure */
- if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
- return EINVAL;
-
- xp = next_nonerror_node(mp->am_child);
- dp->dl_entries = ep;
-
- /* construct "." */
- ep[0].ne_fileid = mp->am_gen;
- ep[0].ne_name = ".";
- ep[0].ne_nextentry = &ep[1];
- *(u_int *) ep[0].ne_cookie = 0;
-
- /* construct ".." */
- if (mp->am_parent)
- ep[1].ne_fileid = mp->am_parent->am_gen;
- else
- ep[1].ne_fileid = mp->am_gen;
- ep[1].ne_name = "..";
- ep[1].ne_nextentry = 0;
- *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE);
-
- if (!xp)
- dp->dl_eof = TRUE; /* by default assume readdir done */
-
-#ifdef DEBUG
- amuDebug(D_READDIR)
- for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
- plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d",
- j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
-#endif /* DEBUG */
- return 0;
- }
-#ifdef DEBUG
- dlog("amfs_auto_readdir: real child");
-#endif /* DEBUG */
-
- if (gen == DOT_DOT_COOKIE) {
-#ifdef DEBUG
- dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path);
-#endif /* DEBUG */
- dp->dl_eof = TRUE;
- dp->dl_entries = 0;
-#ifdef DEBUG
- amuDebug(D_READDIR)
- plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n");
-#endif /* DEBUG */
- return 0;
- }
-
- /* non-browsable directories code */
- xp = mp->am_child;
- while (xp && xp->am_gen != gen)
- xp = xp->am_osib;
-
- if (xp) {
- int nbytes = count / 2; /* conservative */
- int todo = MAX_READDIR_ENTRIES;
-
- dp->dl_entries = ep;
- do {
- am_node *xp_next = next_nonerror_node(xp->am_osib);
-
- if (xp_next) {
- *(u_int *) ep->ne_cookie = xp_next->am_gen;
- } else {
- *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE;
- dp->dl_eof = TRUE;
- }
-
- ep->ne_fileid = xp->am_gen;
- ep->ne_name = xp->am_name;
- nbytes -= sizeof(*ep) + 1;
- if (xp->am_name)
- nbytes -= strlen(xp->am_name);
-
- xp = xp_next;
-
- if (nbytes > 0 && !dp->dl_eof && todo > 1) {
- ep->ne_nextentry = ep + 1;
- ep++;
- --todo;
- } else {
- todo = 0;
- }
- } while (todo > 0);
-
- ep->ne_nextentry = 0;
-
-#ifdef DEBUG
- amuDebug(D_READDIR)
- for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
- plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d",
- j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
-#endif /* DEBUG */
- return 0;
- }
- return ESTALE;
-}
-
-
-/* This one is called only if map is browsable */
-static int
-amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
-{
- u_int gen = *(u_int *) cookie;
- int chain_length, i;
- static nfsentry *te, *te_next;
-#ifdef DEBUG
- nfsentry *ne;
- static int j;
-#endif /* DEBUG */
-
- dp->dl_eof = FALSE; /* assume readdir not done */
-
-#ifdef DEBUG
- amuDebug(D_READDIR)
- plog(XLOG_DEBUG, "amfs_auto_readdir_browsable gen=%u, count=%d",
- gen, count);
-#endif /* DEBUG */
-
- if (gen == 0) {
- /*
- * In the default instance (which is used to start a search) we return
- * "." and "..".
- *
- * This assumes that the count is big enough to allow both "." and ".."
- * to be returned in a single packet. If it isn't (which would be
- * fairly unbelievable) then tough.
- */
-#ifdef DEBUG
- dlog("amfs_auto_readdir_browsable: default search");
-#endif /* DEBUG */
- /*
- * Check for enough room. This is extremely approximate but is more
- * than enough space. Really need 2 times:
- * 4byte fileid
- * 4byte cookie
- * 4byte name length
- * 4byte name
- * plus the dirlist structure */
- if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
- return EINVAL;
-
- /*
- * compute # of entries to send in this chain.
- * heuristics: 128 bytes per entry.
- * This is too much probably, but it seems to work better because
- * of the re-entrant nature of nfs_readdir, and esp. on systems
- * like OpenBSD 2.2.
- */
- chain_length = count / 128;
-
- /* reset static state counters */
- te = te_next = NULL;
-
- dp->dl_entries = ep;
-
- /* construct "." */
- ep[0].ne_fileid = mp->am_gen;
- ep[0].ne_name = ".";
- ep[0].ne_nextentry = &ep[1];
- *(u_int *) ep[0].ne_cookie = 0;
-
- /* construct ".." */
- if (mp->am_parent)
- ep[1].ne_fileid = mp->am_parent->am_gen;
- else
- ep[1].ne_fileid = mp->am_gen;
-
- ep[1].ne_name = "..";
- ep[1].ne_nextentry = 0;
- *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE;
-
- /*
- * If map is browsable, call a function make_entry_chain() to construct
- * a linked list of unmounted keys, and return it. Then link the chain
- * to the regular list. Get the chain only once, but return
- * chunks of it each time.
- */
- te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
- if (!te)
- return 0;
-#ifdef DEBUG
- amuDebug(D_READDIR)
- for (j=0,ne=te; ne; ne=ne->ne_nextentry)
- plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name);
-#endif /* DEBUG */
-
- /* return only "chain_length" entries */
- te_next = te;
- for (i=1; i<chain_length; ++i) {
- te_next = te_next->ne_nextentry;
- if (!te_next)
- break;
- }
- if (te_next) {
- nfsentry *te_saved = te_next->ne_nextentry;
- te_next->ne_nextentry = NULL; /* terminate "te" chain */
- te_next = te_saved; /* save rest of "te" for next iteration */
- dp->dl_eof = FALSE; /* tell readdir there's more */
- } else {
- dp->dl_eof = TRUE; /* tell readdir that's it */
- }
- ep[1].ne_nextentry = te; /* append this chunk of "te" chain */
-#ifdef DEBUG
- amuDebug(D_READDIR) {
- for (j=0,ne=te; ne; ne=ne->ne_nextentry)
- plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name);
- for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
- plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d",
- j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
- plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof);
- }
-#endif /* DEBUG_READDIR */
- return 0;
- } /* end of "if (gen == 0)" statement */
-
-#ifdef DEBUG
- dlog("amfs_auto_readdir_browsable: real child");
-#endif /* DEBUG */
-
- if (gen == DOT_DOT_COOKIE) {
-#ifdef DEBUG
- dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path);
-#endif /* DEBUG */
- dp->dl_eof = TRUE;
- dp->dl_entries = 0;
- return 0;
- }
-
- /*
- * If browsable directories, then continue serving readdir() with another
- * chunk of entries, starting from where we left off (when gen was equal
- * to 0). Once again, assume last chunk served to readdir.
- */
- dp->dl_eof = TRUE;
- dp->dl_entries = ep;
-
- te = te_next; /* reset 'te' from last saved te_next */
- if (!te) { /* another indicator of end of readdir */
- dp->dl_entries = 0;
- return 0;
- }
- /*
- * compute # of entries to send in this chain.
- * heuristics: 128 bytes per entry.
+ * Attach a map cache
*/
- chain_length = count / 128;
+ amfs_mkcacheref(mf);
- /* return only "chain_length" entries */
- for (i=1; i<chain_length; ++i) {
- te_next = te_next->ne_nextentry;
- if (!te_next)
- break;
- }
- if (te_next) {
- nfsentry *te_saved = te_next->ne_nextentry;
- te_next->ne_nextentry = NULL; /* terminate "te" chain */
- te_next = te_saved; /* save rest of "te" for next iteration */
- dp->dl_eof = FALSE; /* tell readdir there's more */
- }
- ep = te; /* send next chunk of "te" chain */
- dp->dl_entries = ep;
-#ifdef DEBUG
- amuDebug(D_READDIR) {
- plog(XLOG_DEBUG, "dl_entries=0x%lx, te_next=0x%lx, dl_eof=%d",
- (long) dp->dl_entries, (long) te_next, dp->dl_eof);
- for (ne=te; ne; ne=ne->ne_nextentry)
- plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name);
- }
-#endif /* DEBUG */
return 0;
}
-
-
-int
-amfs_auto_fmount(am_node *mp)
-{
- mntfs *mf = mp->am_mnt;
- return (*mf->mf_ops->fmount_fs) (mf);
-}
-
-
-int
-amfs_auto_fumount(am_node *mp)
-{
- mntfs *mf = mp->am_mnt;
- return (*mf->mf_ops->fumount_fs) (mf);
-}
OpenPOWER on IntegriCloud