2 * Copyright (c) 1997-1999 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * $Id: amfs_auto.c,v 1.5 1999/09/30 21:01:29 ezk Exp $
46 * Automount file system
51 #endif /* HAVE_CONFIG_H */
55 /****************************************************************************
57 ****************************************************************************/
58 #define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
60 /* DEVELOPERS: turn this on for special debugging of readdir code */
63 #define DOT_DOT_COOKIE (u_int) 1
65 /****************************************************************************
67 ****************************************************************************/
70 /****************************************************************************
71 *** FORWARD DEFINITIONS ***
72 ****************************************************************************/
73 static int amfs_auto_bgmount(struct continuation * cp, int mpe);
74 static int amfs_auto_mount(am_node *mp);
75 static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
76 static void amfs_auto_umounted(am_node *mp);
79 /****************************************************************************
80 *** OPS STRUCTURES ***
81 ****************************************************************************/
82 am_ops amfs_auto_ops =
86 0, /* amfs_auto_init */
93 0, /* amfs_auto_readlink */
94 0, /* amfs_auto_mounted */
97 FS_AMQINFO | FS_DIRECTORY
101 /****************************************************************************
103 ****************************************************************************/
105 * AMFS_AUTO needs nothing in particular.
108 amfs_auto_match(am_opts *fo)
110 char *p = fo->opt_rfs;
113 plog(XLOG_USER, "auto: no mount point named (rfs:=)");
117 plog(XLOG_USER, "auto: no map named (fs:=)");
122 * Swap round fs:= and rfs:= options
123 * ... historical (jsp)
125 fo->opt_rfs = fo->opt_fs;
129 * mtab entry turns out to be the name of the mount map
131 return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
138 * Build a new map cache for this node, or re-use
139 * an existing cache for the same map.
142 amfs_auto_mkcacheref(mntfs *mf)
146 if (mf->mf_fo && mf->mf_fo->opt_cache)
147 cache = mf->mf_fo->opt_cache;
150 mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
151 mf->mf_fo->opt_maptype);
152 mf->mf_prfree = mapc_free;
160 amfs_auto_mount(am_node *mp)
162 mntfs *mf = mp->am_mnt;
165 * Pseudo-directories are used to provide some structure
166 * to the automounted directories instead
167 * of putting them all in the top-level automount directory.
169 * Here, just increment the parent's link count.
171 mp->am_parent->am_fattr.na_nlink++;
174 * Info field of . means use parent's info field.
175 * Historical - not documented.
177 if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
178 mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
183 * If there is an option prefix then use that else
184 * If the parent had a prefix then use that with name
185 * of this node appended else
186 * Use the name of this node.
188 * That means if you want no prefix you must say so
191 if (mf->mf_fo->opt_pref) {
192 /* allow pref:=null to set a real null prefix */
193 if (STREQ(mf->mf_fo->opt_pref, "null")) {
197 * the prefix specified as an option
199 mp->am_pref = strdup(mf->mf_fo->opt_pref);
203 * else the parent's prefix
204 * followed by the name
207 char *ppref = mp->am_parent->am_pref;
210 mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
216 amfs_auto_mkcacheref(mf);
225 * Unmount an automount sub-node
228 amfs_auto_umount(am_node *mp)
235 * Unmount an automount node
238 amfs_auto_umounted(am_node *mp)
241 * If this is a pseudo-directory then just adjust the link count
242 * in the parent, otherwise call the generic unmount routine
244 if (mp->am_parent && mp->am_parent->am_parent)
245 --mp->am_parent->am_fattr.na_nlink;
250 * Discard an old continuation
253 free_continuation(struct continuation *cp)
256 untimeout(cp->callout);
260 XFREE(cp->auto_opts);
262 free_opts(&cp->fs_opts);
268 * Discard the underlying mount point and replace
269 * with a reference to an error filesystem.
272 assign_error_mntfs(am_node *mp)
274 if (mp->am_error > 0) {
276 * Save the old error code
278 int error = mp->am_error;
280 error = mp->am_mnt->mf_error;
282 * Discard the old filesystem
284 free_mntfs(mp->am_mnt);
286 * Allocate a new error reference
288 mp->am_mnt = new_mntfs();
290 * Put back the error code
292 mp->am_mnt->mf_error = error;
293 mp->am_mnt->mf_flags |= MFF_ERROR;
295 * Zero the error in the mount point
303 * The continuation function. This is called by
304 * the task notifier when a background mount attempt
308 amfs_auto_cont(int rc, int term, voidp closure)
310 struct continuation *cp = (struct continuation *) closure;
311 mntfs *mf = cp->mp->am_mnt;
314 * Definitely not trying to mount at the moment
316 mf->mf_flags &= ~MFF_MOUNTING;
319 * While we are mounting - try to avoid race conditions
324 * Wakeup anything waiting for this mount
329 * Check for termination signal or exit status...
336 * Not sure what to do for an error code.
338 mf->mf_error = EIO; /* XXX ? */
339 mf->mf_flags |= MFF_ERROR;
340 plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
343 * Check for exit status...
346 mf->mf_flags |= MFF_ERROR;
347 errno = rc; /* XXX */
348 if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
349 plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
353 * If we get here then that attempt didn't work, so
354 * move the info vector pointer along by one and
355 * call the background mount routine again
360 (void) amfs_auto_bgmount(cp, 0);
361 assign_error_mntfs(xmp);
367 free_continuation(cp);
370 reschedule_timeout_mp();
378 amfs_auto_retry(int rc, int term, voidp closure)
380 struct continuation *cp = (struct continuation *) closure;
384 dlog("Commencing retry for mount of %s", cp->mp->am_path);
389 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
391 * The entire mount has timed out. Set the error code and skip past all
392 * the info vectors so that amfs_auto_bgmount will not have any more
393 * ways to try the mount, so causing an error.
395 plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
399 /* explicitly forbid further retries after timeout */
402 if (error || !IN_PROGRESS(cp)) {
403 (void) amfs_auto_bgmount(cp, error);
405 reschedule_timeout_mp();
410 * Try to mount a file system. Can be called
411 * directly or in a sub-process by run_task.
417 am_node *mp = (am_node *) mvp;
418 mntfs *mf = mp->am_mnt;
421 * If the directory is not yet made and it needs to be made, then make it!
422 * This may be run in a background process in which case the flag setting
423 * won't be noticed later - but it is set anyway just after run_task is
424 * called. It should probably go away totally...
426 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
427 error = mkdirs(mf->mf_mount, 0555);
429 mf->mf_flags |= MFF_MKMNT;
435 error = mount_node(mp);
440 dlog("amfs_auto call to mount_node failed: %m");
449 * Pick a file system to try mounting and
450 * do that in the background if necessary
453 if it is new -defaults then
458 if a location has been tried then
464 discard previous mount location if required
465 find matching mounted filesystem
466 if not applicable then
467 this_error = No such file or directory
470 if the filesystem failed to be mounted then
471 this_error = error from filesystem
472 elif the filesystem is mounting or unmounting then
474 elif the fileserver is down then
476 elif the filesystem is already mounted
480 if no error on this mount then
481 this_error = initialize mount point
483 if no error on this mount and mount is delayed then
486 if this_error < 0 then
489 if no error on this mount then
490 make mount point if required
492 if no error on this mount then
493 if mount in background then
494 run mount in background
497 this_error = mount in foreground
500 if an error occurred on this mount then
502 save error in mount point
507 amfs_auto_bgmount(struct continuation * cp, int mpe)
509 mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
510 mntfs *mf_retry = 0; /* First mntfs which needed retrying */
511 int this_error = -1; /* Per-mount error */
516 * Try to mount each location.
518 * hard_error == 0 indicates something was mounted.
519 * hard_error > 0 indicates everything failed with a hard error
520 * hard_error < 0 indicates nothing could be mounted now
522 for (; this_error && *cp->ivec; cp->ivec++) {
524 am_node *mp = cp->mp;
529 hard_error = this_error;
533 if (**cp->ivec == '-') {
535 * Pick up new defaults
537 if (cp->auto_opts && *cp->auto_opts)
538 cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
540 cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
542 dlog("Setting def_opts to \"%s\"", cp->def_opts);
547 * If a mount has been attempted, and we find
548 * a cut then don't try any more locations.
550 if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
553 dlog("Cut: not trying any more locations for %s",
561 /* match the operators */
562 p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
565 * Find a mounted filesystem for this node.
567 mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
571 cp->fs_opts.opt_opts,
572 cp->fs_opts.opt_remopts);
576 dlog("Got a hit with %s", p->fs_type);
580 * Note whether this is a real mount attempt
582 if (p == &amfs_error_ops) {
583 plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
588 if (cp->fs_opts.fs_mtab) {
589 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
590 cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
602 link_dir = mf->mf_fo->opt_sublink;
604 if (link_dir && *link_dir) {
605 if (*link_dir == '/') {
606 mp->am_link = strdup(link_dir);
609 * try getting fs option from continuation, not mountpoint!
610 * Don't try logging the string from mf, since it may be bad!
612 if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
613 plog(XLOG_ERROR, "use %s instead of 0x%lx",
614 cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs);
616 mp->am_link = str3cat((char *) 0,
617 cp->fs_opts.opt_fs, "/", link_dir);
619 normalize_slash(mp->am_link);
623 if (mf->mf_error > 0) {
624 this_error = mf->mf_error;
625 } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
627 * Still mounting - retry later
630 dlog("Duplicate pending mount fstype %s", p->fs_type);
633 } else if (FSRV_ISDOWN(mf->mf_server)) {
635 * Would just mount from the same place
636 * as a hung mount - so give up
639 dlog("%s is already hung - giving up", mf->mf_mount);
641 mp_error = EWOULDBLOCK;
644 } else if (mf->mf_flags & MFF_MOUNTED) {
646 dlog("duplicate mount of \"%s\" ...", mf->mf_info);
650 * Just call mounted()
659 * Will usually need to play around with the mount nodes
660 * file attribute structure. This must be done here.
661 * Try and get things initialized, even if the fileserver
662 * is not known to be up. In the common case this will
663 * progress things faster.
667 * Fill in attribute fields.
669 if (mf->mf_ops->fs_flags & FS_DIRECTORY)
674 mp->am_fattr.na_fileid = mp->am_gen;
677 this_error = (*p->fs_init) (mf);
681 * Make sure the fileserver is UP before doing any more work
683 if (!FSRV_ISUP(mf->mf_server)) {
685 dlog("waiting for server %s to become available", mf->mf_server->fs_host);
690 if (!this_error && mf->mf_fo->opt_delay) {
692 * If there is a delay timer on the mount
693 * then don't try to mount if the timer
696 int i = atoi(mf->mf_fo->opt_delay);
697 if (i > 0 && clocktime() < (cp->start + i)) {
699 dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start));
705 if (this_error < 0 && !dont_retry) {
707 mf_retry = dup_mntfs(mf);
710 dlog("will retry ...\n");
716 if (p->fs_flags & FS_MBACKGROUND) {
717 mf->mf_flags |= MFF_MOUNTING; /* XXX */
719 dlog("backgrounding mount of \"%s\"", mf->mf_mount);
722 untimeout(cp->callout);
725 run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
726 mf->mf_flags |= MFF_MKMNT; /* XXX */
728 free_mntfs(mf_retry);
732 dlog("foreground mount of \"%s\" ...", mf->mf_info);
734 this_error = try_mount((voidp) mp);
735 if (this_error < 0) {
737 mf_retry = dup_mntfs(mf);
743 if (this_error >= 0) {
744 if (this_error > 0) {
746 if (mf != mf_retry) {
747 mf->mf_error = this_error;
748 mf->mf_flags |= MFF_ERROR;
753 * Wakeup anything waiting for this mount
759 if (this_error && cp->retry) {
761 mf = cp->mp->am_mnt = mf_retry;
763 * Not retrying again (so far)
768 * Start at the beginning.
769 * Rewind the location vector and
770 * reset the default options.
773 dlog("(skipping rewind)\n");
776 * Arrange that amfs_auto_bgmount is called
777 * after anything else happens.
780 dlog("Arranging to retry mount of %s", cp->mp->am_path);
782 sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
784 untimeout(cp->callout);
785 cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
787 cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
790 * Not done yet - so don't return anything
795 if (hard_error < 0 || this_error == 0)
796 hard_error = this_error;
799 * Discard handle on duff filesystem.
800 * This should never happen since it
801 * should be caught by the case above.
805 plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
806 free_mntfs(mf_retry);
810 * If we get here, then either the mount succeeded or
811 * there is no more mount information available.
813 if (hard_error < 0 && mp_error)
814 hard_error = cp->mp->am_error = mp_error;
815 if (hard_error > 0) {
817 * Set a small(ish) timeout on an error node if
818 * the error was not a time out.
820 switch (hard_error) {
823 cp->mp->am_timeo = 17;
826 cp->mp->am_timeo = 5;
833 * Make sure that the error value in the mntfs has a
836 if (mf->mf_error < 0) {
837 mf->mf_error = hard_error;
839 mf->mf_flags |= MFF_ERROR;
843 * In any case we don't need the continuation any more
845 free_continuation(cp);
852 * Automount interface to RPC lookup routine
853 * Find the corresponding entry and return
854 * the file handle for it.
857 amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
859 am_node *ap, *new_mp, *ap_hung;
860 char *info; /* Mount info - where to get the file system */
861 char **ivec, **xivec; /* Split version of info */
862 char *auto_opts; /* Automount options */
863 int error = 0; /* Error so far */
864 char path_name[MAXPATHLEN]; /* General path name buffer */
865 char *pfname; /* Path for database lookup */
866 struct continuation *cp; /* Continuation structure if need to mount */
867 int in_progress = 0; /* # of (un)mount in progress */
872 dlog("in amfs_auto_lookuppn");
876 * If the server is shutting down
877 * then don't return information
878 * about the mount point.
880 if (amd_state == Finishing) {
882 if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
883 dlog("%s mount ignored - going down", fname);
885 dlog("%s/%s mount ignored - going down", mp->am_path, fname);
892 * Handle special case of "." and ".."
894 if (fname[0] == '.') {
895 if (fname[1] == '\0')
896 return mp; /* "." is the current node */
897 if (fname[1] == '.' && fname[2] == '\0') {
900 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
902 return mp->am_parent; /* ".." is the parent node */
909 * Check for valid key name.
910 * If it is invalid then pretend it doesn't exist.
912 if (!valid_key(fname)) {
913 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
919 * fname is now a private copy.
921 fname = expand_key(fname);
923 for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
925 * Otherwise search children of this node
927 if (FSTREQ(ap->am_name, fname)) {
930 error = ap->am_error;
934 * If the error code is undefined then it must be
937 if (mf->mf_error < 0)
941 * Check for a hung node
943 if (FSRV_ISDOWN(mf->mf_server)) {
947 error = ap->am_error;
952 * If there was a previous error with this node
953 * then return that error code.
955 if (mf->mf_flags & MFF_ERROR) {
956 error = mf->mf_error;
959 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
962 * If the fs is not mounted or it is unmounting then there
963 * is a background (un)mount in progress. In this case
964 * we just drop the RPC request (return nil) and
965 * wait for a retry, by which time the (un)mount may
969 dlog("ignoring mount of %s in %s -- flags (%x) in progress",
970 fname, mf->mf_mount, mf->mf_flags);
977 * Otherwise we have a hit: return the current mount point.
980 dlog("matched %s in %s", fname, ap->am_path);
989 dlog("Waiting while %d mount(s) in progress", in_progress);
996 * If an error occurred then return it.
1000 errno = error; /* XXX */
1001 dlog("Returning error: %m");
1008 * If doing a delete then don't create again!
1018 plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
1023 * If the server is going down then just return,
1024 * don't try to mount any more file systems
1026 if ((int) amd_state >= (int) Finishing) {
1028 dlog("not found - server going down anyway");
1035 * If we get there then this is a reference to an,
1036 * as yet, unknown name so we need to search the mount
1040 sprintf(path_name, "%s%s", mp->am_pref, fname);
1049 dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1052 * Consult the oracle for some mount information.
1053 * info is malloc'ed and belongs to this routine.
1054 * It ends up being free'd in free_continuation().
1056 * Note that this may return -1 indicating that information
1057 * is not yet available.
1059 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
1062 plog(XLOG_MAP, "No map entry for %s", pfname);
1064 plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
1069 dlog("mount info is %s", info);
1073 * Split info into an argument vector.
1074 * The vector is malloc'ed and belongs to
1075 * this routine. It is free'd in free_continuation()
1077 xivec = ivec = strsplit(info, ' ', '\"');
1080 * Default error code...
1083 error = EWOULDBLOCK;
1088 * Allocate a new map
1090 new_mp = exported_ap_alloc();
1098 auto_opts = mf->mf_auto;
1102 auto_opts = strdup(auto_opts);
1105 dlog("searching for /defaults entry");
1107 if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
1111 dlog("/defaults gave %s", dflts);
1119 * Chop the defaults up
1121 rvec = strsplit(dfl, ' ', '\"');
1123 if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
1125 * Pick whichever first entry matched the list of selectors.
1126 * Strip the selectors from the string, and assign to dfl the
1127 * rest of the string.
1133 while (*sp) { /* loop until you find something, if any */
1134 memset((char *) &ap, 0, sizeof(am_opts));
1135 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1136 mp->am_parent->am_mnt->mf_info);
1137 free_opts(&ap); /* don't leak */
1138 if (pt == &amfs_error_ops) {
1139 plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
1141 dfl = strip_selectors(*sp, "/defaults");
1142 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1148 } else { /* not enable_default_selectors */
1150 * Extract first value
1156 * If there were any values at all...
1160 * Log error if there were other values
1162 if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
1164 dlog("/defaults chopped into %s", dfl);
1166 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1170 * Prepend to existing defaults if they exist,
1171 * otherwise just use these defaults.
1173 if (*auto_opts && *dfl) {
1174 char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
1175 sprintf(nopts, "%s;%s", dfl, auto_opts);
1179 auto_opts = strealloc(auto_opts, dfl);
1184 * Don't need info vector any more
1192 init_map(new_mp, fname);
1195 * Put it in the table
1197 insert_am(new_mp, mp);
1200 * Fill in some other fields,
1201 * path and mount point.
1203 * bugfix: do not prepend old am_path if direct map
1204 * <wls@astro.umd.edu> William Sebok
1206 new_mp->am_path = str3cat(new_mp->am_path,
1207 mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
1208 *fname == '/' ? "" : "/", fname);
1211 dlog("setting path to %s", new_mp->am_path);
1215 * Take private copy of pfname
1217 pfname = strdup(pfname);
1220 * Construct a continuation
1222 cp = ALLOC(struct continuation);
1229 cp->auto_opts = auto_opts;
1232 cp->start = clocktime();
1233 cp->def_opts = strdup(auto_opts);
1234 memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
1237 * Try and mount the file system. If this succeeds immediately (possible
1238 * for a ufs file system) then return the attributes, otherwise just
1241 error = amfs_auto_bgmount(cp, error);
1242 reschedule_timeout_mp();
1249 * Code for quick reply. If nfs_program_2_transp is set, then
1250 * its the transp that's been passed down from nfs_program_2().
1251 * If new_mp->am_transp is not already set, set it by copying in
1252 * nfs_program_2_transp. Once am_transp is set, quick_reply() can
1253 * use it to send a reply to the client that requested this mount.
1255 if (nfs_program_2_transp && !new_mp->am_transp) {
1256 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1257 *(new_mp->am_transp) = *nfs_program_2_transp;
1259 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1260 new_mp->am_error = error;
1262 assign_error_mntfs(new_mp);
1271 * Locate next node in sibling list which is mounted
1272 * and is not an error node.
1275 next_nonerror_node(am_node *xp)
1280 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
1281 * Fixes a race condition when mounting direct automounts.
1282 * Also fixes a problem when doing a readdir on a directory
1283 * containing hung automounts.
1286 (!(mf = xp->am_mnt) || /* No mounted filesystem */
1287 mf->mf_error != 0 || /* There was a mntfs error */
1288 xp->am_error != 0 || /* There was a mount error */
1289 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
1290 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
1299 * This readdir function which call a special version of it that allows
1300 * browsing if browsable_dirs=yes was set on the map.
1303 amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
1305 u_int gen = *(u_int *) cookie;
1309 dp->dl_eof = FALSE; /* assume readdir not done */
1311 /* check if map is browsable */
1312 if (mp->am_mnt && mp->am_mnt->mf_mopts) {
1313 mnt.mnt_opts = mp->am_mnt->mf_mopts;
1314 if (hasmntopt(&mnt, "fullybrowsable"))
1315 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
1316 if (hasmntopt(&mnt, "browsable"))
1317 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
1322 * In the default instance (which is used to start a search) we return
1325 * This assumes that the count is big enough to allow both "." and ".."
1326 * to be returned in a single packet. If it isn't (which would be
1327 * fairly unbelievable) then tough.
1330 dlog("amfs_auto_readdir: default search");
1333 * Check for enough room. This is extremely approximate but is more
1334 * than enough space. Really need 2 times:
1339 * plus the dirlist structure */
1340 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1343 xp = next_nonerror_node(mp->am_child);
1344 dp->dl_entries = ep;
1347 ep[0].ne_fileid = mp->am_gen;
1348 ep[0].ne_name = ".";
1349 ep[0].ne_nextentry = &ep[1];
1350 *(u_int *) ep[0].ne_cookie = 0;
1352 /* construct ".." */
1354 ep[1].ne_fileid = mp->am_parent->am_gen;
1356 ep[1].ne_fileid = mp->am_gen;
1357 ep[1].ne_name = "..";
1358 ep[1].ne_nextentry = 0;
1359 *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE);
1362 dp->dl_eof = TRUE; /* by default assume readdir done */
1367 dlog("amfs_auto_readdir: real child");
1370 if (gen == DOT_DOT_COOKIE) {
1372 dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path);
1379 /* non-browsable directories code */
1381 while (xp && xp->am_gen != gen)
1385 int nbytes = count / 2; /* conservative */
1386 int todo = MAX_READDIR_ENTRIES;
1388 dp->dl_entries = ep;
1390 am_node *xp_next = next_nonerror_node(xp->am_osib);
1393 *(u_int *) ep->ne_cookie = xp_next->am_gen;
1395 *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE;
1399 ep->ne_fileid = xp->am_gen;
1400 ep->ne_name = xp->am_name;
1401 nbytes -= sizeof(*ep) + 1;
1403 nbytes -= strlen(xp->am_name);
1407 if (nbytes > 0 && !dp->dl_eof && todo > 1) {
1408 ep->ne_nextentry = ep + 1;
1416 ep->ne_nextentry = 0;
1424 /* This one is called only if map is browsable */
1426 amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
1428 u_int gen = *(u_int *) cookie;
1429 int chain_length, i;
1430 static nfsentry *te, *te_next;
1431 #ifdef DEBUG_READDIR
1434 #endif /* DEBUG_READDIR */
1436 dp->dl_eof = FALSE; /* assume readdir not done */
1438 #ifdef DEBUG_READDIR
1439 plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
1441 #endif /* DEBUG_READDIR */
1445 * In the default instance (which is used to start a search) we return
1448 * This assumes that the count is big enough to allow both "." and ".."
1449 * to be returned in a single packet. If it isn't (which would be
1450 * fairly unbelievable) then tough.
1453 dlog("amfs_auto_readdir_browsable: default search");
1456 * Check for enough room. This is extremely approximate but is more
1457 * than enough space. Really need 2 times:
1462 * plus the dirlist structure */
1463 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1467 * compute # of entries to send in this chain.
1468 * heuristics: 128 bytes per entry.
1469 * This is too much probably, but it seems to work better because
1470 * of the re-entrant nature of nfs_readdir, and esp. on systems
1473 chain_length = count / 128;
1475 /* reset static state counters */
1476 te = te_next = NULL;
1478 dp->dl_entries = ep;
1481 ep[0].ne_fileid = mp->am_gen;
1482 ep[0].ne_name = ".";
1483 ep[0].ne_nextentry = &ep[1];
1484 *(u_int *) ep[0].ne_cookie = 0;
1486 /* construct ".." */
1488 ep[1].ne_fileid = mp->am_parent->am_gen;
1490 ep[1].ne_fileid = mp->am_gen;
1491 ep[1].ne_name = "..";
1492 ep[1].ne_nextentry = 0;
1493 *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE;
1496 * If map is browsable, call a function make_entry_chain() to construct
1497 * a linked list of unmounted keys, and return it. Then link the chain
1498 * to the regular list. Get the chain only once, but return
1499 * chunks of it each time.
1501 te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
1504 #ifdef DEBUG_READDIR
1505 for (j=0,ne=te; ne; ne=ne->ne_nextentry)
1506 plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
1507 #endif /* DEBUG_READDIR */
1509 /* return only "chain_length" entries */
1511 for (i=1; i<chain_length; ++i) {
1512 te_next = te_next->ne_nextentry;
1517 nfsentry *te_saved = te_next->ne_nextentry;
1518 te_next->ne_nextentry = NULL; /* terminate "te" chain */
1519 te_next = te_saved; /* save rest of "te" for next iteration */
1520 dp->dl_eof = FALSE; /* tell readdir there's more */
1522 dp->dl_eof = TRUE; /* tell readdir that's it */
1524 ep[1].ne_nextentry = te; /* append this chunk of "te" chain */
1525 #ifdef DEBUG_READDIR
1526 for (j=0,ne=te; ne; ne=ne->ne_nextentry)
1527 plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
1528 for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
1529 plog(XLOG_INFO, "gen2+ key %4d \"%s\" fi=%d ck=%d",
1530 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
1531 plog(XLOG_INFO, "EOF is %d", dp->dl_eof);
1532 #endif /* DEBUG_READDIR */
1534 } /* end of "if (gen == 0)" statement */
1537 dlog("amfs_auto_readdir_browsable: real child");
1540 if (gen == DOT_DOT_COOKIE) {
1542 dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path);
1550 * If browsable directories, then continue serving readdir() with another
1551 * chunk of entries, starting from where we left off (when gen was equal
1552 * to 0). Once again, assume last chunk served to readdir.
1555 dp->dl_entries = ep;
1557 te = te_next; /* reset 'te' from last saved te_next */
1558 if (!te) { /* another indicator of end of readdir */
1563 * compute # of entries to send in this chain.
1564 * heuristics: 128 bytes per entry.
1566 chain_length = count / 128;
1568 /* return only "chain_length" entries */
1569 for (i=1; i<chain_length; ++i) {
1570 te_next = te_next->ne_nextentry;
1575 nfsentry *te_saved = te_next->ne_nextentry;
1576 te_next->ne_nextentry = NULL; /* terminate "te" chain */
1577 te_next = te_saved; /* save rest of "te" for next iteration */
1578 dp->dl_eof = FALSE; /* tell readdir there's more */
1580 ep = te; /* send next chunk of "te" chain */
1581 dp->dl_entries = ep;
1582 #ifdef DEBUG_READDIR
1583 plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
1584 (int) dp->dl_entries, (int) te_next, dp->dl_eof);
1585 for (ne=te; ne; ne=ne->ne_nextentry)
1586 plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
1587 #endif /* DEBUG_READDIR */
1593 amfs_auto_fmount(am_node *mp)
1595 mntfs *mf = mp->am_mnt;
1596 return (*mf->mf_ops->fmount_fs) (mf);
1601 amfs_auto_fumount(am_node *mp)
1603 mntfs *mf = mp->am_mnt;
1604 return (*mf->mf_ops->fumount_fs) (mf);