amfs_auto.c revision 42629
138494Sobrien/* 238494Sobrien * Copyright (c) 1997-1998 Erez Zadok 338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry 438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 538494Sobrien * Copyright (c) 1990 The Regents of the University of California. 638494Sobrien * All rights reserved. 738494Sobrien * 838494Sobrien * This code is derived from software contributed to Berkeley by 938494Sobrien * Jan-Simon Pendry at Imperial College, London. 1038494Sobrien * 1138494Sobrien * Redistribution and use in source and binary forms, with or without 1238494Sobrien * modification, are permitted provided that the following conditions 1338494Sobrien * are met: 1438494Sobrien * 1. Redistributions of source code must retain the above copyright 1538494Sobrien * notice, this list of conditions and the following disclaimer. 1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1738494Sobrien * notice, this list of conditions and the following disclaimer in the 1838494Sobrien * documentation and/or other materials provided with the distribution. 1938494Sobrien * 3. All advertising materials mentioning features or use of this software 2042629Sobrien * must display the following acknowledgment: 2138494Sobrien * This product includes software developed by the University of 2238494Sobrien * California, Berkeley and its contributors. 2338494Sobrien * 4. Neither the name of the University nor the names of its contributors 2438494Sobrien * may be used to endorse or promote products derived from this software 2538494Sobrien * without specific prior written permission. 2638494Sobrien * 2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3038494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3738494Sobrien * SUCH DAMAGE. 3838494Sobrien * 3938494Sobrien * %W% (Berkeley) %G% 4038494Sobrien * 4142629Sobrien * $Id: amfs_auto.c,v 1.1.1.1 1998/11/05 02:04:46 ezk Exp $ 4238494Sobrien * 4338494Sobrien */ 4438494Sobrien 4538494Sobrien/* 4638494Sobrien * Automount file system 4738494Sobrien */ 4838494Sobrien 4938494Sobrien#ifdef HAVE_CONFIG_H 5038494Sobrien# include <config.h> 5138494Sobrien#endif /* HAVE_CONFIG_H */ 5238494Sobrien#include <am_defs.h> 5338494Sobrien#include <amd.h> 5438494Sobrien 5538494Sobrien/**************************************************************************** 5638494Sobrien *** MACROS *** 5738494Sobrien ****************************************************************************/ 5838494Sobrien#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) 5938494Sobrien 6038494Sobrien/* DEVELOPERS: turn this on for special debugging of readdir code */ 6138494Sobrien#undef DEBUG_READDIR 6238494Sobrien 6338494Sobrien/**************************************************************************** 6438494Sobrien *** STRUCTURES *** 6538494Sobrien ****************************************************************************/ 6638494Sobrien 6738494Sobrien 6838494Sobrien 6938494Sobrien/**************************************************************************** 7038494Sobrien *** FORWARD DEFINITIONS *** 7138494Sobrien ****************************************************************************/ 7238494Sobrienstatic int amfs_auto_bgmount(struct continuation * cp, int mpe); 7338494Sobrienstatic int amfs_auto_mount(am_node *mp); 7438494Sobrienstatic int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable); 7538494Sobrienstatic void amfs_auto_umounted(am_node *mp); 7638494Sobrien 7738494Sobrien 7838494Sobrien/**************************************************************************** 7938494Sobrien *** OPS STRUCTURES *** 8038494Sobrien ****************************************************************************/ 8138494Sobrienam_ops amfs_auto_ops = 8238494Sobrien{ 8338494Sobrien "auto", 8438494Sobrien amfs_auto_match, 8538494Sobrien 0, /* amfs_auto_init */ 8638494Sobrien amfs_auto_mount, 8738494Sobrien 0, 8838494Sobrien amfs_auto_umount, 8938494Sobrien 0, 9038494Sobrien amfs_auto_lookuppn, 9138494Sobrien amfs_auto_readdir, 9238494Sobrien 0, /* amfs_auto_readlink */ 9338494Sobrien 0, /* amfs_auto_mounted */ 9438494Sobrien amfs_auto_umounted, 9538494Sobrien find_amfs_auto_srvr, 9638494Sobrien FS_AMQINFO | FS_DIRECTORY 9738494Sobrien}; 9838494Sobrien 9938494Sobrien 10038494Sobrien/**************************************************************************** 10138494Sobrien *** FUNCTIONS *** 10238494Sobrien ****************************************************************************/ 10338494Sobrien/* 10438494Sobrien * AMFS_AUTO needs nothing in particular. 10538494Sobrien */ 10638494Sobrienchar * 10738494Sobrienamfs_auto_match(am_opts *fo) 10838494Sobrien{ 10938494Sobrien char *p = fo->opt_rfs; 11038494Sobrien 11138494Sobrien if (!fo->opt_rfs) { 11238494Sobrien plog(XLOG_USER, "auto: no mount point named (rfs:=)"); 11338494Sobrien return 0; 11438494Sobrien } 11538494Sobrien if (!fo->opt_fs) { 11638494Sobrien plog(XLOG_USER, "auto: no map named (fs:=)"); 11738494Sobrien return 0; 11838494Sobrien } 11938494Sobrien 12038494Sobrien /* 12138494Sobrien * Swap round fs:= and rfs:= options 12238494Sobrien * ... historical (jsp) 12338494Sobrien */ 12438494Sobrien fo->opt_rfs = fo->opt_fs; 12538494Sobrien fo->opt_fs = p; 12638494Sobrien 12738494Sobrien /* 12838494Sobrien * mtab entry turns out to be the name of the mount map 12938494Sobrien */ 13038494Sobrien return strdup(fo->opt_rfs ? fo->opt_rfs : "."); 13138494Sobrien} 13238494Sobrien 13338494Sobrien 13438494Sobrien 13538494Sobrien 13638494Sobrien/* 13738494Sobrien * Build a new map cache for this node, or re-use 13838494Sobrien * an existing cache for the same map. 13938494Sobrien */ 14038494Sobrienvoid 14138494Sobrienamfs_auto_mkcacheref(mntfs *mf) 14238494Sobrien{ 14338494Sobrien char *cache; 14438494Sobrien 14538494Sobrien if (mf->mf_fo && mf->mf_fo->opt_cache) 14638494Sobrien cache = mf->mf_fo->opt_cache; 14738494Sobrien else 14838494Sobrien cache = "none"; 14938494Sobrien mf->mf_private = (voidp) mapc_find(mf->mf_info, cache, 15038494Sobrien mf->mf_fo->opt_maptype); 15138494Sobrien mf->mf_prfree = mapc_free; 15238494Sobrien} 15338494Sobrien 15438494Sobrien 15538494Sobrien/* 15638494Sobrien * Mount a sub-mount 15738494Sobrien */ 15838494Sobrienstatic int 15938494Sobrienamfs_auto_mount(am_node *mp) 16038494Sobrien{ 16138494Sobrien mntfs *mf = mp->am_mnt; 16238494Sobrien 16338494Sobrien /* 16438494Sobrien * Pseudo-directories are used to provide some structure 16538494Sobrien * to the automounted directories instead 16638494Sobrien * of putting them all in the top-level automount directory. 16738494Sobrien * 16838494Sobrien * Here, just increment the parent's link count. 16938494Sobrien */ 17038494Sobrien mp->am_parent->am_fattr.na_nlink++; 17138494Sobrien 17238494Sobrien /* 17338494Sobrien * Info field of . means use parent's info field. 17438494Sobrien * Historical - not documented. 17538494Sobrien */ 17638494Sobrien if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0') 17738494Sobrien mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info); 17838494Sobrien 17938494Sobrien /* 18038494Sobrien * Compute prefix: 18138494Sobrien * 18238494Sobrien * If there is an option prefix then use that else 18338494Sobrien * If the parent had a prefix then use that with name 18438494Sobrien * of this node appended else 18538494Sobrien * Use the name of this node. 18638494Sobrien * 18738494Sobrien * That means if you want no prefix you must say so 18838494Sobrien * in the map. 18938494Sobrien */ 19038494Sobrien if (mf->mf_fo->opt_pref) { 19138494Sobrien /* allow pref:=null to set a real null prefix */ 19238494Sobrien if (STREQ(mf->mf_fo->opt_pref, "null")) { 19338494Sobrien mp->am_pref = ""; 19438494Sobrien } else { 19538494Sobrien /* 19638494Sobrien * the prefix specified as an option 19738494Sobrien */ 19838494Sobrien mp->am_pref = strdup(mf->mf_fo->opt_pref); 19938494Sobrien } 20038494Sobrien } else { 20138494Sobrien /* 20238494Sobrien * else the parent's prefix 20338494Sobrien * followed by the name 20438494Sobrien * followed by / 20538494Sobrien */ 20638494Sobrien char *ppref = mp->am_parent->am_pref; 20738494Sobrien if (ppref == 0) 20838494Sobrien ppref = ""; 20938494Sobrien mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/"); 21038494Sobrien } 21138494Sobrien 21238494Sobrien /* 21338494Sobrien * Attach a map cache 21438494Sobrien */ 21538494Sobrien amfs_auto_mkcacheref(mf); 21638494Sobrien 21738494Sobrien return 0; 21838494Sobrien} 21938494Sobrien 22038494Sobrien 22138494Sobrien 22238494Sobrien 22338494Sobrien/* 22438494Sobrien * Unmount an automount sub-node 22538494Sobrien */ 22638494Sobrienint 22738494Sobrienamfs_auto_umount(am_node *mp) 22838494Sobrien{ 22938494Sobrien return 0; 23038494Sobrien} 23138494Sobrien 23238494Sobrien 23338494Sobrien/* 23438494Sobrien * Unmount an automount node 23538494Sobrien */ 23638494Sobrienstatic void 23738494Sobrienamfs_auto_umounted(am_node *mp) 23838494Sobrien{ 23938494Sobrien /* 24038494Sobrien * If this is a pseudo-directory then just adjust the link count 24138494Sobrien * in the parent, otherwise call the generic unmount routine 24238494Sobrien */ 24338494Sobrien if (mp->am_parent && mp->am_parent->am_parent) 24438494Sobrien --mp->am_parent->am_fattr.na_nlink; 24538494Sobrien} 24638494Sobrien 24738494Sobrien 24838494Sobrien/* 24938494Sobrien * Discard an old continuation 25038494Sobrien */ 25138494Sobrienvoid 25238494Sobrienfree_continuation(struct continuation *cp) 25338494Sobrien{ 25438494Sobrien if (cp->callout) 25538494Sobrien untimeout(cp->callout); 25638494Sobrien XFREE(cp->key); 25738494Sobrien XFREE(cp->xivec); 25838494Sobrien XFREE(cp->info); 25938494Sobrien XFREE(cp->auto_opts); 26038494Sobrien XFREE(cp->def_opts); 26138494Sobrien free_opts(&cp->fs_opts); 26238494Sobrien XFREE(cp); 26338494Sobrien} 26438494Sobrien 26538494Sobrien 26638494Sobrien/* 26738494Sobrien * Discard the underlying mount point and replace 26838494Sobrien * with a reference to an error filesystem. 26938494Sobrien */ 27038494Sobrienvoid 27138494Sobrienassign_error_mntfs(am_node *mp) 27238494Sobrien{ 27338494Sobrien if (mp->am_error > 0) { 27438494Sobrien /* 27538494Sobrien * Save the old error code 27638494Sobrien */ 27738494Sobrien int error = mp->am_error; 27838494Sobrien if (error <= 0) 27938494Sobrien error = mp->am_mnt->mf_error; 28038494Sobrien /* 28138494Sobrien * Discard the old filesystem 28238494Sobrien */ 28338494Sobrien free_mntfs(mp->am_mnt); 28438494Sobrien /* 28538494Sobrien * Allocate a new error reference 28638494Sobrien */ 28738494Sobrien mp->am_mnt = new_mntfs(); 28838494Sobrien /* 28938494Sobrien * Put back the error code 29038494Sobrien */ 29138494Sobrien mp->am_mnt->mf_error = error; 29238494Sobrien mp->am_mnt->mf_flags |= MFF_ERROR; 29338494Sobrien /* 29438494Sobrien * Zero the error in the mount point 29538494Sobrien */ 29638494Sobrien mp->am_error = 0; 29738494Sobrien } 29838494Sobrien} 29938494Sobrien 30038494Sobrien 30138494Sobrien/* 30238494Sobrien * The continuation function. This is called by 30338494Sobrien * the task notifier when a background mount attempt 30438494Sobrien * completes. 30538494Sobrien */ 30638494Sobrienvoid 30738494Sobrienamfs_auto_cont(int rc, int term, voidp closure) 30838494Sobrien{ 30938494Sobrien struct continuation *cp = (struct continuation *) closure; 31038494Sobrien mntfs *mf = cp->mp->am_mnt; 31138494Sobrien 31238494Sobrien /* 31338494Sobrien * Definitely not trying to mount at the moment 31438494Sobrien */ 31538494Sobrien mf->mf_flags &= ~MFF_MOUNTING; 31638494Sobrien 31738494Sobrien /* 31838494Sobrien * While we are mounting - try to avoid race conditions 31938494Sobrien */ 32038494Sobrien new_ttl(cp->mp); 32138494Sobrien 32238494Sobrien /* 32338494Sobrien * Wakeup anything waiting for this mount 32438494Sobrien */ 32538494Sobrien wakeup((voidp) mf); 32638494Sobrien 32738494Sobrien /* 32838494Sobrien * Check for termination signal or exit status... 32938494Sobrien */ 33038494Sobrien if (rc || term) { 33138494Sobrien am_node *xmp; 33238494Sobrien 33338494Sobrien if (term) { 33438494Sobrien /* 33538494Sobrien * Not sure what to do for an error code. 33638494Sobrien */ 33738494Sobrien mf->mf_error = EIO; /* XXX ? */ 33838494Sobrien mf->mf_flags |= MFF_ERROR; 33938494Sobrien plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term); 34038494Sobrien } else { 34138494Sobrien /* 34238494Sobrien * Check for exit status... 34338494Sobrien */ 34438494Sobrien mf->mf_error = rc; 34538494Sobrien mf->mf_flags |= MFF_ERROR; 34638494Sobrien errno = rc; /* XXX */ 34738494Sobrien if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx")) 34838494Sobrien plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path); 34938494Sobrien } 35038494Sobrien 35138494Sobrien /* 35238494Sobrien * If we get here then that attempt didn't work, so 35338494Sobrien * move the info vector pointer along by one and 35438494Sobrien * call the background mount routine again 35538494Sobrien */ 35638494Sobrien amd_stats.d_merr++; 35738494Sobrien cp->ivec++; 35838494Sobrien xmp = cp->mp; 35938494Sobrien (void) amfs_auto_bgmount(cp, 0); 36038494Sobrien assign_error_mntfs(xmp); 36138494Sobrien } else { 36238494Sobrien /* 36338494Sobrien * The mount worked. 36438494Sobrien */ 36538494Sobrien am_mounted(cp->mp); 36638494Sobrien free_continuation(cp); 36738494Sobrien } 36838494Sobrien 36938494Sobrien reschedule_timeout_mp(); 37038494Sobrien} 37138494Sobrien 37238494Sobrien 37338494Sobrien/* 37438494Sobrien * Retry a mount 37538494Sobrien */ 37638494Sobrienvoid 37738494Sobrienamfs_auto_retry(int rc, int term, voidp closure) 37838494Sobrien{ 37938494Sobrien struct continuation *cp = (struct continuation *) closure; 38038494Sobrien int error = 0; 38138494Sobrien 38238494Sobrien#ifdef DEBUG 38338494Sobrien dlog("Commencing retry for mount of %s", cp->mp->am_path); 38438494Sobrien#endif /* DEBUG */ 38538494Sobrien 38638494Sobrien new_ttl(cp->mp); 38738494Sobrien 38838494Sobrien if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) { 38938494Sobrien /* 39038494Sobrien * The entire mount has timed out. Set the error code and skip past all 39138494Sobrien * the info vectors so that amfs_auto_bgmount will not have any more 39238494Sobrien * ways to try the mount, so causing an error. 39338494Sobrien */ 39438494Sobrien plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path); 39538494Sobrien error = ETIMEDOUT; 39638494Sobrien while (*cp->ivec) 39738494Sobrien cp->ivec++; 39838494Sobrien /* explicitly forbid further retries after timeout */ 39938494Sobrien cp->retry = FALSE; 40038494Sobrien } 40138494Sobrien if (error || !IN_PROGRESS(cp)) { 40238494Sobrien (void) amfs_auto_bgmount(cp, error); 40338494Sobrien } 40438494Sobrien reschedule_timeout_mp(); 40538494Sobrien} 40638494Sobrien 40738494Sobrien 40838494Sobrien/* 40938494Sobrien * Try to mount a file system. Can be called 41038494Sobrien * directly or in a sub-process by run_task. 41138494Sobrien */ 41238494Sobrienint 41338494Sobrientry_mount(voidp mvp) 41438494Sobrien{ 41538494Sobrien int error = 0; 41638494Sobrien am_node *mp = (am_node *) mvp; 41738494Sobrien mntfs *mf = mp->am_mnt; 41838494Sobrien 41938494Sobrien /* 42038494Sobrien * If the directory is not yet made and it needs to be made, then make it! 42138494Sobrien * This may be run in a background process in which case the flag setting 42238494Sobrien * won't be noticed later - but it is set anyway just after run_task is 42338494Sobrien * called. It should probably go away totally... 42438494Sobrien */ 42538494Sobrien if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) { 42638494Sobrien error = mkdirs(mf->mf_mount, 0555); 42738494Sobrien if (!error) 42838494Sobrien mf->mf_flags |= MFF_MKMNT; 42938494Sobrien } 43038494Sobrien 43138494Sobrien /* 43238494Sobrien * Mount it! 43338494Sobrien */ 43438494Sobrien error = mount_node(mp); 43538494Sobrien 43638494Sobrien#ifdef DEBUG 43738494Sobrien if (error > 0) { 43838494Sobrien errno = error; 43938494Sobrien dlog("amfs_auto call to mount_node failed: %m"); 44038494Sobrien } 44138494Sobrien#endif /* DEBUG */ 44238494Sobrien 44338494Sobrien return error; 44438494Sobrien} 44538494Sobrien 44638494Sobrien 44738494Sobrien/* 44838494Sobrien * Pick a file system to try mounting and 44938494Sobrien * do that in the background if necessary 45038494Sobrien * 45138494Sobrien For each location: 45238494Sobrien if it is new -defaults then 45338494Sobrien extract and process 45438494Sobrien continue; 45538494Sobrien fi 45638494Sobrien if it is a cut then 45738494Sobrien if a location has been tried then 45838494Sobrien break; 45938494Sobrien fi 46038494Sobrien continue; 46138494Sobrien fi 46238494Sobrien parse mount location 46338494Sobrien discard previous mount location if required 46438494Sobrien find matching mounted filesystem 46538494Sobrien if not applicable then 46638494Sobrien this_error = No such file or directory 46738494Sobrien continue 46838494Sobrien fi 46938494Sobrien if the filesystem failed to be mounted then 47038494Sobrien this_error = error from filesystem 47138494Sobrien elif the filesystem is mounting or unmounting then 47238494Sobrien this_error = -1 47338494Sobrien elif the fileserver is down then 47438494Sobrien this_error = -1 47538494Sobrien elif the filesystem is already mounted 47638494Sobrien this_error = 0 47738494Sobrien break 47838494Sobrien fi 47938494Sobrien if no error on this mount then 48042629Sobrien this_error = initialize mount point 48138494Sobrien fi 48238494Sobrien if no error on this mount and mount is delayed then 48338494Sobrien this_error = -1 48438494Sobrien fi 48538494Sobrien if this_error < 0 then 48638494Sobrien retry = true 48738494Sobrien fi 48838494Sobrien if no error on this mount then 48938494Sobrien make mount point if required 49038494Sobrien fi 49138494Sobrien if no error on this mount then 49238494Sobrien if mount in background then 49338494Sobrien run mount in background 49438494Sobrien return -1 49538494Sobrien else 49638494Sobrien this_error = mount in foreground 49738494Sobrien fi 49838494Sobrien fi 49942629Sobrien if an error occurred on this mount then 50038494Sobrien update stats 50138494Sobrien save error in mount point 50238494Sobrien fi 50338494Sobrien endfor 50438494Sobrien */ 50538494Sobrienstatic int 50638494Sobrienamfs_auto_bgmount(struct continuation * cp, int mpe) 50738494Sobrien{ 50838494Sobrien mntfs *mf = cp->mp->am_mnt; /* Current mntfs */ 50938494Sobrien mntfs *mf_retry = 0; /* First mntfs which needed retrying */ 51038494Sobrien int this_error = -1; /* Per-mount error */ 51138494Sobrien int hard_error = -1; 51238494Sobrien int mp_error = mpe; 51338494Sobrien 51438494Sobrien /* 51538494Sobrien * Try to mount each location. 51638494Sobrien * At the end: 51738494Sobrien * hard_error == 0 indicates something was mounted. 51838494Sobrien * hard_error > 0 indicates everything failed with a hard error 51938494Sobrien * hard_error < 0 indicates nothing could be mounted now 52038494Sobrien */ 52138494Sobrien for (; this_error && *cp->ivec; cp->ivec++) { 52238494Sobrien am_ops *p; 52338494Sobrien am_node *mp = cp->mp; 52438494Sobrien char *link_dir; 52538494Sobrien int dont_retry; 52638494Sobrien 52738494Sobrien if (hard_error < 0) 52838494Sobrien hard_error = this_error; 52938494Sobrien 53038494Sobrien this_error = -1; 53138494Sobrien 53238494Sobrien if (**cp->ivec == '-') { 53338494Sobrien /* 53438494Sobrien * Pick up new defaults 53538494Sobrien */ 53638494Sobrien if (cp->auto_opts && *cp->auto_opts) 53738494Sobrien cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1); 53838494Sobrien else 53938494Sobrien cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1); 54038494Sobrien#ifdef DEBUG 54138494Sobrien dlog("Setting def_opts to \"%s\"", cp->def_opts); 54238494Sobrien#endif /* DEBUG */ 54338494Sobrien continue; 54438494Sobrien } 54538494Sobrien /* 54638494Sobrien * If a mount has been attempted, and we find 54738494Sobrien * a cut then don't try any more locations. 54838494Sobrien */ 54938494Sobrien if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) { 55038494Sobrien if (cp->tried) { 55138494Sobrien#ifdef DEBUG 55238494Sobrien dlog("Cut: not trying any more locations for %s", 55338494Sobrien mp->am_path); 55438494Sobrien#endif /* DEBUG */ 55538494Sobrien break; 55638494Sobrien } 55738494Sobrien continue; 55838494Sobrien } 55938494Sobrien 56038494Sobrien /* match the operators */ 56138494Sobrien p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info); 56238494Sobrien 56338494Sobrien /* 56438494Sobrien * Find a mounted filesystem for this node. 56538494Sobrien */ 56638494Sobrien mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, 56738494Sobrien cp->fs_opts.opt_fs, 56838494Sobrien cp->fs_opts.fs_mtab, 56938494Sobrien cp->auto_opts, 57038494Sobrien cp->fs_opts.opt_opts, 57138494Sobrien cp->fs_opts.opt_remopts); 57238494Sobrien 57338494Sobrien p = mf->mf_ops; 57438494Sobrien#ifdef DEBUG 57538494Sobrien dlog("Got a hit with %s", p->fs_type); 57638494Sobrien#endif /* DEBUG */ 57738494Sobrien 57838494Sobrien /* 57938494Sobrien * Note whether this is a real mount attempt 58038494Sobrien */ 58138494Sobrien if (p == &amfs_error_ops) { 58238494Sobrien plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path); 58338494Sobrien if (this_error <= 0) 58438494Sobrien this_error = ENOENT; 58538494Sobrien continue; 58638494Sobrien } else { 58738494Sobrien if (cp->fs_opts.fs_mtab) { 58838494Sobrien plog(XLOG_MAP, "Trying mount of %s on %s fstype %s", 58938494Sobrien cp->fs_opts.fs_mtab, mp->am_path, p->fs_type); 59038494Sobrien } 59138494Sobrien cp->tried = TRUE; 59238494Sobrien } 59338494Sobrien 59438494Sobrien this_error = 0; 59538494Sobrien dont_retry = FALSE; 59638494Sobrien 59738494Sobrien if (mp->am_link) { 59838494Sobrien XFREE(mp->am_link); 59938494Sobrien mp->am_link = 0; 60038494Sobrien } 60138494Sobrien link_dir = mf->mf_fo->opt_sublink; 60238494Sobrien 60338494Sobrien if (link_dir && *link_dir) { 60438494Sobrien if (*link_dir == '/') { 60538494Sobrien mp->am_link = strdup(link_dir); 60638494Sobrien } else { 60738494Sobrien /* 60838494Sobrien * try getting fs option from continuation, not mountpoint! 60938494Sobrien * Don't try logging the string from mf, since it may be bad! 61038494Sobrien */ 61138494Sobrien if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs) 61238494Sobrien plog(XLOG_ERROR, "use %s instead of 0x%x", 61338494Sobrien cp->fs_opts.opt_fs, mf->mf_fo->opt_fs); 61438494Sobrien 61538494Sobrien mp->am_link = str3cat((char *) 0, 61638494Sobrien cp->fs_opts.opt_fs, "/", link_dir); 61738494Sobrien 61838494Sobrien normalize_slash(mp->am_link); 61938494Sobrien } 62038494Sobrien } 62138494Sobrien 62238494Sobrien if (mf->mf_error > 0) { 62338494Sobrien this_error = mf->mf_error; 62438494Sobrien } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { 62538494Sobrien /* 62638494Sobrien * Still mounting - retry later 62738494Sobrien */ 62838494Sobrien#ifdef DEBUG 62938494Sobrien dlog("Duplicate pending mount fstype %s", p->fs_type); 63038494Sobrien#endif /* DEBUG */ 63138494Sobrien this_error = -1; 63238494Sobrien } else if (FSRV_ISDOWN(mf->mf_server)) { 63338494Sobrien /* 63438494Sobrien * Would just mount from the same place 63538494Sobrien * as a hung mount - so give up 63638494Sobrien */ 63738494Sobrien#ifdef DEBUG 63838494Sobrien dlog("%s is already hung - giving up", mf->mf_mount); 63938494Sobrien#endif /* DEBUG */ 64038494Sobrien mp_error = EWOULDBLOCK; 64138494Sobrien dont_retry = TRUE; 64238494Sobrien this_error = -1; 64338494Sobrien } else if (mf->mf_flags & MFF_MOUNTED) { 64438494Sobrien#ifdef DEBUG 64538494Sobrien dlog("duplicate mount of \"%s\" ...", mf->mf_info); 64638494Sobrien#endif /* DEBUG */ 64738494Sobrien 64838494Sobrien /* 64938494Sobrien * Just call mounted() 65038494Sobrien */ 65138494Sobrien am_mounted(mp); 65238494Sobrien 65338494Sobrien this_error = 0; 65438494Sobrien break; 65538494Sobrien } 65638494Sobrien 65738494Sobrien /* 65838494Sobrien * Will usually need to play around with the mount nodes 65938494Sobrien * file attribute structure. This must be done here. 66042629Sobrien * Try and get things initialized, even if the fileserver 66138494Sobrien * is not known to be up. In the common case this will 66238494Sobrien * progress things faster. 66338494Sobrien */ 66438494Sobrien if (!this_error) { 66538494Sobrien /* 66638494Sobrien * Fill in attribute fields. 66738494Sobrien */ 66838494Sobrien if (mf->mf_ops->fs_flags & FS_DIRECTORY) 66938494Sobrien mk_fattr(mp, NFDIR); 67038494Sobrien else 67138494Sobrien mk_fattr(mp, NFLNK); 67238494Sobrien 67338494Sobrien mp->am_fattr.na_fileid = mp->am_gen; 67438494Sobrien 67538494Sobrien if (p->fs_init) 67638494Sobrien this_error = (*p->fs_init) (mf); 67738494Sobrien } 67838494Sobrien 67938494Sobrien /* 68038494Sobrien * Make sure the fileserver is UP before doing any more work 68138494Sobrien */ 68238494Sobrien if (!FSRV_ISUP(mf->mf_server)) { 68338494Sobrien#ifdef DEBUG 68438494Sobrien dlog("waiting for server %s to become available", mf->mf_server->fs_host); 68538494Sobrien#endif /* DEBUG */ 68638494Sobrien this_error = -1; 68738494Sobrien } 68838494Sobrien 68938494Sobrien if (!this_error && mf->mf_fo->opt_delay) { 69038494Sobrien /* 69138494Sobrien * If there is a delay timer on the mount 69238494Sobrien * then don't try to mount if the timer 69338494Sobrien * has not expired. 69438494Sobrien */ 69538494Sobrien int i = atoi(mf->mf_fo->opt_delay); 69638494Sobrien if (i > 0 && clocktime() < (cp->start + i)) { 69738494Sobrien#ifdef DEBUG 69838494Sobrien dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start); 69938494Sobrien#endif /* DEBUG */ 70038494Sobrien this_error = -1; 70138494Sobrien } 70238494Sobrien } 70338494Sobrien 70438494Sobrien if (this_error < 0 && !dont_retry) { 70538494Sobrien if (!mf_retry) 70638494Sobrien mf_retry = dup_mntfs(mf); 70738494Sobrien cp->retry = TRUE; 70838494Sobrien } 70938494Sobrien 71038494Sobrien if (!this_error) { 71138494Sobrien if (p->fs_flags & FS_MBACKGROUND) { 71238494Sobrien mf->mf_flags |= MFF_MOUNTING; /* XXX */ 71338494Sobrien#ifdef DEBUG 71438494Sobrien dlog("backgrounding mount of \"%s\"", mf->mf_mount); 71538494Sobrien#endif /* DEBUG */ 71638494Sobrien if (cp->callout) { 71738494Sobrien untimeout(cp->callout); 71838494Sobrien cp->callout = 0; 71938494Sobrien } 72038494Sobrien run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp); 72138494Sobrien mf->mf_flags |= MFF_MKMNT; /* XXX */ 72238494Sobrien if (mf_retry) 72338494Sobrien free_mntfs(mf_retry); 72438494Sobrien return -1; 72538494Sobrien } else { 72638494Sobrien#ifdef DEBUG 72738494Sobrien dlog("foreground mount of \"%s\" ...", mf->mf_info); 72838494Sobrien#endif /* DEBUG */ 72938494Sobrien this_error = try_mount((voidp) mp); 73038494Sobrien if (this_error < 0) { 73138494Sobrien if (!mf_retry) 73238494Sobrien mf_retry = dup_mntfs(mf); 73338494Sobrien cp->retry = TRUE; 73438494Sobrien } 73538494Sobrien } 73638494Sobrien } 73738494Sobrien 73838494Sobrien if (this_error >= 0) { 73938494Sobrien if (this_error > 0) { 74038494Sobrien amd_stats.d_merr++; 74138494Sobrien if (mf != mf_retry) { 74238494Sobrien mf->mf_error = this_error; 74338494Sobrien mf->mf_flags |= MFF_ERROR; 74438494Sobrien } 74538494Sobrien } 74638494Sobrien 74738494Sobrien /* 74838494Sobrien * Wakeup anything waiting for this mount 74938494Sobrien */ 75038494Sobrien wakeup((voidp) mf); 75138494Sobrien } 75238494Sobrien } 75338494Sobrien 75438494Sobrien if (this_error && cp->retry) { 75538494Sobrien free_mntfs(mf); 75638494Sobrien mf = cp->mp->am_mnt = mf_retry; 75738494Sobrien /* 75838494Sobrien * Not retrying again (so far) 75938494Sobrien */ 76038494Sobrien cp->retry = FALSE; 76138494Sobrien cp->tried = FALSE; 76238494Sobrien /* 76338494Sobrien * Start at the beginning. 76438494Sobrien * Rewind the location vector and 76538494Sobrien * reset the default options. 76638494Sobrien */ 76738494Sobrien cp->ivec = cp->xivec; 76838494Sobrien cp->def_opts = strealloc(cp->def_opts, cp->auto_opts); 76938494Sobrien /* 77038494Sobrien * Arrange that amfs_auto_bgmount is called 77138494Sobrien * after anything else happens. 77238494Sobrien */ 77338494Sobrien#ifdef DEBUG 77438494Sobrien dlog("Arranging to retry mount of %s", cp->mp->am_path); 77538494Sobrien#endif /* DEBUG */ 77638494Sobrien sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf); 77738494Sobrien if (cp->callout) 77838494Sobrien untimeout(cp->callout); 77938494Sobrien cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf); 78038494Sobrien 78138494Sobrien cp->mp->am_ttl = clocktime() + RETRY_INTERVAL; 78238494Sobrien 78338494Sobrien /* 78438494Sobrien * Not done yet - so don't return anything 78538494Sobrien */ 78638494Sobrien return -1; 78738494Sobrien } 78838494Sobrien 78938494Sobrien if (hard_error < 0 || this_error == 0) 79038494Sobrien hard_error = this_error; 79138494Sobrien 79238494Sobrien /* 79338494Sobrien * Discard handle on duff filesystem. 79438494Sobrien * This should never happen since it 79538494Sobrien * should be caught by the case above. 79638494Sobrien */ 79738494Sobrien if (mf_retry) { 79838494Sobrien if (hard_error) 79938494Sobrien plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount); 80038494Sobrien free_mntfs(mf_retry); 80138494Sobrien } 80238494Sobrien 80338494Sobrien /* 80438494Sobrien * If we get here, then either the mount succeeded or 80538494Sobrien * there is no more mount information available. 80638494Sobrien */ 80738494Sobrien if (hard_error < 0 && mp_error) 80838494Sobrien hard_error = cp->mp->am_error = mp_error; 80938494Sobrien if (hard_error > 0) { 81038494Sobrien /* 81138494Sobrien * Set a small(ish) timeout on an error node if 81238494Sobrien * the error was not a time out. 81338494Sobrien */ 81438494Sobrien switch (hard_error) { 81538494Sobrien case ETIMEDOUT: 81638494Sobrien case EWOULDBLOCK: 81738494Sobrien cp->mp->am_timeo = 17; 81838494Sobrien break; 81938494Sobrien default: 82038494Sobrien cp->mp->am_timeo = 5; 82138494Sobrien break; 82238494Sobrien } 82338494Sobrien new_ttl(cp->mp); 82438494Sobrien } 82538494Sobrien 82638494Sobrien /* 82738494Sobrien * Make sure that the error value in the mntfs has a 82838494Sobrien * reasonable value. 82938494Sobrien */ 83038494Sobrien if (mf->mf_error < 0) { 83138494Sobrien mf->mf_error = hard_error; 83238494Sobrien if (hard_error) 83338494Sobrien mf->mf_flags |= MFF_ERROR; 83438494Sobrien } 83538494Sobrien 83638494Sobrien /* 83738494Sobrien * In any case we don't need the continuation any more 83838494Sobrien */ 83938494Sobrien free_continuation(cp); 84038494Sobrien 84138494Sobrien return hard_error; 84238494Sobrien} 84338494Sobrien 84438494Sobrien 84538494Sobrien/* 84638494Sobrien * Automount interface to RPC lookup routine 84738494Sobrien * Find the corresponding entry and return 84838494Sobrien * the file handle for it. 84938494Sobrien */ 85038494Sobrienam_node * 85138494Sobrienamfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op) 85238494Sobrien{ 85338494Sobrien am_node *ap, *new_mp, *ap_hung; 85438494Sobrien char *info; /* Mount info - where to get the file system */ 85538494Sobrien char **ivec, **xivec; /* Split version of info */ 85638494Sobrien char *auto_opts; /* Automount options */ 85738494Sobrien int error = 0; /* Error so far */ 85838494Sobrien char path_name[MAXPATHLEN]; /* General path name buffer */ 85938494Sobrien char *pfname; /* Path for database lookup */ 86038494Sobrien struct continuation *cp; /* Continuation structure if need to mount */ 86138494Sobrien int in_progress = 0; /* # of (un)mount in progress */ 86238494Sobrien char *dflts; 86338494Sobrien mntfs *mf; 86438494Sobrien 86538494Sobrien#ifdef DEBUG 86638494Sobrien dlog("in amfs_auto_lookuppn"); 86738494Sobrien#endif /* DEBUG */ 86838494Sobrien 86938494Sobrien /* 87038494Sobrien * If the server is shutting down 87138494Sobrien * then don't return information 87238494Sobrien * about the mount point. 87338494Sobrien */ 87438494Sobrien if (amd_state == Finishing) { 87538494Sobrien#ifdef DEBUG 87638494Sobrien if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) { 87738494Sobrien dlog("%s mount ignored - going down", fname); 87838494Sobrien } else { 87938494Sobrien dlog("%s/%s mount ignored - going down", mp->am_path, fname); 88038494Sobrien } 88138494Sobrien#endif /* DEBUG */ 88238494Sobrien ereturn(ENOENT); 88338494Sobrien } 88438494Sobrien 88538494Sobrien /* 88638494Sobrien * Handle special case of "." and ".." 88738494Sobrien */ 88838494Sobrien if (fname[0] == '.') { 88938494Sobrien if (fname[1] == '\0') 89038494Sobrien return mp; /* "." is the current node */ 89138494Sobrien if (fname[1] == '.' && fname[2] == '\0') { 89238494Sobrien if (mp->am_parent) { 89338494Sobrien#ifdef DEBUG 89438494Sobrien dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); 89538494Sobrien#endif /* DEBUG */ 89638494Sobrien return mp->am_parent; /* ".." is the parent node */ 89738494Sobrien } 89838494Sobrien ereturn(ESTALE); 89938494Sobrien } 90038494Sobrien } 90138494Sobrien 90238494Sobrien /* 90338494Sobrien * Check for valid key name. 90438494Sobrien * If it is invalid then pretend it doesn't exist. 90538494Sobrien */ 90638494Sobrien if (!valid_key(fname)) { 90738494Sobrien plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); 90838494Sobrien ereturn(ENOENT); 90938494Sobrien } 91038494Sobrien 91138494Sobrien /* 91238494Sobrien * Expand key name. 91338494Sobrien * fname is now a private copy. 91438494Sobrien */ 91538494Sobrien fname = expand_key(fname); 91638494Sobrien 91738494Sobrien for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) { 91838494Sobrien /* 91938494Sobrien * Otherwise search children of this node 92038494Sobrien */ 92138494Sobrien if (FSTREQ(ap->am_name, fname)) { 92238494Sobrien mf = ap->am_mnt; 92338494Sobrien if (ap->am_error) { 92438494Sobrien error = ap->am_error; 92538494Sobrien continue; 92638494Sobrien } 92738494Sobrien /* 92838494Sobrien * If the error code is undefined then it must be 92938494Sobrien * in progress. 93038494Sobrien */ 93138494Sobrien if (mf->mf_error < 0) 93238494Sobrien goto in_progrss; 93338494Sobrien 93438494Sobrien /* 93538494Sobrien * Check for a hung node 93638494Sobrien */ 93738494Sobrien if (FSRV_ISDOWN(mf->mf_server)) { 93838494Sobrien#ifdef DEBUG 93938494Sobrien dlog("server hung"); 94038494Sobrien#endif /* DEBUG */ 94138494Sobrien error = ap->am_error; 94238494Sobrien ap_hung = ap; 94338494Sobrien continue; 94438494Sobrien } 94538494Sobrien /* 94638494Sobrien * If there was a previous error with this node 94738494Sobrien * then return that error code. 94838494Sobrien */ 94938494Sobrien if (mf->mf_flags & MFF_ERROR) { 95038494Sobrien error = mf->mf_error; 95138494Sobrien continue; 95238494Sobrien } 95338494Sobrien if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { 95438494Sobrien in_progrss: 95538494Sobrien /* 95638494Sobrien * If the fs is not mounted or it is unmounting then there 95738494Sobrien * is a background (un)mount in progress. In this case 95838494Sobrien * we just drop the RPC request (return nil) and 95938494Sobrien * wait for a retry, by which time the (un)mount may 96038494Sobrien * have completed. 96138494Sobrien */ 96238494Sobrien#ifdef DEBUG 96338494Sobrien dlog("ignoring mount of %s in %s -- flags (%x) in progress", 96438494Sobrien fname, mf->mf_mount, mf->mf_flags); 96538494Sobrien#endif /* DEBUG */ 96638494Sobrien in_progress++; 96738494Sobrien continue; 96838494Sobrien } 96938494Sobrien 97038494Sobrien /* 97138494Sobrien * Otherwise we have a hit: return the current mount point. 97238494Sobrien */ 97338494Sobrien#ifdef DEBUG 97438494Sobrien dlog("matched %s in %s", fname, ap->am_path); 97538494Sobrien#endif /* DEBUG */ 97638494Sobrien XFREE(fname); 97738494Sobrien return ap; 97838494Sobrien } 97938494Sobrien } 98038494Sobrien 98138494Sobrien if (in_progress) { 98238494Sobrien#ifdef DEBUG 98338494Sobrien dlog("Waiting while %d mount(s) in progress", in_progress); 98438494Sobrien#endif /* DEBUG */ 98538494Sobrien XFREE(fname); 98638494Sobrien ereturn(-1); 98738494Sobrien } 98838494Sobrien 98938494Sobrien /* 99042629Sobrien * If an error occurred then return it. 99138494Sobrien */ 99238494Sobrien if (error) { 99338494Sobrien#ifdef DEBUG 99438494Sobrien errno = error; /* XXX */ 99538494Sobrien dlog("Returning error: %m", error); 99638494Sobrien#endif /* DEBUG */ 99738494Sobrien XFREE(fname); 99838494Sobrien ereturn(error); 99938494Sobrien } 100038494Sobrien 100138494Sobrien /* 100238494Sobrien * If doing a delete then don't create again! 100338494Sobrien */ 100438494Sobrien switch (op) { 100538494Sobrien case VLOOK_DELETE: 100638494Sobrien ereturn(ENOENT); 100738494Sobrien 100838494Sobrien case VLOOK_CREATE: 100938494Sobrien break; 101038494Sobrien 101138494Sobrien default: 101238494Sobrien plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op); 101338494Sobrien ereturn(EINVAL); 101438494Sobrien } 101538494Sobrien 101638494Sobrien /* 101738494Sobrien * If the server is going down then just return, 101838494Sobrien * don't try to mount any more file systems 101938494Sobrien */ 102038494Sobrien if ((int) amd_state >= (int) Finishing) { 102138494Sobrien#ifdef DEBUG 102238494Sobrien dlog("not found - server going down anyway"); 102338494Sobrien#endif /* DEBUG */ 102438494Sobrien XFREE(fname); 102538494Sobrien ereturn(ENOENT); 102638494Sobrien } 102738494Sobrien 102838494Sobrien /* 102938494Sobrien * If we get there then this is a reference to an, 103038494Sobrien * as yet, unknown name so we need to search the mount 103138494Sobrien * map for it. 103238494Sobrien */ 103338494Sobrien if (mp->am_pref) { 103438494Sobrien sprintf(path_name, "%s%s", mp->am_pref, fname); 103538494Sobrien pfname = path_name; 103638494Sobrien } else { 103738494Sobrien pfname = fname; 103838494Sobrien } 103938494Sobrien 104038494Sobrien mf = mp->am_mnt; 104138494Sobrien 104238494Sobrien#ifdef DEBUG 104338494Sobrien dlog("will search map info in %s to find %s", mf->mf_info, pfname); 104438494Sobrien#endif /* DEBUG */ 104538494Sobrien /* 104638494Sobrien * Consult the oracle for some mount information. 104738494Sobrien * info is malloc'ed and belongs to this routine. 104838494Sobrien * It ends up being free'd in free_continuation(). 104938494Sobrien * 105038494Sobrien * Note that this may return -1 indicating that information 105138494Sobrien * is not yet available. 105238494Sobrien */ 105338494Sobrien error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); 105438494Sobrien if (error) { 105538494Sobrien if (error > 0) 105638494Sobrien plog(XLOG_MAP, "No map entry for %s", pfname); 105738494Sobrien else 105838494Sobrien plog(XLOG_MAP, "Waiting on map entry for %s", pfname); 105938494Sobrien XFREE(fname); 106038494Sobrien ereturn(error); 106138494Sobrien } 106238494Sobrien#ifdef DEBUG 106338494Sobrien dlog("mount info is %s", info); 106438494Sobrien#endif /* DEBUG */ 106538494Sobrien 106638494Sobrien /* 106738494Sobrien * Split info into an argument vector. 106838494Sobrien * The vector is malloc'ed and belongs to 106938494Sobrien * this routine. It is free'd in free_continuation() 107038494Sobrien */ 107138494Sobrien xivec = ivec = strsplit(info, ' ', '\"'); 107238494Sobrien 107338494Sobrien /* 107438494Sobrien * Default error code... 107538494Sobrien */ 107638494Sobrien if (ap_hung) 107738494Sobrien error = EWOULDBLOCK; 107838494Sobrien else 107938494Sobrien error = ENOENT; 108038494Sobrien 108138494Sobrien /* 108238494Sobrien * Allocate a new map 108338494Sobrien */ 108438494Sobrien new_mp = exported_ap_alloc(); 108538494Sobrien if (new_mp == 0) { 108638494Sobrien XFREE(xivec); 108738494Sobrien XFREE(info); 108838494Sobrien XFREE(fname); 108938494Sobrien ereturn(ENOSPC); 109038494Sobrien } 109138494Sobrien if (mf->mf_auto) 109238494Sobrien auto_opts = mf->mf_auto; 109338494Sobrien else 109438494Sobrien auto_opts = ""; 109538494Sobrien 109638494Sobrien auto_opts = strdup(auto_opts); 109738494Sobrien 109838494Sobrien#ifdef DEBUG 109938494Sobrien dlog("searching for /defaults entry"); 110038494Sobrien#endif /* DEBUG */ 110138494Sobrien if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) { 110238494Sobrien char *dfl; 110338494Sobrien char **rvec; 110438494Sobrien#ifdef DEBUG 110538494Sobrien dlog("/defaults gave %s", dflts); 110638494Sobrien#endif /* DEBUG */ 110738494Sobrien if (*dflts == '-') 110838494Sobrien dfl = dflts + 1; 110938494Sobrien else 111038494Sobrien dfl = dflts; 111138494Sobrien 111238494Sobrien /* 111338494Sobrien * Chop the defaults up 111438494Sobrien */ 111538494Sobrien rvec = strsplit(dfl, ' ', '\"'); 111638494Sobrien 111738494Sobrien if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) { 111838494Sobrien /* 111938494Sobrien * Pick whichever first entry matched the list of selectors. 112038494Sobrien * Strip the selectors from the string, and assign to dfl the 112138494Sobrien * rest of the string. 112238494Sobrien */ 112338494Sobrien if (rvec) { 112438494Sobrien am_opts ap; 112538494Sobrien am_ops *pt; 112638494Sobrien char **sp = rvec; 112738494Sobrien while (*sp) { /* loop until you find something, if any */ 112838494Sobrien memset((char *) &ap, 0, sizeof(am_opts)); 112938494Sobrien pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", 113038494Sobrien mp->am_parent->am_mnt->mf_info); 113141142Sobrien free_opts(&ap); /* don't leak */ 113238494Sobrien if (pt == &amfs_error_ops) { 113338494Sobrien plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp); 113438494Sobrien } else { 113538494Sobrien dfl = strip_selectors(*sp, "/defaults"); 113638494Sobrien plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); 113738494Sobrien break; 113838494Sobrien } 113938494Sobrien ++sp; 114038494Sobrien } 114138494Sobrien } 114238494Sobrien } else { /* not enable_default_selectors */ 114338494Sobrien /* 114438494Sobrien * Extract first value 114538494Sobrien */ 114638494Sobrien dfl = rvec[0]; 114738494Sobrien } 114838494Sobrien 114938494Sobrien /* 115038494Sobrien * If there were any values at all... 115138494Sobrien */ 115238494Sobrien if (dfl) { 115338494Sobrien /* 115438494Sobrien * Log error if there were other values 115538494Sobrien */ 115638494Sobrien if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) { 115738494Sobrien# ifdef DEBUG 115838494Sobrien dlog("/defaults chopped into %s", dfl); 115938494Sobrien# endif /* DEBUG */ 116038494Sobrien plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); 116138494Sobrien } 116238494Sobrien 116338494Sobrien /* 116438494Sobrien * Prepend to existing defaults if they exist, 116538494Sobrien * otherwise just use these defaults. 116638494Sobrien */ 116738494Sobrien if (*auto_opts && *dfl) { 116838494Sobrien char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2); 116938494Sobrien sprintf(nopts, "%s;%s", dfl, auto_opts); 117038494Sobrien XFREE(auto_opts); 117138494Sobrien auto_opts = nopts; 117238494Sobrien } else if (*dfl) { 117338494Sobrien auto_opts = strealloc(auto_opts, dfl); 117438494Sobrien } 117538494Sobrien } 117638494Sobrien XFREE(dflts); 117738494Sobrien /* 117838494Sobrien * Don't need info vector any more 117938494Sobrien */ 118038494Sobrien XFREE(rvec); 118138494Sobrien } 118238494Sobrien 118338494Sobrien /* 118438494Sobrien * Fill it in 118538494Sobrien */ 118638494Sobrien init_map(new_mp, fname); 118738494Sobrien 118838494Sobrien /* 118938494Sobrien * Put it in the table 119038494Sobrien */ 119138494Sobrien insert_am(new_mp, mp); 119238494Sobrien 119338494Sobrien /* 119438494Sobrien * Fill in some other fields, 119538494Sobrien * path and mount point. 119638494Sobrien * 119738494Sobrien * bugfix: do not prepend old am_path if direct map 119838494Sobrien * <wls@astro.umd.edu> William Sebok 119938494Sobrien */ 120038494Sobrien new_mp->am_path = str3cat(new_mp->am_path, 120138494Sobrien mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path, 120238494Sobrien *fname == '/' ? "" : "/", fname); 120338494Sobrien 120438494Sobrien#ifdef DEBUG 120538494Sobrien dlog("setting path to %s", new_mp->am_path); 120638494Sobrien#endif /* DEBUG */ 120738494Sobrien 120838494Sobrien /* 120938494Sobrien * Take private copy of pfname 121038494Sobrien */ 121138494Sobrien pfname = strdup(pfname); 121238494Sobrien 121338494Sobrien /* 121438494Sobrien * Construct a continuation 121538494Sobrien */ 121638494Sobrien cp = ALLOC(struct continuation); 121738494Sobrien cp->callout = 0; 121838494Sobrien cp->mp = new_mp; 121938494Sobrien cp->xivec = xivec; 122038494Sobrien cp->ivec = ivec; 122138494Sobrien cp->info = info; 122238494Sobrien cp->key = pfname; 122338494Sobrien cp->auto_opts = auto_opts; 122438494Sobrien cp->retry = FALSE; 122538494Sobrien cp->tried = FALSE; 122638494Sobrien cp->start = clocktime(); 122738494Sobrien cp->def_opts = strdup(auto_opts); 122838494Sobrien memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts)); 122938494Sobrien 123038494Sobrien /* 123138494Sobrien * Try and mount the file system. If this succeeds immediately (possible 123238494Sobrien * for a ufs file system) then return the attributes, otherwise just 123338494Sobrien * return an error. 123438494Sobrien */ 123538494Sobrien error = amfs_auto_bgmount(cp, error); 123638494Sobrien reschedule_timeout_mp(); 123738494Sobrien if (!error) { 123838494Sobrien XFREE(fname); 123938494Sobrien return new_mp; 124038494Sobrien } 124138494Sobrien 124238494Sobrien /* 124338494Sobrien * Code for quick reply. If nfs_program_2_transp is set, then 124438494Sobrien * its the transp that's been passed down from nfs_program_2(). 124538494Sobrien * If new_mp->am_transp is not already set, set it by copying in 124638494Sobrien * nfs_program_2_transp. Once am_transp is set, quick_reply() can 124738494Sobrien * use it to send a reply to the client that requested this mount. 124838494Sobrien */ 124938494Sobrien if (nfs_program_2_transp && !new_mp->am_transp) { 125038494Sobrien new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); 125138494Sobrien *(new_mp->am_transp) = *nfs_program_2_transp; 125238494Sobrien } 125338494Sobrien if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) 125438494Sobrien new_mp->am_error = error; 125538494Sobrien 125638494Sobrien assign_error_mntfs(new_mp); 125738494Sobrien 125838494Sobrien XFREE(fname); 125938494Sobrien 126038494Sobrien ereturn(error); 126138494Sobrien} 126238494Sobrien 126338494Sobrien 126438494Sobrien/* 126538494Sobrien * Locate next node in sibling list which is mounted 126638494Sobrien * and is not an error node. 126738494Sobrien */ 126838494Sobrienam_node * 126938494Sobriennext_nonerror_node(am_node *xp) 127038494Sobrien{ 127138494Sobrien mntfs *mf; 127238494Sobrien 127338494Sobrien /* 127438494Sobrien * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 127538494Sobrien * Fixes a race condition when mounting direct automounts. 127638494Sobrien * Also fixes a problem when doing a readdir on a directory 127738494Sobrien * containing hung automounts. 127838494Sobrien */ 127938494Sobrien while (xp && 128038494Sobrien (!(mf = xp->am_mnt) || /* No mounted filesystem */ 128138494Sobrien mf->mf_error != 0 || /* There was a mntfs error */ 128238494Sobrien xp->am_error != 0 || /* There was a mount error */ 128338494Sobrien !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 128438494Sobrien (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 128538494Sobrien ) 128638494Sobrien xp = xp->am_osib; 128738494Sobrien 128838494Sobrien return xp; 128938494Sobrien} 129038494Sobrien 129138494Sobrien 129238494Sobrien/* 129338494Sobrien * This readdir function which call a special version of it that allows 129438494Sobrien * browsing if browsable_dirs=yes was set on the map. 129538494Sobrien */ 129638494Sobrienint 129738494Sobrienamfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count) 129838494Sobrien{ 129938494Sobrien u_int gen = *(u_int *) cookie; 130038494Sobrien am_node *xp; 130138494Sobrien mntent_t mnt; 130238494Sobrien 130338494Sobrien dp->dl_eof = FALSE; /* assume readdir not done */ 130438494Sobrien 130538494Sobrien /* check if map is browsable */ 130638494Sobrien if (mp->am_mnt && mp->am_mnt->mf_mopts) { 130738494Sobrien mnt.mnt_opts = mp->am_mnt->mf_mopts; 130838494Sobrien if (hasmntopt(&mnt, "fullybrowsable")) 130938494Sobrien return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE); 131038494Sobrien if (hasmntopt(&mnt, "browsable")) 131138494Sobrien return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE); 131238494Sobrien } 131338494Sobrien 131438494Sobrien if (gen == 0) { 131538494Sobrien /* 131638494Sobrien * In the default instance (which is used to start a search) we return 131738494Sobrien * "." and "..". 131838494Sobrien * 131938494Sobrien * This assumes that the count is big enough to allow both "." and ".." 132038494Sobrien * to be returned in a single packet. If it isn't (which would be 132138494Sobrien * fairly unbelievable) then tough. 132238494Sobrien */ 132338494Sobrien#ifdef DEBUG 132438494Sobrien dlog("default search"); 132538494Sobrien#endif /* DEBUG */ 132638494Sobrien /* 132738494Sobrien * Check for enough room. This is extremely approximate but is more 132838494Sobrien * than enough space. Really need 2 times: 132938494Sobrien * 4byte fileid 133038494Sobrien * 4byte cookie 133138494Sobrien * 4byte name length 133238494Sobrien * 4byte name 133338494Sobrien * plus the dirlist structure */ 133438494Sobrien if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 133538494Sobrien return EINVAL; 133638494Sobrien 133738494Sobrien xp = next_nonerror_node(mp->am_child); 133838494Sobrien dp->dl_entries = ep; 133938494Sobrien 134038494Sobrien /* construct "." */ 134138494Sobrien ep[0].ne_fileid = mp->am_gen; 134238494Sobrien ep[0].ne_name = "."; 134338494Sobrien ep[0].ne_nextentry = &ep[1]; 134438494Sobrien *(u_int *) ep[0].ne_cookie = 0; 134538494Sobrien 134638494Sobrien /* construct ".." */ 134738494Sobrien if (mp->am_parent) 134838494Sobrien ep[1].ne_fileid = mp->am_parent->am_gen; 134938494Sobrien else 135038494Sobrien ep[1].ne_fileid = mp->am_gen; 135138494Sobrien ep[1].ne_name = ".."; 135238494Sobrien ep[1].ne_nextentry = 0; 135338494Sobrien *(u_int *) ep[1].ne_cookie = 135438494Sobrien xp ? xp->am_gen : ~(u_int) 0; 135538494Sobrien 135638494Sobrien if (!xp) 135738494Sobrien dp->dl_eof = TRUE; /* by default assume readdir done */ 135838494Sobrien 135938494Sobrien return 0; 136038494Sobrien } 136138494Sobrien#ifdef DEBUG 136238494Sobrien dlog("real child"); 136338494Sobrien#endif /* DEBUG */ 136438494Sobrien 136538494Sobrien if (gen == ~(u_int) 0) { 136638494Sobrien#ifdef DEBUG 136738494Sobrien dlog("End of readdir in %s", mp->am_path); 136838494Sobrien#endif /* DEBUG */ 136938494Sobrien dp->dl_eof = TRUE; 137038494Sobrien dp->dl_entries = 0; 137138494Sobrien return 0; 137238494Sobrien } 137338494Sobrien 137438494Sobrien /* non-browsable directories code */ 137538494Sobrien xp = mp->am_child; 137638494Sobrien while (xp && xp->am_gen != gen) 137738494Sobrien xp = xp->am_osib; 137838494Sobrien 137938494Sobrien if (xp) { 138038494Sobrien int nbytes = count / 2; /* conservative */ 138138494Sobrien int todo = MAX_READDIR_ENTRIES; 138238494Sobrien dp->dl_entries = ep; 138338494Sobrien do { 138438494Sobrien am_node *xp_next = next_nonerror_node(xp->am_osib); 138538494Sobrien 138638494Sobrien if (xp_next) { 138738494Sobrien *(u_int *) ep->ne_cookie = xp_next->am_gen; 138838494Sobrien } else { 138938494Sobrien *(u_int *) ep->ne_cookie = ~(u_int) 0; 139038494Sobrien dp->dl_eof = TRUE; 139138494Sobrien } 139238494Sobrien 139338494Sobrien ep->ne_fileid = xp->am_gen; 139438494Sobrien ep->ne_name = xp->am_name; 139538494Sobrien nbytes -= sizeof(*ep) + 1; 139638494Sobrien if (xp->am_name) 139738494Sobrien nbytes -= strlen(xp->am_name); 139838494Sobrien 139938494Sobrien xp = xp_next; 140038494Sobrien 140138494Sobrien if (nbytes > 0 && !dp->dl_eof && todo > 1) { 140238494Sobrien ep->ne_nextentry = ep + 1; 140338494Sobrien ep++; 140438494Sobrien --todo; 140538494Sobrien } else { 140638494Sobrien todo = 0; 140738494Sobrien } 140838494Sobrien } while (todo > 0); 140938494Sobrien 141038494Sobrien ep->ne_nextentry = 0; 141138494Sobrien 141238494Sobrien return 0; 141338494Sobrien } 141438494Sobrien return ESTALE; 141538494Sobrien} 141638494Sobrien 141738494Sobrien 141838494Sobrien/* This one is called only if map is browsable */ 141938494Sobrienstatic int 142038494Sobrienamfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable) 142138494Sobrien{ 142238494Sobrien u_int gen = *(u_int *) cookie; 142338494Sobrien int chain_length, i; 142438494Sobrien static nfsentry *te, *te_next; 142538494Sobrien#ifdef DEBUG_READDIR 142638494Sobrien nfsentry *ne; 142738494Sobrien static int j; 142838494Sobrien#endif /* DEBUG_READDIR */ 142938494Sobrien 143038494Sobrien dp->dl_eof = FALSE; /* assume readdir not done */ 143138494Sobrien 143238494Sobrien#ifdef DEBUG_READDIR 143338494Sobrien plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d", 143438494Sobrien gen, count); 143538494Sobrien#endif /* DEBUG_READDIR */ 143638494Sobrien 143738494Sobrien if (gen == 0) { 143838494Sobrien /* 143938494Sobrien * In the default instance (which is used to start a search) we return 144038494Sobrien * "." and "..". 144138494Sobrien * 144238494Sobrien * This assumes that the count is big enough to allow both "." and ".." 144338494Sobrien * to be returned in a single packet. If it isn't (which would be 144438494Sobrien * fairly unbelievable) then tough. 144538494Sobrien */ 144638494Sobrien#ifdef DEBUG 144738494Sobrien dlog("default search"); 144838494Sobrien#endif /* DEBUG */ 144938494Sobrien /* 145038494Sobrien * Check for enough room. This is extremely approximate but is more 145138494Sobrien * than enough space. Really need 2 times: 145238494Sobrien * 4byte fileid 145338494Sobrien * 4byte cookie 145438494Sobrien * 4byte name length 145538494Sobrien * 4byte name 145638494Sobrien * plus the dirlist structure */ 145738494Sobrien if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 145838494Sobrien return EINVAL; 145938494Sobrien 146038494Sobrien /* 146138494Sobrien * compute # of entries to send in this chain. 146238494Sobrien * heuristics: 128 bytes per entry. 146338494Sobrien * This is too much probably, but it seems to work better because 146438494Sobrien * of the re-entrant nature of nfs_readdir, and esp. on systems 146538494Sobrien * like OpenBSD 2.2. 146638494Sobrien */ 146738494Sobrien chain_length = count / 128; 146838494Sobrien 146938494Sobrien /* reset static state counters */ 147038494Sobrien te = te_next = NULL; 147138494Sobrien 147238494Sobrien dp->dl_entries = ep; 147338494Sobrien 147438494Sobrien /* construct "." */ 147538494Sobrien ep[0].ne_fileid = mp->am_gen; 147638494Sobrien ep[0].ne_name = "."; 147738494Sobrien ep[0].ne_nextentry = &ep[1]; 147838494Sobrien *(u_int *) ep[0].ne_cookie = 0; 147938494Sobrien 148038494Sobrien /* construct ".." */ 148138494Sobrien if (mp->am_parent) 148238494Sobrien ep[1].ne_fileid = mp->am_parent->am_gen; 148338494Sobrien else 148438494Sobrien ep[1].ne_fileid = mp->am_gen; 148538494Sobrien ep[1].ne_name = ".."; 148638494Sobrien ep[1].ne_nextentry = 0; 148738494Sobrien *(u_int *) ep[1].ne_cookie = ~(u_int) 0; 148838494Sobrien 148938494Sobrien /* 149038494Sobrien * If map is browsable, call a function make_entry_chain() to construct 149138494Sobrien * a linked list of unmounted keys, and return it. Then link the chain 149238494Sobrien * to the regular list. Get the chain only once, but return 149338494Sobrien * chunks of it each time. 149438494Sobrien */ 149538494Sobrien te = make_entry_chain(mp, dp->dl_entries, fully_browsable); 149638494Sobrien if (!te) 149738494Sobrien return 0; 149838494Sobrien#ifdef DEBUG_READDIR 149938494Sobrien j = 0; 150038494Sobrien for (ne=te; ne; ne=ne->ne_nextentry) 150138494Sobrien plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name); 150238494Sobrien#endif /* DEBUG_READDIR */ 150338494Sobrien 150438494Sobrien /* return only "chain_length" entries */ 150538494Sobrien te_next = te; 150638494Sobrien for (i=1; i<chain_length; ++i) { 150738494Sobrien te_next = te_next->ne_nextentry; 150838494Sobrien if (!te_next) 150938494Sobrien break; 151038494Sobrien } 151138494Sobrien if (te_next) { 151238494Sobrien nfsentry *te_saved = te_next->ne_nextentry; 151338494Sobrien te_next->ne_nextentry = NULL; /* terminate "te" chain */ 151442629Sobrien te_next = te_saved; /* save rest of "te" for next iteration */ 151538494Sobrien dp->dl_eof = FALSE; /* tell readdir there's more */ 151638494Sobrien } else { 151738494Sobrien dp->dl_eof = TRUE; /* tell readdir that's it */ 151838494Sobrien } 151938494Sobrien ep[1].ne_nextentry = te; /* append this chunk of "te" chain */ 152038494Sobrien#ifdef DEBUG_READDIR 152138494Sobrien for (ne=te; ne; ne=ne->ne_nextentry) 152238494Sobrien plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name); 152338494Sobrien#endif /* DEBUG_READDIR */ 152438494Sobrien return 0; 152538494Sobrien } /* end of "if (gen == 0)" statement */ 152638494Sobrien 152738494Sobrien#ifdef DEBUG 152838494Sobrien dlog("real child"); 152938494Sobrien#endif /* DEBUG */ 153038494Sobrien 153138494Sobrien if (gen == ~(u_int) 0) { 153238494Sobrien#ifdef DEBUG 153338494Sobrien dlog("End of readdir in %s", mp->am_path); 153438494Sobrien#endif /* DEBUG */ 153538494Sobrien dp->dl_eof = TRUE; 153638494Sobrien dp->dl_entries = 0; 153738494Sobrien return 0; 153838494Sobrien } 153938494Sobrien 154038494Sobrien /* 154138494Sobrien * If browsable directories, then continue serving readdir() with another 154238494Sobrien * chunk of entries, starting from where we left off (when gen was equal 154338494Sobrien * to 0). Once again, assume last chunk served to readdir. 154438494Sobrien */ 154538494Sobrien dp->dl_eof = TRUE; 154638494Sobrien dp->dl_entries = ep; 154738494Sobrien 154838494Sobrien te = te_next; /* reset 'te' from last saved te_next */ 154938494Sobrien if (!te) { /* another indicator of end of readdir */ 155038494Sobrien dp->dl_entries = 0; 155138494Sobrien return 0; 155238494Sobrien } 155338494Sobrien /* 155438494Sobrien * compute # of entries to send in this chain. 155538494Sobrien * heuristics: 128 bytes per entry. 155638494Sobrien */ 155738494Sobrien chain_length = count / 128; 155838494Sobrien 155938494Sobrien /* return only "chain_length" entries */ 156038494Sobrien for (i=1; i<chain_length; ++i) { 156138494Sobrien te_next = te_next->ne_nextentry; 156238494Sobrien if (!te_next) 156338494Sobrien break; 156438494Sobrien } 156538494Sobrien if (te_next) { 156638494Sobrien nfsentry *te_saved = te_next->ne_nextentry; 156738494Sobrien te_next->ne_nextentry = NULL; /* terminate "te" chain */ 156842629Sobrien te_next = te_saved; /* save rest of "te" for next iteration */ 156938494Sobrien dp->dl_eof = FALSE; /* tell readdir there's more */ 157038494Sobrien } 157138494Sobrien ep = te; /* send next chunk of "te" chain */ 157238494Sobrien dp->dl_entries = ep; 157338494Sobrien#ifdef DEBUG_READDIR 157438494Sobrien plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d", 157538494Sobrien dp->dl_entries, te_next, dp->dl_eof); 157638494Sobrien for (ne=te; ne; ne=ne->ne_nextentry) 157738494Sobrien plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name); 157838494Sobrien#endif /* DEBUG_READDIR */ 157938494Sobrien return 0; 158038494Sobrien} 158138494Sobrien 158238494Sobrien 158338494Sobrienint 158438494Sobrienamfs_auto_fmount(am_node *mp) 158538494Sobrien{ 158638494Sobrien mntfs *mf = mp->am_mnt; 158738494Sobrien return (*mf->mf_ops->fmount_fs) (mf); 158838494Sobrien} 158938494Sobrien 159038494Sobrien 159138494Sobrienint 159238494Sobrienamfs_auto_fumount(am_node *mp) 159338494Sobrien{ 159438494Sobrien mntfs *mf = mp->am_mnt; 159538494Sobrien return (*mf->mf_ops->fumount_fs) (mf); 159638494Sobrien} 1597