amfs_auto.c revision 119679
138494Sobrien/* 2119679Smbr * Copyright (c) 1997-2003 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 * 41119679Smbr * $Id: amfs_auto.c,v 1.9.2.11 2003/07/18 04:50:18 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 6082794Sobrien#define DOT_DOT_COOKIE (u_int) 1 6138494Sobrien 6238494Sobrien/**************************************************************************** 6338494Sobrien *** STRUCTURES *** 6438494Sobrien ****************************************************************************/ 6538494Sobrien 6638494Sobrien 6738494Sobrien/**************************************************************************** 6838494Sobrien *** FORWARD DEFINITIONS *** 6938494Sobrien ****************************************************************************/ 7082794Sobrienstatic int amfs_auto_bgmount(struct continuation *cp, int mpe); 7138494Sobrienstatic int amfs_auto_mount(am_node *mp); 7238494Sobrienstatic int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable); 7338494Sobrienstatic void amfs_auto_umounted(am_node *mp); 7438494Sobrien 7538494Sobrien 7638494Sobrien/**************************************************************************** 7738494Sobrien *** OPS STRUCTURES *** 7838494Sobrien ****************************************************************************/ 7938494Sobrienam_ops amfs_auto_ops = 8038494Sobrien{ 8138494Sobrien "auto", 8238494Sobrien amfs_auto_match, 8338494Sobrien 0, /* amfs_auto_init */ 8438494Sobrien amfs_auto_mount, 8538494Sobrien 0, 8638494Sobrien amfs_auto_umount, 8738494Sobrien 0, 8838494Sobrien amfs_auto_lookuppn, 8938494Sobrien amfs_auto_readdir, 9038494Sobrien 0, /* amfs_auto_readlink */ 9138494Sobrien 0, /* amfs_auto_mounted */ 9238494Sobrien amfs_auto_umounted, 9338494Sobrien find_amfs_auto_srvr, 9438494Sobrien FS_AMQINFO | FS_DIRECTORY 9538494Sobrien}; 9638494Sobrien 9738494Sobrien 9838494Sobrien/**************************************************************************** 9938494Sobrien *** FUNCTIONS *** 10038494Sobrien ****************************************************************************/ 10138494Sobrien/* 10238494Sobrien * AMFS_AUTO needs nothing in particular. 10338494Sobrien */ 10438494Sobrienchar * 10538494Sobrienamfs_auto_match(am_opts *fo) 10638494Sobrien{ 10738494Sobrien char *p = fo->opt_rfs; 10838494Sobrien 10938494Sobrien if (!fo->opt_rfs) { 11038494Sobrien plog(XLOG_USER, "auto: no mount point named (rfs:=)"); 11138494Sobrien return 0; 11238494Sobrien } 11338494Sobrien if (!fo->opt_fs) { 11438494Sobrien plog(XLOG_USER, "auto: no map named (fs:=)"); 11538494Sobrien return 0; 11638494Sobrien } 11738494Sobrien 11838494Sobrien /* 11938494Sobrien * Swap round fs:= and rfs:= options 12038494Sobrien * ... historical (jsp) 12138494Sobrien */ 12238494Sobrien fo->opt_rfs = fo->opt_fs; 12338494Sobrien fo->opt_fs = p; 12438494Sobrien 12538494Sobrien /* 12638494Sobrien * mtab entry turns out to be the name of the mount map 12738494Sobrien */ 12838494Sobrien return strdup(fo->opt_rfs ? fo->opt_rfs : "."); 12938494Sobrien} 13038494Sobrien 13138494Sobrien 13238494Sobrien 13338494Sobrien 13438494Sobrien/* 13538494Sobrien * Build a new map cache for this node, or re-use 13638494Sobrien * an existing cache for the same map. 13738494Sobrien */ 13838494Sobrienvoid 13938494Sobrienamfs_auto_mkcacheref(mntfs *mf) 14038494Sobrien{ 14138494Sobrien char *cache; 14238494Sobrien 14338494Sobrien if (mf->mf_fo && mf->mf_fo->opt_cache) 14438494Sobrien cache = mf->mf_fo->opt_cache; 14538494Sobrien else 14638494Sobrien cache = "none"; 14738494Sobrien mf->mf_private = (voidp) mapc_find(mf->mf_info, cache, 14838494Sobrien mf->mf_fo->opt_maptype); 14938494Sobrien mf->mf_prfree = mapc_free; 15038494Sobrien} 15138494Sobrien 15238494Sobrien 15338494Sobrien/* 15438494Sobrien * Mount a sub-mount 15538494Sobrien */ 15638494Sobrienstatic int 15738494Sobrienamfs_auto_mount(am_node *mp) 15838494Sobrien{ 15938494Sobrien mntfs *mf = mp->am_mnt; 16038494Sobrien 16138494Sobrien /* 16238494Sobrien * Pseudo-directories are used to provide some structure 16338494Sobrien * to the automounted directories instead 16438494Sobrien * of putting them all in the top-level automount directory. 16538494Sobrien * 16638494Sobrien * Here, just increment the parent's link count. 16738494Sobrien */ 16838494Sobrien mp->am_parent->am_fattr.na_nlink++; 16938494Sobrien 17038494Sobrien /* 17138494Sobrien * Info field of . means use parent's info field. 17238494Sobrien * Historical - not documented. 17338494Sobrien */ 17438494Sobrien if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0') 17538494Sobrien mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info); 17638494Sobrien 17738494Sobrien /* 17838494Sobrien * Compute prefix: 17938494Sobrien * 18038494Sobrien * If there is an option prefix then use that else 18138494Sobrien * If the parent had a prefix then use that with name 18238494Sobrien * of this node appended else 18338494Sobrien * Use the name of this node. 18438494Sobrien * 18538494Sobrien * That means if you want no prefix you must say so 18638494Sobrien * in the map. 18738494Sobrien */ 18838494Sobrien if (mf->mf_fo->opt_pref) { 18938494Sobrien /* allow pref:=null to set a real null prefix */ 19038494Sobrien if (STREQ(mf->mf_fo->opt_pref, "null")) { 191119679Smbr mp->am_pref = strdup(""); 19238494Sobrien } else { 19338494Sobrien /* 19438494Sobrien * the prefix specified as an option 19538494Sobrien */ 19638494Sobrien mp->am_pref = strdup(mf->mf_fo->opt_pref); 19738494Sobrien } 19838494Sobrien } else { 19938494Sobrien /* 20038494Sobrien * else the parent's prefix 20138494Sobrien * followed by the name 20238494Sobrien * followed by / 20338494Sobrien */ 20438494Sobrien char *ppref = mp->am_parent->am_pref; 20538494Sobrien if (ppref == 0) 20638494Sobrien ppref = ""; 20738494Sobrien mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/"); 20838494Sobrien } 20938494Sobrien 21038494Sobrien /* 21138494Sobrien * Attach a map cache 21238494Sobrien */ 21338494Sobrien amfs_auto_mkcacheref(mf); 21438494Sobrien 21538494Sobrien return 0; 21638494Sobrien} 21738494Sobrien 21838494Sobrien 21938494Sobrien 22038494Sobrien 22138494Sobrien/* 22238494Sobrien * Unmount an automount sub-node 22338494Sobrien */ 22438494Sobrienint 22538494Sobrienamfs_auto_umount(am_node *mp) 22638494Sobrien{ 22738494Sobrien return 0; 22838494Sobrien} 22938494Sobrien 23038494Sobrien 23138494Sobrien/* 23238494Sobrien * Unmount an automount node 23338494Sobrien */ 23438494Sobrienstatic void 23538494Sobrienamfs_auto_umounted(am_node *mp) 23638494Sobrien{ 23738494Sobrien /* 23838494Sobrien * If this is a pseudo-directory then just adjust the link count 23938494Sobrien * in the parent, otherwise call the generic unmount routine 24038494Sobrien */ 24138494Sobrien if (mp->am_parent && mp->am_parent->am_parent) 24238494Sobrien --mp->am_parent->am_fattr.na_nlink; 24338494Sobrien} 24438494Sobrien 24538494Sobrien 24638494Sobrien/* 24738494Sobrien * Discard an old continuation 24838494Sobrien */ 24938494Sobrienvoid 25038494Sobrienfree_continuation(struct continuation *cp) 25138494Sobrien{ 25238494Sobrien if (cp->callout) 25338494Sobrien untimeout(cp->callout); 25438494Sobrien XFREE(cp->key); 25538494Sobrien XFREE(cp->xivec); 25638494Sobrien XFREE(cp->info); 25738494Sobrien XFREE(cp->auto_opts); 25838494Sobrien XFREE(cp->def_opts); 25938494Sobrien free_opts(&cp->fs_opts); 26038494Sobrien XFREE(cp); 26138494Sobrien} 26238494Sobrien 26338494Sobrien 26438494Sobrien/* 26538494Sobrien * Discard the underlying mount point and replace 26638494Sobrien * with a reference to an error filesystem. 26738494Sobrien */ 26838494Sobrienvoid 26938494Sobrienassign_error_mntfs(am_node *mp) 27038494Sobrien{ 27138494Sobrien if (mp->am_error > 0) { 27238494Sobrien /* 27338494Sobrien * Save the old error code 27438494Sobrien */ 27538494Sobrien int error = mp->am_error; 27638494Sobrien if (error <= 0) 27738494Sobrien error = mp->am_mnt->mf_error; 27838494Sobrien /* 27938494Sobrien * Discard the old filesystem 28038494Sobrien */ 28138494Sobrien free_mntfs(mp->am_mnt); 28238494Sobrien /* 28338494Sobrien * Allocate a new error reference 28438494Sobrien */ 28538494Sobrien mp->am_mnt = new_mntfs(); 28638494Sobrien /* 28738494Sobrien * Put back the error code 28838494Sobrien */ 28938494Sobrien mp->am_mnt->mf_error = error; 29038494Sobrien mp->am_mnt->mf_flags |= MFF_ERROR; 29138494Sobrien /* 29238494Sobrien * Zero the error in the mount point 29338494Sobrien */ 29438494Sobrien mp->am_error = 0; 29538494Sobrien } 29638494Sobrien} 29738494Sobrien 29838494Sobrien 29938494Sobrien/* 30038494Sobrien * The continuation function. This is called by 30138494Sobrien * the task notifier when a background mount attempt 30238494Sobrien * completes. 30338494Sobrien */ 30438494Sobrienvoid 30538494Sobrienamfs_auto_cont(int rc, int term, voidp closure) 30638494Sobrien{ 30738494Sobrien struct continuation *cp = (struct continuation *) closure; 30838494Sobrien mntfs *mf = cp->mp->am_mnt; 30938494Sobrien 31038494Sobrien /* 31138494Sobrien * Definitely not trying to mount at the moment 31238494Sobrien */ 31338494Sobrien mf->mf_flags &= ~MFF_MOUNTING; 31438494Sobrien 31538494Sobrien /* 31638494Sobrien * While we are mounting - try to avoid race conditions 31738494Sobrien */ 31838494Sobrien new_ttl(cp->mp); 31938494Sobrien 32038494Sobrien /* 32138494Sobrien * Wakeup anything waiting for this mount 32238494Sobrien */ 32338494Sobrien wakeup((voidp) mf); 32438494Sobrien 32538494Sobrien /* 32638494Sobrien * Check for termination signal or exit status... 32738494Sobrien */ 32838494Sobrien if (rc || term) { 32938494Sobrien am_node *xmp; 33038494Sobrien 33138494Sobrien if (term) { 33238494Sobrien /* 33338494Sobrien * Not sure what to do for an error code. 33438494Sobrien */ 33538494Sobrien mf->mf_error = EIO; /* XXX ? */ 33638494Sobrien mf->mf_flags |= MFF_ERROR; 33738494Sobrien plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term); 33838494Sobrien } else { 33938494Sobrien /* 34038494Sobrien * Check for exit status... 34138494Sobrien */ 34238494Sobrien mf->mf_error = rc; 34338494Sobrien mf->mf_flags |= MFF_ERROR; 34438494Sobrien errno = rc; /* XXX */ 34538494Sobrien if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx")) 34638494Sobrien plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path); 34738494Sobrien } 34838494Sobrien 34938494Sobrien /* 35038494Sobrien * If we get here then that attempt didn't work, so 35138494Sobrien * move the info vector pointer along by one and 35238494Sobrien * call the background mount routine again 35338494Sobrien */ 35438494Sobrien amd_stats.d_merr++; 35538494Sobrien cp->ivec++; 35638494Sobrien xmp = cp->mp; 35738494Sobrien (void) amfs_auto_bgmount(cp, 0); 35838494Sobrien assign_error_mntfs(xmp); 35938494Sobrien } else { 36038494Sobrien /* 36138494Sobrien * The mount worked. 36238494Sobrien */ 36338494Sobrien am_mounted(cp->mp); 36438494Sobrien free_continuation(cp); 36538494Sobrien } 36638494Sobrien 36738494Sobrien reschedule_timeout_mp(); 36838494Sobrien} 36938494Sobrien 37038494Sobrien 37138494Sobrien/* 37238494Sobrien * Retry a mount 37338494Sobrien */ 37438494Sobrienvoid 37538494Sobrienamfs_auto_retry(int rc, int term, voidp closure) 37638494Sobrien{ 37738494Sobrien struct continuation *cp = (struct continuation *) closure; 37838494Sobrien int error = 0; 37938494Sobrien 38038494Sobrien#ifdef DEBUG 38138494Sobrien dlog("Commencing retry for mount of %s", cp->mp->am_path); 38238494Sobrien#endif /* DEBUG */ 38338494Sobrien 38438494Sobrien new_ttl(cp->mp); 38538494Sobrien 38638494Sobrien if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) { 38738494Sobrien /* 38838494Sobrien * The entire mount has timed out. Set the error code and skip past all 38938494Sobrien * the info vectors so that amfs_auto_bgmount will not have any more 39038494Sobrien * ways to try the mount, so causing an error. 39138494Sobrien */ 39238494Sobrien plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path); 39338494Sobrien error = ETIMEDOUT; 39438494Sobrien while (*cp->ivec) 39538494Sobrien cp->ivec++; 39638494Sobrien /* explicitly forbid further retries after timeout */ 39738494Sobrien cp->retry = FALSE; 39838494Sobrien } 39938494Sobrien if (error || !IN_PROGRESS(cp)) { 40038494Sobrien (void) amfs_auto_bgmount(cp, error); 40138494Sobrien } 40238494Sobrien reschedule_timeout_mp(); 40338494Sobrien} 40438494Sobrien 40538494Sobrien 40638494Sobrien/* 40738494Sobrien * Try to mount a file system. Can be called 40838494Sobrien * directly or in a sub-process by run_task. 40938494Sobrien */ 41038494Sobrienint 41138494Sobrientry_mount(voidp mvp) 41238494Sobrien{ 41338494Sobrien int error = 0; 41438494Sobrien am_node *mp = (am_node *) mvp; 41538494Sobrien mntfs *mf = mp->am_mnt; 41638494Sobrien 41738494Sobrien /* 41838494Sobrien * If the directory is not yet made and it needs to be made, then make it! 41938494Sobrien * This may be run in a background process in which case the flag setting 42038494Sobrien * won't be noticed later - but it is set anyway just after run_task is 42138494Sobrien * called. It should probably go away totally... 42238494Sobrien */ 42338494Sobrien if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) { 42438494Sobrien error = mkdirs(mf->mf_mount, 0555); 42538494Sobrien if (!error) 42638494Sobrien mf->mf_flags |= MFF_MKMNT; 42738494Sobrien } 42838494Sobrien 42938494Sobrien /* 43038494Sobrien * Mount it! 43138494Sobrien */ 43238494Sobrien error = mount_node(mp); 43338494Sobrien 43438494Sobrien#ifdef DEBUG 43538494Sobrien if (error > 0) { 43638494Sobrien errno = error; 437119679Smbr dlog("amfs_auto: call to mount_node(%s) failed: %m", mp->am_path); 43838494Sobrien } 43938494Sobrien#endif /* DEBUG */ 44038494Sobrien 44138494Sobrien return error; 44238494Sobrien} 44338494Sobrien 44438494Sobrien 44538494Sobrien/* 44638494Sobrien * Pick a file system to try mounting and 44738494Sobrien * do that in the background if necessary 44838494Sobrien * 44952894SobrienFor each location: 45052894Sobrien if it is new -defaults then 45152894Sobrien extract and process 45252894Sobrien continue; 45352894Sobrien fi 45452894Sobrien if it is a cut then 45552894Sobrien if a location has been tried then 45652894Sobrien break; 45752894Sobrien fi 45852894Sobrien continue; 45952894Sobrien fi 46052894Sobrien parse mount location 46152894Sobrien discard previous mount location if required 46252894Sobrien find matching mounted filesystem 46352894Sobrien if not applicable then 46452894Sobrien this_error = No such file or directory 46552894Sobrien continue 46652894Sobrien fi 46752894Sobrien if the filesystem failed to be mounted then 46852894Sobrien this_error = error from filesystem 46952894Sobrien elif the filesystem is mounting or unmounting then 47052894Sobrien this_error = -1 47152894Sobrien elif the fileserver is down then 47252894Sobrien this_error = -1 47352894Sobrien elif the filesystem is already mounted 47452894Sobrien this_error = 0 47552894Sobrien break 47652894Sobrien fi 47752894Sobrien if no error on this mount then 47852894Sobrien this_error = initialize mount point 47952894Sobrien fi 48052894Sobrien if no error on this mount and mount is delayed then 48152894Sobrien this_error = -1 48252894Sobrien fi 48352894Sobrien if this_error < 0 then 48452894Sobrien retry = true 48552894Sobrien fi 48652894Sobrien if no error on this mount then 48752894Sobrien make mount point if required 48852894Sobrien fi 48952894Sobrien if no error on this mount then 49052894Sobrien if mount in background then 49152894Sobrien run mount in background 49252894Sobrien return -1 49352894Sobrien else 49452894Sobrien this_error = mount in foreground 49552894Sobrien fi 49652894Sobrien fi 49752894Sobrien if an error occurred on this mount then 49852894Sobrien update stats 49952894Sobrien save error in mount point 50052894Sobrien fi 50152894Sobrienendfor 50238494Sobrien */ 50338494Sobrienstatic int 50482794Sobrienamfs_auto_bgmount(struct continuation *cp, int mpe) 50538494Sobrien{ 50638494Sobrien mntfs *mf = cp->mp->am_mnt; /* Current mntfs */ 50738494Sobrien mntfs *mf_retry = 0; /* First mntfs which needed retrying */ 50838494Sobrien int this_error = -1; /* Per-mount error */ 50938494Sobrien int hard_error = -1; 51038494Sobrien int mp_error = mpe; 51138494Sobrien 51238494Sobrien /* 51338494Sobrien * Try to mount each location. 51438494Sobrien * At the end: 51538494Sobrien * hard_error == 0 indicates something was mounted. 51638494Sobrien * hard_error > 0 indicates everything failed with a hard error 51738494Sobrien * hard_error < 0 indicates nothing could be mounted now 51838494Sobrien */ 51938494Sobrien for (; this_error && *cp->ivec; cp->ivec++) { 52038494Sobrien am_ops *p; 52138494Sobrien am_node *mp = cp->mp; 52238494Sobrien char *link_dir; 52338494Sobrien int dont_retry; 52438494Sobrien 52538494Sobrien if (hard_error < 0) 52638494Sobrien hard_error = this_error; 52738494Sobrien 52838494Sobrien this_error = -1; 52938494Sobrien 53038494Sobrien if (**cp->ivec == '-') { 53138494Sobrien /* 53238494Sobrien * Pick up new defaults 53338494Sobrien */ 53438494Sobrien if (cp->auto_opts && *cp->auto_opts) 53538494Sobrien cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1); 53638494Sobrien else 53738494Sobrien cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1); 53838494Sobrien#ifdef DEBUG 53938494Sobrien dlog("Setting def_opts to \"%s\"", cp->def_opts); 54038494Sobrien#endif /* DEBUG */ 54138494Sobrien continue; 54238494Sobrien } 54338494Sobrien /* 54438494Sobrien * If a mount has been attempted, and we find 54538494Sobrien * a cut then don't try any more locations. 54638494Sobrien */ 54738494Sobrien if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) { 54838494Sobrien if (cp->tried) { 54938494Sobrien#ifdef DEBUG 55038494Sobrien dlog("Cut: not trying any more locations for %s", 55138494Sobrien mp->am_path); 55238494Sobrien#endif /* DEBUG */ 55338494Sobrien break; 55438494Sobrien } 55538494Sobrien continue; 55638494Sobrien } 55738494Sobrien 55838494Sobrien /* match the operators */ 55938494Sobrien p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info); 56038494Sobrien 56138494Sobrien /* 56238494Sobrien * Find a mounted filesystem for this node. 56338494Sobrien */ 56438494Sobrien mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, 56538494Sobrien cp->fs_opts.opt_fs, 56638494Sobrien cp->fs_opts.fs_mtab, 56738494Sobrien cp->auto_opts, 56838494Sobrien cp->fs_opts.opt_opts, 56938494Sobrien cp->fs_opts.opt_remopts); 57038494Sobrien 57138494Sobrien p = mf->mf_ops; 57238494Sobrien#ifdef DEBUG 57338494Sobrien dlog("Got a hit with %s", p->fs_type); 57438494Sobrien#endif /* DEBUG */ 57538494Sobrien 57638494Sobrien /* 57738494Sobrien * Note whether this is a real mount attempt 57838494Sobrien */ 57938494Sobrien if (p == &amfs_error_ops) { 58082794Sobrien plog(XLOG_MAP, "Map entry %s for %s did not match", *cp->ivec, mp->am_path); 58138494Sobrien if (this_error <= 0) 58238494Sobrien this_error = ENOENT; 58338494Sobrien continue; 58438494Sobrien } else { 58538494Sobrien if (cp->fs_opts.fs_mtab) { 58638494Sobrien plog(XLOG_MAP, "Trying mount of %s on %s fstype %s", 58738494Sobrien cp->fs_opts.fs_mtab, mp->am_path, p->fs_type); 58838494Sobrien } 58938494Sobrien cp->tried = TRUE; 59038494Sobrien } 59138494Sobrien 59238494Sobrien this_error = 0; 59338494Sobrien dont_retry = FALSE; 59438494Sobrien 59538494Sobrien if (mp->am_link) { 59638494Sobrien XFREE(mp->am_link); 59738494Sobrien mp->am_link = 0; 59838494Sobrien } 59938494Sobrien link_dir = mf->mf_fo->opt_sublink; 60038494Sobrien 60138494Sobrien if (link_dir && *link_dir) { 60238494Sobrien if (*link_dir == '/') { 60338494Sobrien mp->am_link = strdup(link_dir); 60438494Sobrien } else { 60538494Sobrien /* 60682794Sobrien * Try getting fs option from continuation, not mountpoint! 60738494Sobrien * Don't try logging the string from mf, since it may be bad! 60838494Sobrien */ 60938494Sobrien if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs) 61051292Sobrien plog(XLOG_ERROR, "use %s instead of 0x%lx", 61151292Sobrien cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs); 61238494Sobrien 61338494Sobrien mp->am_link = str3cat((char *) 0, 61438494Sobrien cp->fs_opts.opt_fs, "/", link_dir); 61538494Sobrien 61638494Sobrien normalize_slash(mp->am_link); 61738494Sobrien } 61838494Sobrien } 61938494Sobrien 62038494Sobrien if (mf->mf_error > 0) { 62138494Sobrien this_error = mf->mf_error; 62238494Sobrien } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { 62338494Sobrien /* 62438494Sobrien * Still mounting - retry later 62538494Sobrien */ 62638494Sobrien#ifdef DEBUG 62738494Sobrien dlog("Duplicate pending mount fstype %s", p->fs_type); 62838494Sobrien#endif /* DEBUG */ 62938494Sobrien this_error = -1; 63038494Sobrien } else if (FSRV_ISDOWN(mf->mf_server)) { 63138494Sobrien /* 63238494Sobrien * Would just mount from the same place 63338494Sobrien * as a hung mount - so give up 63438494Sobrien */ 63538494Sobrien#ifdef DEBUG 63638494Sobrien dlog("%s is already hung - giving up", mf->mf_mount); 63738494Sobrien#endif /* DEBUG */ 63838494Sobrien mp_error = EWOULDBLOCK; 63938494Sobrien dont_retry = TRUE; 64038494Sobrien this_error = -1; 64138494Sobrien } else if (mf->mf_flags & MFF_MOUNTED) { 64238494Sobrien#ifdef DEBUG 64338494Sobrien dlog("duplicate mount of \"%s\" ...", mf->mf_info); 64438494Sobrien#endif /* DEBUG */ 64538494Sobrien 64638494Sobrien /* 64738494Sobrien * Just call mounted() 64838494Sobrien */ 64938494Sobrien am_mounted(mp); 65038494Sobrien 65138494Sobrien this_error = 0; 65238494Sobrien break; 65338494Sobrien } 65438494Sobrien 65538494Sobrien /* 65638494Sobrien * Will usually need to play around with the mount nodes 65738494Sobrien * file attribute structure. This must be done here. 65842629Sobrien * Try and get things initialized, even if the fileserver 65938494Sobrien * is not known to be up. In the common case this will 66038494Sobrien * progress things faster. 66138494Sobrien */ 66238494Sobrien if (!this_error) { 66338494Sobrien /* 66438494Sobrien * Fill in attribute fields. 66538494Sobrien */ 66638494Sobrien if (mf->mf_ops->fs_flags & FS_DIRECTORY) 66738494Sobrien mk_fattr(mp, NFDIR); 66838494Sobrien else 66938494Sobrien mk_fattr(mp, NFLNK); 67038494Sobrien 67138494Sobrien if (p->fs_init) 67238494Sobrien this_error = (*p->fs_init) (mf); 67338494Sobrien } 67438494Sobrien 67538494Sobrien /* 67638494Sobrien * Make sure the fileserver is UP before doing any more work 67738494Sobrien */ 67838494Sobrien if (!FSRV_ISUP(mf->mf_server)) { 67938494Sobrien#ifdef DEBUG 68038494Sobrien dlog("waiting for server %s to become available", mf->mf_server->fs_host); 68138494Sobrien#endif /* DEBUG */ 68238494Sobrien this_error = -1; 68338494Sobrien } 68438494Sobrien 68538494Sobrien if (!this_error && mf->mf_fo->opt_delay) { 68638494Sobrien /* 68738494Sobrien * If there is a delay timer on the mount 68838494Sobrien * then don't try to mount if the timer 68938494Sobrien * has not expired. 69038494Sobrien */ 69138494Sobrien int i = atoi(mf->mf_fo->opt_delay); 69238494Sobrien if (i > 0 && clocktime() < (cp->start + i)) { 69338494Sobrien#ifdef DEBUG 69451292Sobrien dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start)); 69538494Sobrien#endif /* DEBUG */ 69638494Sobrien this_error = -1; 69738494Sobrien } 69838494Sobrien } 69938494Sobrien 70038494Sobrien if (this_error < 0 && !dont_retry) { 70138494Sobrien if (!mf_retry) 70238494Sobrien mf_retry = dup_mntfs(mf); 70338494Sobrien cp->retry = TRUE; 70452894Sobrien#ifdef DEBUG 70552894Sobrien dlog("will retry ...\n"); 70652894Sobrien#endif /* DEBUG */ 70752894Sobrien break; 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 } 72082794Sobrien 72182794Sobrien /* actually run the task, backgrounding as necessary */ 72238494Sobrien run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp); 72382794Sobrien 72438494Sobrien mf->mf_flags |= MFF_MKMNT; /* XXX */ 72538494Sobrien if (mf_retry) 72638494Sobrien free_mntfs(mf_retry); 72738494Sobrien return -1; 72838494Sobrien } else { 72938494Sobrien#ifdef DEBUG 73038494Sobrien dlog("foreground mount of \"%s\" ...", mf->mf_info); 73138494Sobrien#endif /* DEBUG */ 73238494Sobrien this_error = try_mount((voidp) mp); 73338494Sobrien if (this_error < 0) { 73438494Sobrien if (!mf_retry) 73538494Sobrien mf_retry = dup_mntfs(mf); 73638494Sobrien cp->retry = TRUE; 73738494Sobrien } 73838494Sobrien } 73938494Sobrien } 74038494Sobrien 74138494Sobrien if (this_error >= 0) { 74238494Sobrien if (this_error > 0) { 74338494Sobrien amd_stats.d_merr++; 74438494Sobrien if (mf != mf_retry) { 74538494Sobrien mf->mf_error = this_error; 74638494Sobrien mf->mf_flags |= MFF_ERROR; 74738494Sobrien } 74838494Sobrien } 74938494Sobrien 75038494Sobrien /* 75138494Sobrien * Wakeup anything waiting for this mount 75238494Sobrien */ 75338494Sobrien wakeup((voidp) mf); 75438494Sobrien } 75538494Sobrien } 75638494Sobrien 75738494Sobrien if (this_error && cp->retry) { 75838494Sobrien free_mntfs(mf); 75938494Sobrien mf = cp->mp->am_mnt = mf_retry; 76038494Sobrien /* 76138494Sobrien * Not retrying again (so far) 76238494Sobrien */ 76338494Sobrien cp->retry = FALSE; 76438494Sobrien cp->tried = FALSE; 76538494Sobrien /* 76638494Sobrien * Start at the beginning. 76738494Sobrien * Rewind the location vector and 76838494Sobrien * reset the default options. 76938494Sobrien */ 77052894Sobrien#ifdef DEBUG 77152894Sobrien dlog("(skipping rewind)\n"); 77252894Sobrien#endif /* DEBUG */ 77338494Sobrien /* 77438494Sobrien * Arrange that amfs_auto_bgmount is called 77538494Sobrien * after anything else happens. 77638494Sobrien */ 77738494Sobrien#ifdef DEBUG 77838494Sobrien dlog("Arranging to retry mount of %s", cp->mp->am_path); 77938494Sobrien#endif /* DEBUG */ 78038494Sobrien sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf); 78138494Sobrien if (cp->callout) 78238494Sobrien untimeout(cp->callout); 78338494Sobrien cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf); 78438494Sobrien 78538494Sobrien cp->mp->am_ttl = clocktime() + RETRY_INTERVAL; 78638494Sobrien 78738494Sobrien /* 78838494Sobrien * Not done yet - so don't return anything 78938494Sobrien */ 79038494Sobrien return -1; 79138494Sobrien } 79238494Sobrien 79338494Sobrien if (hard_error < 0 || this_error == 0) 79438494Sobrien hard_error = this_error; 79538494Sobrien 79638494Sobrien /* 79738494Sobrien * Discard handle on duff filesystem. 79838494Sobrien * This should never happen since it 79938494Sobrien * should be caught by the case above. 80038494Sobrien */ 80138494Sobrien if (mf_retry) { 80238494Sobrien if (hard_error) 80338494Sobrien plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount); 80438494Sobrien free_mntfs(mf_retry); 80538494Sobrien } 80638494Sobrien 80738494Sobrien /* 80838494Sobrien * If we get here, then either the mount succeeded or 80938494Sobrien * there is no more mount information available. 81038494Sobrien */ 81138494Sobrien if (hard_error < 0 && mp_error) 81238494Sobrien hard_error = cp->mp->am_error = mp_error; 81338494Sobrien if (hard_error > 0) { 81438494Sobrien /* 81538494Sobrien * Set a small(ish) timeout on an error node if 81638494Sobrien * the error was not a time out. 81738494Sobrien */ 81838494Sobrien switch (hard_error) { 81938494Sobrien case ETIMEDOUT: 82038494Sobrien case EWOULDBLOCK: 82138494Sobrien cp->mp->am_timeo = 17; 82238494Sobrien break; 82338494Sobrien default: 82438494Sobrien cp->mp->am_timeo = 5; 82538494Sobrien break; 82638494Sobrien } 82738494Sobrien new_ttl(cp->mp); 82838494Sobrien } 82938494Sobrien 83038494Sobrien /* 83138494Sobrien * Make sure that the error value in the mntfs has a 83238494Sobrien * reasonable value. 83338494Sobrien */ 83438494Sobrien if (mf->mf_error < 0) { 83538494Sobrien mf->mf_error = hard_error; 83638494Sobrien if (hard_error) 83738494Sobrien mf->mf_flags |= MFF_ERROR; 83838494Sobrien } 83938494Sobrien 84038494Sobrien /* 84138494Sobrien * In any case we don't need the continuation any more 84238494Sobrien */ 84338494Sobrien free_continuation(cp); 84438494Sobrien 84538494Sobrien return hard_error; 84638494Sobrien} 84738494Sobrien 84838494Sobrien 84938494Sobrien/* 85038494Sobrien * Automount interface to RPC lookup routine 85138494Sobrien * Find the corresponding entry and return 85238494Sobrien * the file handle for it. 85338494Sobrien */ 85438494Sobrienam_node * 85538494Sobrienamfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op) 85638494Sobrien{ 85738494Sobrien am_node *ap, *new_mp, *ap_hung; 85838494Sobrien char *info; /* Mount info - where to get the file system */ 85938494Sobrien char **ivec, **xivec; /* Split version of info */ 86038494Sobrien char *auto_opts; /* Automount options */ 86138494Sobrien int error = 0; /* Error so far */ 86238494Sobrien char path_name[MAXPATHLEN]; /* General path name buffer */ 86338494Sobrien char *pfname; /* Path for database lookup */ 86438494Sobrien struct continuation *cp; /* Continuation structure if need to mount */ 86538494Sobrien int in_progress = 0; /* # of (un)mount in progress */ 86638494Sobrien char *dflts; 86738494Sobrien mntfs *mf; 86838494Sobrien 86938494Sobrien#ifdef DEBUG 87038494Sobrien dlog("in amfs_auto_lookuppn"); 87138494Sobrien#endif /* DEBUG */ 87238494Sobrien 87338494Sobrien /* 87438494Sobrien * If the server is shutting down 87538494Sobrien * then don't return information 87638494Sobrien * about the mount point. 87738494Sobrien */ 87838494Sobrien if (amd_state == Finishing) { 87938494Sobrien#ifdef DEBUG 88038494Sobrien if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) { 88138494Sobrien dlog("%s mount ignored - going down", fname); 88238494Sobrien } else { 88338494Sobrien dlog("%s/%s mount ignored - going down", mp->am_path, fname); 88438494Sobrien } 88538494Sobrien#endif /* DEBUG */ 88638494Sobrien ereturn(ENOENT); 88738494Sobrien } 88838494Sobrien 88938494Sobrien /* 89038494Sobrien * Handle special case of "." and ".." 89138494Sobrien */ 89238494Sobrien if (fname[0] == '.') { 89338494Sobrien if (fname[1] == '\0') 89438494Sobrien return mp; /* "." is the current node */ 89538494Sobrien if (fname[1] == '.' && fname[2] == '\0') { 89638494Sobrien if (mp->am_parent) { 89738494Sobrien#ifdef DEBUG 89838494Sobrien dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); 89938494Sobrien#endif /* DEBUG */ 90038494Sobrien return mp->am_parent; /* ".." is the parent node */ 90138494Sobrien } 90238494Sobrien ereturn(ESTALE); 90338494Sobrien } 90438494Sobrien } 90538494Sobrien 90638494Sobrien /* 90738494Sobrien * Check for valid key name. 90838494Sobrien * If it is invalid then pretend it doesn't exist. 90938494Sobrien */ 91038494Sobrien if (!valid_key(fname)) { 91138494Sobrien plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); 91238494Sobrien ereturn(ENOENT); 91338494Sobrien } 91438494Sobrien 91538494Sobrien /* 91638494Sobrien * Expand key name. 91738494Sobrien * fname is now a private copy. 91838494Sobrien */ 91938494Sobrien fname = expand_key(fname); 92038494Sobrien 92138494Sobrien for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) { 92238494Sobrien /* 92338494Sobrien * Otherwise search children of this node 92438494Sobrien */ 92538494Sobrien if (FSTREQ(ap->am_name, fname)) { 92638494Sobrien mf = ap->am_mnt; 92738494Sobrien if (ap->am_error) { 92838494Sobrien error = ap->am_error; 92938494Sobrien continue; 93038494Sobrien } 93138494Sobrien /* 93238494Sobrien * If the error code is undefined then it must be 93338494Sobrien * in progress. 93438494Sobrien */ 93538494Sobrien if (mf->mf_error < 0) 93638494Sobrien goto in_progrss; 93738494Sobrien 93838494Sobrien /* 93938494Sobrien * Check for a hung node 94038494Sobrien */ 94138494Sobrien if (FSRV_ISDOWN(mf->mf_server)) { 94238494Sobrien#ifdef DEBUG 94338494Sobrien dlog("server hung"); 94438494Sobrien#endif /* DEBUG */ 94538494Sobrien error = ap->am_error; 94638494Sobrien ap_hung = ap; 94738494Sobrien continue; 94838494Sobrien } 94938494Sobrien /* 95038494Sobrien * If there was a previous error with this node 95138494Sobrien * then return that error code. 95238494Sobrien */ 95338494Sobrien if (mf->mf_flags & MFF_ERROR) { 95438494Sobrien error = mf->mf_error; 95538494Sobrien continue; 95638494Sobrien } 95738494Sobrien if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { 95838494Sobrien in_progrss: 95938494Sobrien /* 96038494Sobrien * If the fs is not mounted or it is unmounting then there 96138494Sobrien * is a background (un)mount in progress. In this case 96238494Sobrien * we just drop the RPC request (return nil) and 96338494Sobrien * wait for a retry, by which time the (un)mount may 96438494Sobrien * have completed. 96538494Sobrien */ 96638494Sobrien#ifdef DEBUG 96738494Sobrien dlog("ignoring mount of %s in %s -- flags (%x) in progress", 96838494Sobrien fname, mf->mf_mount, mf->mf_flags); 96938494Sobrien#endif /* DEBUG */ 97038494Sobrien in_progress++; 97138494Sobrien continue; 97238494Sobrien } 97338494Sobrien 97438494Sobrien /* 97538494Sobrien * Otherwise we have a hit: return the current mount point. 97638494Sobrien */ 97738494Sobrien#ifdef DEBUG 97838494Sobrien dlog("matched %s in %s", fname, ap->am_path); 97938494Sobrien#endif /* DEBUG */ 98038494Sobrien XFREE(fname); 98138494Sobrien return ap; 98238494Sobrien } 98338494Sobrien } 98438494Sobrien 98538494Sobrien if (in_progress) { 98638494Sobrien#ifdef DEBUG 98738494Sobrien dlog("Waiting while %d mount(s) in progress", in_progress); 98838494Sobrien#endif /* DEBUG */ 98938494Sobrien XFREE(fname); 99038494Sobrien ereturn(-1); 99138494Sobrien } 99238494Sobrien 99338494Sobrien /* 99442629Sobrien * If an error occurred then return it. 99538494Sobrien */ 99638494Sobrien if (error) { 99738494Sobrien#ifdef DEBUG 99838494Sobrien errno = error; /* XXX */ 99951292Sobrien dlog("Returning error: %m"); 100038494Sobrien#endif /* DEBUG */ 100138494Sobrien XFREE(fname); 100238494Sobrien ereturn(error); 100338494Sobrien } 100438494Sobrien 100538494Sobrien /* 100638494Sobrien * If doing a delete then don't create again! 100738494Sobrien */ 100838494Sobrien switch (op) { 100938494Sobrien case VLOOK_DELETE: 101038494Sobrien ereturn(ENOENT); 101138494Sobrien 101238494Sobrien case VLOOK_CREATE: 101338494Sobrien break; 101438494Sobrien 101538494Sobrien default: 101638494Sobrien plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op); 101738494Sobrien ereturn(EINVAL); 101838494Sobrien } 101938494Sobrien 102038494Sobrien /* 102138494Sobrien * If the server is going down then just return, 102238494Sobrien * don't try to mount any more file systems 102338494Sobrien */ 102438494Sobrien if ((int) amd_state >= (int) Finishing) { 102538494Sobrien#ifdef DEBUG 102638494Sobrien dlog("not found - server going down anyway"); 102738494Sobrien#endif /* DEBUG */ 102838494Sobrien XFREE(fname); 102938494Sobrien ereturn(ENOENT); 103038494Sobrien } 103138494Sobrien 103238494Sobrien /* 103338494Sobrien * If we get there then this is a reference to an, 103438494Sobrien * as yet, unknown name so we need to search the mount 103538494Sobrien * map for it. 103638494Sobrien */ 103738494Sobrien if (mp->am_pref) { 103838494Sobrien sprintf(path_name, "%s%s", mp->am_pref, fname); 103938494Sobrien pfname = path_name; 104038494Sobrien } else { 104138494Sobrien pfname = fname; 104238494Sobrien } 104338494Sobrien 104438494Sobrien mf = mp->am_mnt; 104538494Sobrien 104638494Sobrien#ifdef DEBUG 104738494Sobrien dlog("will search map info in %s to find %s", mf->mf_info, pfname); 104838494Sobrien#endif /* DEBUG */ 104938494Sobrien /* 105038494Sobrien * Consult the oracle for some mount information. 105138494Sobrien * info is malloc'ed and belongs to this routine. 105238494Sobrien * It ends up being free'd in free_continuation(). 105338494Sobrien * 105438494Sobrien * Note that this may return -1 indicating that information 105538494Sobrien * is not yet available. 105638494Sobrien */ 105738494Sobrien error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); 105838494Sobrien if (error) { 105938494Sobrien if (error > 0) 106038494Sobrien plog(XLOG_MAP, "No map entry for %s", pfname); 106138494Sobrien else 106238494Sobrien plog(XLOG_MAP, "Waiting on map entry for %s", pfname); 106338494Sobrien XFREE(fname); 106438494Sobrien ereturn(error); 106538494Sobrien } 106638494Sobrien#ifdef DEBUG 106738494Sobrien dlog("mount info is %s", info); 106838494Sobrien#endif /* DEBUG */ 106938494Sobrien 107038494Sobrien /* 107138494Sobrien * Split info into an argument vector. 107238494Sobrien * The vector is malloc'ed and belongs to 107338494Sobrien * this routine. It is free'd in free_continuation() 107438494Sobrien */ 107538494Sobrien xivec = ivec = strsplit(info, ' ', '\"'); 107638494Sobrien 107738494Sobrien /* 107838494Sobrien * Default error code... 107938494Sobrien */ 108038494Sobrien if (ap_hung) 108138494Sobrien error = EWOULDBLOCK; 108238494Sobrien else 108338494Sobrien error = ENOENT; 108438494Sobrien 108538494Sobrien /* 108638494Sobrien * Allocate a new map 108738494Sobrien */ 108838494Sobrien new_mp = exported_ap_alloc(); 108938494Sobrien if (new_mp == 0) { 109038494Sobrien XFREE(xivec); 109138494Sobrien XFREE(info); 109238494Sobrien XFREE(fname); 109338494Sobrien ereturn(ENOSPC); 109438494Sobrien } 109538494Sobrien if (mf->mf_auto) 109638494Sobrien auto_opts = mf->mf_auto; 109738494Sobrien else 109838494Sobrien auto_opts = ""; 109938494Sobrien 110038494Sobrien auto_opts = strdup(auto_opts); 110138494Sobrien 110238494Sobrien#ifdef DEBUG 110338494Sobrien dlog("searching for /defaults entry"); 110438494Sobrien#endif /* DEBUG */ 110538494Sobrien if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) { 110638494Sobrien char *dfl; 110738494Sobrien char **rvec; 110838494Sobrien#ifdef DEBUG 110938494Sobrien dlog("/defaults gave %s", dflts); 111038494Sobrien#endif /* DEBUG */ 111138494Sobrien if (*dflts == '-') 111238494Sobrien dfl = dflts + 1; 111338494Sobrien else 111438494Sobrien dfl = dflts; 111538494Sobrien 111638494Sobrien /* 111738494Sobrien * Chop the defaults up 111838494Sobrien */ 111938494Sobrien rvec = strsplit(dfl, ' ', '\"'); 112038494Sobrien 112182794Sobrien if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) { 112238494Sobrien /* 112338494Sobrien * Pick whichever first entry matched the list of selectors. 112438494Sobrien * Strip the selectors from the string, and assign to dfl the 112538494Sobrien * rest of the string. 112638494Sobrien */ 112738494Sobrien if (rvec) { 112838494Sobrien am_opts ap; 112938494Sobrien am_ops *pt; 113038494Sobrien char **sp = rvec; 113138494Sobrien while (*sp) { /* loop until you find something, if any */ 113238494Sobrien memset((char *) &ap, 0, sizeof(am_opts)); 113382794Sobrien /* 113482794Sobrien * This next routine cause many spurious "expansion of ... is" 113582794Sobrien * messages, which are ignored, b/c all we need out of this 113682794Sobrien * routine is to match selectors. These spurious messages may 113782794Sobrien * be wrong, esp. if they try to expand ${key} b/c it will 113882794Sobrien * get expanded to "/defaults" 113982794Sobrien */ 114038494Sobrien pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", 114138494Sobrien mp->am_parent->am_mnt->mf_info); 114241142Sobrien free_opts(&ap); /* don't leak */ 114338494Sobrien if (pt == &amfs_error_ops) { 114482794Sobrien plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp); 114538494Sobrien } else { 114638494Sobrien dfl = strip_selectors(*sp, "/defaults"); 114738494Sobrien plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); 114838494Sobrien break; 114938494Sobrien } 115038494Sobrien ++sp; 115138494Sobrien } 115238494Sobrien } 115338494Sobrien } else { /* not enable_default_selectors */ 115438494Sobrien /* 115538494Sobrien * Extract first value 115638494Sobrien */ 115738494Sobrien dfl = rvec[0]; 115838494Sobrien } 115938494Sobrien 116038494Sobrien /* 116138494Sobrien * If there were any values at all... 116238494Sobrien */ 116338494Sobrien if (dfl) { 116438494Sobrien /* 116538494Sobrien * Log error if there were other values 116638494Sobrien */ 116782794Sobrien if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) { 116838494Sobrien# ifdef DEBUG 116938494Sobrien dlog("/defaults chopped into %s", dfl); 117038494Sobrien# endif /* DEBUG */ 117138494Sobrien plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); 117238494Sobrien } 117338494Sobrien 117438494Sobrien /* 117538494Sobrien * Prepend to existing defaults if they exist, 117638494Sobrien * otherwise just use these defaults. 117738494Sobrien */ 117838494Sobrien if (*auto_opts && *dfl) { 117938494Sobrien char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2); 118038494Sobrien sprintf(nopts, "%s;%s", dfl, auto_opts); 118138494Sobrien XFREE(auto_opts); 118238494Sobrien auto_opts = nopts; 118338494Sobrien } else if (*dfl) { 118438494Sobrien auto_opts = strealloc(auto_opts, dfl); 118538494Sobrien } 118638494Sobrien } 118738494Sobrien XFREE(dflts); 118838494Sobrien /* 118938494Sobrien * Don't need info vector any more 119038494Sobrien */ 119138494Sobrien XFREE(rvec); 119238494Sobrien } 119338494Sobrien 119438494Sobrien /* 119538494Sobrien * Fill it in 119638494Sobrien */ 119738494Sobrien init_map(new_mp, fname); 119838494Sobrien 119938494Sobrien /* 120038494Sobrien * Put it in the table 120138494Sobrien */ 120238494Sobrien insert_am(new_mp, mp); 120338494Sobrien 120438494Sobrien /* 120538494Sobrien * Fill in some other fields, 120638494Sobrien * path and mount point. 120738494Sobrien * 120838494Sobrien * bugfix: do not prepend old am_path if direct map 120938494Sobrien * <wls@astro.umd.edu> William Sebok 121038494Sobrien */ 121138494Sobrien new_mp->am_path = str3cat(new_mp->am_path, 121238494Sobrien mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path, 121338494Sobrien *fname == '/' ? "" : "/", fname); 121438494Sobrien 121538494Sobrien#ifdef DEBUG 121638494Sobrien dlog("setting path to %s", new_mp->am_path); 121738494Sobrien#endif /* DEBUG */ 121838494Sobrien 121938494Sobrien /* 122038494Sobrien * Take private copy of pfname 122138494Sobrien */ 122238494Sobrien pfname = strdup(pfname); 122338494Sobrien 122438494Sobrien /* 122538494Sobrien * Construct a continuation 122638494Sobrien */ 122738494Sobrien cp = ALLOC(struct continuation); 122838494Sobrien cp->callout = 0; 122938494Sobrien cp->mp = new_mp; 123038494Sobrien cp->xivec = xivec; 123138494Sobrien cp->ivec = ivec; 123238494Sobrien cp->info = info; 123338494Sobrien cp->key = pfname; 123438494Sobrien cp->auto_opts = auto_opts; 123538494Sobrien cp->retry = FALSE; 123638494Sobrien cp->tried = FALSE; 123738494Sobrien cp->start = clocktime(); 123838494Sobrien cp->def_opts = strdup(auto_opts); 123938494Sobrien memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts)); 124038494Sobrien 124138494Sobrien /* 124238494Sobrien * Try and mount the file system. If this succeeds immediately (possible 124338494Sobrien * for a ufs file system) then return the attributes, otherwise just 124438494Sobrien * return an error. 124538494Sobrien */ 124638494Sobrien error = amfs_auto_bgmount(cp, error); 124738494Sobrien reschedule_timeout_mp(); 124838494Sobrien if (!error) { 124938494Sobrien XFREE(fname); 125038494Sobrien return new_mp; 125138494Sobrien } 125238494Sobrien 125338494Sobrien /* 125438494Sobrien * Code for quick reply. If nfs_program_2_transp is set, then 125538494Sobrien * its the transp that's been passed down from nfs_program_2(). 125638494Sobrien * If new_mp->am_transp is not already set, set it by copying in 125738494Sobrien * nfs_program_2_transp. Once am_transp is set, quick_reply() can 125838494Sobrien * use it to send a reply to the client that requested this mount. 125938494Sobrien */ 126038494Sobrien if (nfs_program_2_transp && !new_mp->am_transp) { 126138494Sobrien new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); 126238494Sobrien *(new_mp->am_transp) = *nfs_program_2_transp; 126338494Sobrien } 126438494Sobrien if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) 126538494Sobrien new_mp->am_error = error; 126638494Sobrien 126738494Sobrien assign_error_mntfs(new_mp); 126838494Sobrien 126938494Sobrien XFREE(fname); 127038494Sobrien 127138494Sobrien ereturn(error); 127238494Sobrien} 127338494Sobrien 127438494Sobrien 127538494Sobrien/* 127638494Sobrien * Locate next node in sibling list which is mounted 127738494Sobrien * and is not an error node. 127838494Sobrien */ 127938494Sobrienam_node * 128038494Sobriennext_nonerror_node(am_node *xp) 128138494Sobrien{ 128238494Sobrien mntfs *mf; 128338494Sobrien 128438494Sobrien /* 128538494Sobrien * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 128638494Sobrien * Fixes a race condition when mounting direct automounts. 128738494Sobrien * Also fixes a problem when doing a readdir on a directory 128838494Sobrien * containing hung automounts. 128938494Sobrien */ 129038494Sobrien while (xp && 129138494Sobrien (!(mf = xp->am_mnt) || /* No mounted filesystem */ 129238494Sobrien mf->mf_error != 0 || /* There was a mntfs error */ 129338494Sobrien xp->am_error != 0 || /* There was a mount error */ 129438494Sobrien !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 129538494Sobrien (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 129638494Sobrien ) 129738494Sobrien xp = xp->am_osib; 129838494Sobrien 129938494Sobrien return xp; 130038494Sobrien} 130138494Sobrien 130238494Sobrien 130338494Sobrien/* 130438494Sobrien * This readdir function which call a special version of it that allows 130538494Sobrien * browsing if browsable_dirs=yes was set on the map. 130638494Sobrien */ 130738494Sobrienint 130838494Sobrienamfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count) 130938494Sobrien{ 131038494Sobrien u_int gen = *(u_int *) cookie; 131138494Sobrien am_node *xp; 131238494Sobrien mntent_t mnt; 131382794Sobrien#ifdef DEBUG 131482794Sobrien nfsentry *ne; 131582794Sobrien static int j; 131682794Sobrien#endif /* DEBUG */ 131738494Sobrien 131838494Sobrien dp->dl_eof = FALSE; /* assume readdir not done */ 131938494Sobrien 132038494Sobrien /* check if map is browsable */ 132138494Sobrien if (mp->am_mnt && mp->am_mnt->mf_mopts) { 132238494Sobrien mnt.mnt_opts = mp->am_mnt->mf_mopts; 132338494Sobrien if (hasmntopt(&mnt, "fullybrowsable")) 132438494Sobrien return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE); 132538494Sobrien if (hasmntopt(&mnt, "browsable")) 132638494Sobrien return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE); 132738494Sobrien } 132838494Sobrien 132982794Sobrien /* when gen is 0, we start reading from the beginning of the directory */ 133038494Sobrien if (gen == 0) { 133138494Sobrien /* 133238494Sobrien * In the default instance (which is used to start a search) we return 133338494Sobrien * "." and "..". 133438494Sobrien * 133538494Sobrien * This assumes that the count is big enough to allow both "." and ".." 133638494Sobrien * to be returned in a single packet. If it isn't (which would be 133738494Sobrien * fairly unbelievable) then tough. 133838494Sobrien */ 133938494Sobrien#ifdef DEBUG 134051292Sobrien dlog("amfs_auto_readdir: default search"); 134138494Sobrien#endif /* DEBUG */ 134238494Sobrien /* 134338494Sobrien * Check for enough room. This is extremely approximate but is more 134438494Sobrien * than enough space. Really need 2 times: 134538494Sobrien * 4byte fileid 134638494Sobrien * 4byte cookie 134738494Sobrien * 4byte name length 134838494Sobrien * 4byte name 134938494Sobrien * plus the dirlist structure */ 135038494Sobrien if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 135138494Sobrien return EINVAL; 135238494Sobrien 135338494Sobrien xp = next_nonerror_node(mp->am_child); 135438494Sobrien dp->dl_entries = ep; 135538494Sobrien 135638494Sobrien /* construct "." */ 135738494Sobrien ep[0].ne_fileid = mp->am_gen; 135838494Sobrien ep[0].ne_name = "."; 135938494Sobrien ep[0].ne_nextentry = &ep[1]; 136038494Sobrien *(u_int *) ep[0].ne_cookie = 0; 136138494Sobrien 136238494Sobrien /* construct ".." */ 136338494Sobrien if (mp->am_parent) 136438494Sobrien ep[1].ne_fileid = mp->am_parent->am_gen; 136538494Sobrien else 136638494Sobrien ep[1].ne_fileid = mp->am_gen; 136738494Sobrien ep[1].ne_name = ".."; 136838494Sobrien ep[1].ne_nextentry = 0; 136951292Sobrien *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE); 137038494Sobrien 137138494Sobrien if (!xp) 137238494Sobrien dp->dl_eof = TRUE; /* by default assume readdir done */ 137338494Sobrien 137482794Sobrien#ifdef DEBUG 137582794Sobrien amuDebug(D_READDIR) 137682794Sobrien for (j=0,ne=ep; ne; ne=ne->ne_nextentry) 137782794Sobrien plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d", 137882794Sobrien j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); 137982794Sobrien#endif /* DEBUG */ 138038494Sobrien return 0; 138138494Sobrien } 138238494Sobrien#ifdef DEBUG 138351292Sobrien dlog("amfs_auto_readdir: real child"); 138438494Sobrien#endif /* DEBUG */ 138538494Sobrien 138651292Sobrien if (gen == DOT_DOT_COOKIE) { 138738494Sobrien#ifdef DEBUG 138851292Sobrien dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path); 138938494Sobrien#endif /* DEBUG */ 139038494Sobrien dp->dl_eof = TRUE; 139138494Sobrien dp->dl_entries = 0; 139282794Sobrien#ifdef DEBUG 139382794Sobrien amuDebug(D_READDIR) 139482794Sobrien plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n"); 139582794Sobrien#endif /* DEBUG */ 139638494Sobrien return 0; 139738494Sobrien } 139838494Sobrien 139938494Sobrien /* non-browsable directories code */ 140038494Sobrien xp = mp->am_child; 140138494Sobrien while (xp && xp->am_gen != gen) 140238494Sobrien xp = xp->am_osib; 140338494Sobrien 140438494Sobrien if (xp) { 140538494Sobrien int nbytes = count / 2; /* conservative */ 140638494Sobrien int todo = MAX_READDIR_ENTRIES; 140751292Sobrien 140838494Sobrien dp->dl_entries = ep; 140938494Sobrien do { 141038494Sobrien am_node *xp_next = next_nonerror_node(xp->am_osib); 141138494Sobrien 141238494Sobrien if (xp_next) { 141338494Sobrien *(u_int *) ep->ne_cookie = xp_next->am_gen; 141438494Sobrien } else { 141551292Sobrien *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE; 141638494Sobrien dp->dl_eof = TRUE; 141738494Sobrien } 141838494Sobrien 141938494Sobrien ep->ne_fileid = xp->am_gen; 142038494Sobrien ep->ne_name = xp->am_name; 142138494Sobrien nbytes -= sizeof(*ep) + 1; 142238494Sobrien if (xp->am_name) 142338494Sobrien nbytes -= strlen(xp->am_name); 142438494Sobrien 142538494Sobrien xp = xp_next; 142638494Sobrien 142738494Sobrien if (nbytes > 0 && !dp->dl_eof && todo > 1) { 142838494Sobrien ep->ne_nextentry = ep + 1; 142938494Sobrien ep++; 143038494Sobrien --todo; 143138494Sobrien } else { 143238494Sobrien todo = 0; 143338494Sobrien } 143438494Sobrien } while (todo > 0); 143538494Sobrien 143638494Sobrien ep->ne_nextentry = 0; 143738494Sobrien 143882794Sobrien#ifdef DEBUG 143982794Sobrien amuDebug(D_READDIR) 144082794Sobrien for (j=0,ne=ep; ne; ne=ne->ne_nextentry) 144182794Sobrien plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d", 144282794Sobrien j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); 144382794Sobrien#endif /* DEBUG */ 144438494Sobrien return 0; 144538494Sobrien } 144638494Sobrien return ESTALE; 144738494Sobrien} 144838494Sobrien 144938494Sobrien 145038494Sobrien/* This one is called only if map is browsable */ 145138494Sobrienstatic int 145238494Sobrienamfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable) 145338494Sobrien{ 145438494Sobrien u_int gen = *(u_int *) cookie; 145538494Sobrien int chain_length, i; 145638494Sobrien static nfsentry *te, *te_next; 145782794Sobrien#ifdef DEBUG 145838494Sobrien nfsentry *ne; 145938494Sobrien static int j; 146082794Sobrien#endif /* DEBUG */ 146138494Sobrien 146238494Sobrien dp->dl_eof = FALSE; /* assume readdir not done */ 146338494Sobrien 146482794Sobrien#ifdef DEBUG 146582794Sobrien amuDebug(D_READDIR) 146682794Sobrien plog(XLOG_DEBUG, "amfs_auto_readdir_browsable gen=%u, count=%d", 146782794Sobrien gen, count); 146882794Sobrien#endif /* DEBUG */ 146938494Sobrien 147038494Sobrien if (gen == 0) { 147138494Sobrien /* 147238494Sobrien * In the default instance (which is used to start a search) we return 147338494Sobrien * "." and "..". 147438494Sobrien * 147538494Sobrien * This assumes that the count is big enough to allow both "." and ".." 147638494Sobrien * to be returned in a single packet. If it isn't (which would be 147738494Sobrien * fairly unbelievable) then tough. 147838494Sobrien */ 147938494Sobrien#ifdef DEBUG 148051292Sobrien dlog("amfs_auto_readdir_browsable: default search"); 148138494Sobrien#endif /* DEBUG */ 148238494Sobrien /* 148338494Sobrien * Check for enough room. This is extremely approximate but is more 148438494Sobrien * than enough space. Really need 2 times: 148538494Sobrien * 4byte fileid 148638494Sobrien * 4byte cookie 148738494Sobrien * 4byte name length 148838494Sobrien * 4byte name 148938494Sobrien * plus the dirlist structure */ 149038494Sobrien if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 149138494Sobrien return EINVAL; 149238494Sobrien 149338494Sobrien /* 149438494Sobrien * compute # of entries to send in this chain. 149538494Sobrien * heuristics: 128 bytes per entry. 149638494Sobrien * This is too much probably, but it seems to work better because 149738494Sobrien * of the re-entrant nature of nfs_readdir, and esp. on systems 149838494Sobrien * like OpenBSD 2.2. 149938494Sobrien */ 150038494Sobrien chain_length = count / 128; 150138494Sobrien 150238494Sobrien /* reset static state counters */ 150338494Sobrien te = te_next = NULL; 150438494Sobrien 150538494Sobrien dp->dl_entries = ep; 150638494Sobrien 150738494Sobrien /* construct "." */ 150838494Sobrien ep[0].ne_fileid = mp->am_gen; 150938494Sobrien ep[0].ne_name = "."; 151038494Sobrien ep[0].ne_nextentry = &ep[1]; 151138494Sobrien *(u_int *) ep[0].ne_cookie = 0; 151238494Sobrien 151338494Sobrien /* construct ".." */ 151438494Sobrien if (mp->am_parent) 151538494Sobrien ep[1].ne_fileid = mp->am_parent->am_gen; 151638494Sobrien else 151738494Sobrien ep[1].ne_fileid = mp->am_gen; 151882794Sobrien 151938494Sobrien ep[1].ne_name = ".."; 152038494Sobrien ep[1].ne_nextentry = 0; 152151292Sobrien *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE; 152238494Sobrien 152338494Sobrien /* 152438494Sobrien * If map is browsable, call a function make_entry_chain() to construct 152538494Sobrien * a linked list of unmounted keys, and return it. Then link the chain 152638494Sobrien * to the regular list. Get the chain only once, but return 152738494Sobrien * chunks of it each time. 152838494Sobrien */ 152938494Sobrien te = make_entry_chain(mp, dp->dl_entries, fully_browsable); 153038494Sobrien if (!te) 153138494Sobrien return 0; 153282794Sobrien#ifdef DEBUG 153382794Sobrien amuDebug(D_READDIR) 153482794Sobrien for (j=0,ne=te; ne; ne=ne->ne_nextentry) 153582794Sobrien plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name); 153682794Sobrien#endif /* DEBUG */ 153738494Sobrien 153838494Sobrien /* return only "chain_length" entries */ 153938494Sobrien te_next = te; 154038494Sobrien for (i=1; i<chain_length; ++i) { 154138494Sobrien te_next = te_next->ne_nextentry; 154238494Sobrien if (!te_next) 154338494Sobrien break; 154438494Sobrien } 154538494Sobrien if (te_next) { 154638494Sobrien nfsentry *te_saved = te_next->ne_nextentry; 154738494Sobrien te_next->ne_nextentry = NULL; /* terminate "te" chain */ 154842629Sobrien te_next = te_saved; /* save rest of "te" for next iteration */ 154938494Sobrien dp->dl_eof = FALSE; /* tell readdir there's more */ 155038494Sobrien } else { 155138494Sobrien dp->dl_eof = TRUE; /* tell readdir that's it */ 155238494Sobrien } 155338494Sobrien ep[1].ne_nextentry = te; /* append this chunk of "te" chain */ 155482794Sobrien#ifdef DEBUG 155582794Sobrien amuDebug(D_READDIR) { 155682794Sobrien for (j=0,ne=te; ne; ne=ne->ne_nextentry) 155782794Sobrien plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name); 155882794Sobrien for (j=0,ne=ep; ne; ne=ne->ne_nextentry) 155982794Sobrien plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d", 156082794Sobrien j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); 156182794Sobrien plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof); 156282794Sobrien } 156338494Sobrien#endif /* DEBUG_READDIR */ 156438494Sobrien return 0; 156538494Sobrien } /* end of "if (gen == 0)" statement */ 156638494Sobrien 156738494Sobrien#ifdef DEBUG 156851292Sobrien dlog("amfs_auto_readdir_browsable: real child"); 156938494Sobrien#endif /* DEBUG */ 157038494Sobrien 157151292Sobrien if (gen == DOT_DOT_COOKIE) { 157238494Sobrien#ifdef DEBUG 157351292Sobrien dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path); 157438494Sobrien#endif /* DEBUG */ 157538494Sobrien dp->dl_eof = TRUE; 157638494Sobrien dp->dl_entries = 0; 157738494Sobrien return 0; 157838494Sobrien } 157938494Sobrien 158038494Sobrien /* 158138494Sobrien * If browsable directories, then continue serving readdir() with another 158238494Sobrien * chunk of entries, starting from where we left off (when gen was equal 158338494Sobrien * to 0). Once again, assume last chunk served to readdir. 158438494Sobrien */ 158538494Sobrien dp->dl_eof = TRUE; 158638494Sobrien dp->dl_entries = ep; 158738494Sobrien 158838494Sobrien te = te_next; /* reset 'te' from last saved te_next */ 158938494Sobrien if (!te) { /* another indicator of end of readdir */ 159038494Sobrien dp->dl_entries = 0; 159138494Sobrien return 0; 159238494Sobrien } 159338494Sobrien /* 159438494Sobrien * compute # of entries to send in this chain. 159538494Sobrien * heuristics: 128 bytes per entry. 159638494Sobrien */ 159738494Sobrien chain_length = count / 128; 159838494Sobrien 159938494Sobrien /* return only "chain_length" entries */ 160038494Sobrien for (i=1; i<chain_length; ++i) { 160138494Sobrien te_next = te_next->ne_nextentry; 160238494Sobrien if (!te_next) 160338494Sobrien break; 160438494Sobrien } 160538494Sobrien if (te_next) { 160638494Sobrien nfsentry *te_saved = te_next->ne_nextentry; 160738494Sobrien te_next->ne_nextentry = NULL; /* terminate "te" chain */ 160842629Sobrien te_next = te_saved; /* save rest of "te" for next iteration */ 160938494Sobrien dp->dl_eof = FALSE; /* tell readdir there's more */ 161038494Sobrien } 161138494Sobrien ep = te; /* send next chunk of "te" chain */ 161238494Sobrien dp->dl_entries = ep; 161382794Sobrien#ifdef DEBUG 161482794Sobrien amuDebug(D_READDIR) { 161582794Sobrien plog(XLOG_DEBUG, "dl_entries=0x%lx, te_next=0x%lx, dl_eof=%d", 161682794Sobrien (long) dp->dl_entries, (long) te_next, dp->dl_eof); 161782794Sobrien for (ne=te; ne; ne=ne->ne_nextentry) 161882794Sobrien plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name); 161982794Sobrien } 162082794Sobrien#endif /* DEBUG */ 162138494Sobrien return 0; 162238494Sobrien} 162338494Sobrien 162438494Sobrien 162538494Sobrienint 162638494Sobrienamfs_auto_fmount(am_node *mp) 162738494Sobrien{ 162838494Sobrien mntfs *mf = mp->am_mnt; 162938494Sobrien return (*mf->mf_ops->fmount_fs) (mf); 163038494Sobrien} 163138494Sobrien 163238494Sobrien 163338494Sobrienint 163438494Sobrienamfs_auto_fumount(am_node *mp) 163538494Sobrien{ 163638494Sobrien mntfs *mf = mp->am_mnt; 163738494Sobrien return (*mf->mf_ops->fumount_fs) (mf); 163838494Sobrien} 1639