2 * Copyright (c) 1997-1998 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 acknowledgement:
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.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
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 /****************************************************************************
65 ****************************************************************************/
69 /****************************************************************************
70 *** FORWARD DEFINITIONS ***
71 ****************************************************************************/
72 static int amfs_auto_bgmount(struct continuation * cp, int mpe);
73 static int amfs_auto_mount(am_node *mp);
74 static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
75 static void amfs_auto_umounted(am_node *mp);
78 /****************************************************************************
79 *** OPS STRUCTURES ***
80 ****************************************************************************/
81 am_ops amfs_auto_ops =
85 0, /* amfs_auto_init */
92 0, /* amfs_auto_readlink */
93 0, /* amfs_auto_mounted */
96 FS_AMQINFO | FS_DIRECTORY
100 /****************************************************************************
102 ****************************************************************************/
104 * AMFS_AUTO needs nothing in particular.
107 amfs_auto_match(am_opts *fo)
109 char *p = fo->opt_rfs;
112 plog(XLOG_USER, "auto: no mount point named (rfs:=)");
116 plog(XLOG_USER, "auto: no map named (fs:=)");
121 * Swap round fs:= and rfs:= options
122 * ... historical (jsp)
124 fo->opt_rfs = fo->opt_fs;
128 * mtab entry turns out to be the name of the mount map
130 return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
137 * Build a new map cache for this node, or re-use
138 * an existing cache for the same map.
141 amfs_auto_mkcacheref(mntfs *mf)
145 if (mf->mf_fo && mf->mf_fo->opt_cache)
146 cache = mf->mf_fo->opt_cache;
149 mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
150 mf->mf_fo->opt_maptype);
151 mf->mf_prfree = mapc_free;
159 amfs_auto_mount(am_node *mp)
161 mntfs *mf = mp->am_mnt;
164 * Pseudo-directories are used to provide some structure
165 * to the automounted directories instead
166 * of putting them all in the top-level automount directory.
168 * Here, just increment the parent's link count.
170 mp->am_parent->am_fattr.na_nlink++;
173 * Info field of . means use parent's info field.
174 * Historical - not documented.
176 if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
177 mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
182 * If there is an option prefix then use that else
183 * If the parent had a prefix then use that with name
184 * of this node appended else
185 * Use the name of this node.
187 * That means if you want no prefix you must say so
190 if (mf->mf_fo->opt_pref) {
191 /* allow pref:=null to set a real null prefix */
192 if (STREQ(mf->mf_fo->opt_pref, "null")) {
196 * the prefix specified as an option
198 mp->am_pref = strdup(mf->mf_fo->opt_pref);
202 * else the parent's prefix
203 * followed by the name
206 char *ppref = mp->am_parent->am_pref;
209 mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
215 amfs_auto_mkcacheref(mf);
224 * Unmount an automount sub-node
227 amfs_auto_umount(am_node *mp)
234 * Unmount an automount node
237 amfs_auto_umounted(am_node *mp)
240 * If this is a pseudo-directory then just adjust the link count
241 * in the parent, otherwise call the generic unmount routine
243 if (mp->am_parent && mp->am_parent->am_parent)
244 --mp->am_parent->am_fattr.na_nlink;
249 * Discard an old continuation
252 free_continuation(struct continuation *cp)
255 untimeout(cp->callout);
259 XFREE(cp->auto_opts);
261 free_opts(&cp->fs_opts);
267 * Discard the underlying mount point and replace
268 * with a reference to an error filesystem.
271 assign_error_mntfs(am_node *mp)
273 if (mp->am_error > 0) {
275 * Save the old error code
277 int error = mp->am_error;
279 error = mp->am_mnt->mf_error;
281 * Discard the old filesystem
283 free_mntfs(mp->am_mnt);
285 * Allocate a new error reference
287 mp->am_mnt = new_mntfs();
289 * Put back the error code
291 mp->am_mnt->mf_error = error;
292 mp->am_mnt->mf_flags |= MFF_ERROR;
294 * Zero the error in the mount point
302 * The continuation function. This is called by
303 * the task notifier when a background mount attempt
307 amfs_auto_cont(int rc, int term, voidp closure)
309 struct continuation *cp = (struct continuation *) closure;
310 mntfs *mf = cp->mp->am_mnt;
313 * Definitely not trying to mount at the moment
315 mf->mf_flags &= ~MFF_MOUNTING;
318 * While we are mounting - try to avoid race conditions
323 * Wakeup anything waiting for this mount
328 * Check for termination signal or exit status...
335 * Not sure what to do for an error code.
337 mf->mf_error = EIO; /* XXX ? */
338 mf->mf_flags |= MFF_ERROR;
339 plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
342 * Check for exit status...
345 mf->mf_flags |= MFF_ERROR;
346 errno = rc; /* XXX */
347 if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
348 plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
352 * If we get here then that attempt didn't work, so
353 * move the info vector pointer along by one and
354 * call the background mount routine again
359 (void) amfs_auto_bgmount(cp, 0);
360 assign_error_mntfs(xmp);
366 free_continuation(cp);
369 reschedule_timeout_mp();
377 amfs_auto_retry(int rc, int term, voidp closure)
379 struct continuation *cp = (struct continuation *) closure;
383 dlog("Commencing retry for mount of %s", cp->mp->am_path);
388 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
390 * The entire mount has timed out. Set the error code and skip past all
391 * the info vectors so that amfs_auto_bgmount will not have any more
392 * ways to try the mount, so causing an error.
394 plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
398 /* explicitly forbid further retries after timeout */
401 if (error || !IN_PROGRESS(cp)) {
402 (void) amfs_auto_bgmount(cp, error);
404 reschedule_timeout_mp();
409 * Try to mount a file system. Can be called
410 * directly or in a sub-process by run_task.
416 am_node *mp = (am_node *) mvp;
417 mntfs *mf = mp->am_mnt;
420 * If the directory is not yet made and it needs to be made, then make it!
421 * This may be run in a background process in which case the flag setting
422 * won't be noticed later - but it is set anyway just after run_task is
423 * called. It should probably go away totally...
425 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
426 error = mkdirs(mf->mf_mount, 0555);
428 mf->mf_flags |= MFF_MKMNT;
434 error = mount_node(mp);
439 dlog("amfs_auto call to mount_node failed: %m");
448 * Pick a file system to try mounting and
449 * do that in the background if necessary
452 if it is new -defaults then
457 if a location has been tried then
463 discard previous mount location if required
464 find matching mounted filesystem
465 if not applicable then
466 this_error = No such file or directory
469 if the filesystem failed to be mounted then
470 this_error = error from filesystem
471 elif the filesystem is mounting or unmounting then
473 elif the fileserver is down then
475 elif the filesystem is already mounted
479 if no error on this mount then
480 this_error = initialise mount point
482 if no error on this mount and mount is delayed then
485 if this_error < 0 then
488 if no error on this mount then
489 make mount point if required
491 if no error on this mount then
492 if mount in background then
493 run mount in background
496 this_error = mount in foreground
499 if an error occured on this mount then
501 save error in mount point
506 amfs_auto_bgmount(struct continuation * cp, int mpe)
508 mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
509 mntfs *mf_retry = 0; /* First mntfs which needed retrying */
510 int this_error = -1; /* Per-mount error */
515 * Try to mount each location.
517 * hard_error == 0 indicates something was mounted.
518 * hard_error > 0 indicates everything failed with a hard error
519 * hard_error < 0 indicates nothing could be mounted now
521 for (; this_error && *cp->ivec; cp->ivec++) {
523 am_node *mp = cp->mp;
528 hard_error = this_error;
532 if (**cp->ivec == '-') {
534 * Pick up new defaults
536 if (cp->auto_opts && *cp->auto_opts)
537 cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
539 cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
541 dlog("Setting def_opts to \"%s\"", cp->def_opts);
546 * If a mount has been attempted, and we find
547 * a cut then don't try any more locations.
549 if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
552 dlog("Cut: not trying any more locations for %s",
560 /* match the operators */
561 p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
564 * Find a mounted filesystem for this node.
566 mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
570 cp->fs_opts.opt_opts,
571 cp->fs_opts.opt_remopts);
575 dlog("Got a hit with %s", p->fs_type);
579 * Note whether this is a real mount attempt
581 if (p == &amfs_error_ops) {
582 plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
587 if (cp->fs_opts.fs_mtab) {
588 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
589 cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
601 link_dir = mf->mf_fo->opt_sublink;
603 if (link_dir && *link_dir) {
604 if (*link_dir == '/') {
605 mp->am_link = strdup(link_dir);
608 * try getting fs option from continuation, not mountpoint!
609 * Don't try logging the string from mf, since it may be bad!
611 if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
612 plog(XLOG_ERROR, "use %s instead of 0x%x",
613 cp->fs_opts.opt_fs, mf->mf_fo->opt_fs);
615 mp->am_link = str3cat((char *) 0,
616 cp->fs_opts.opt_fs, "/", link_dir);
618 normalize_slash(mp->am_link);
622 if (mf->mf_error > 0) {
623 this_error = mf->mf_error;
624 } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
626 * Still mounting - retry later
629 dlog("Duplicate pending mount fstype %s", p->fs_type);
632 } else if (FSRV_ISDOWN(mf->mf_server)) {
634 * Would just mount from the same place
635 * as a hung mount - so give up
638 dlog("%s is already hung - giving up", mf->mf_mount);
640 mp_error = EWOULDBLOCK;
643 } else if (mf->mf_flags & MFF_MOUNTED) {
645 dlog("duplicate mount of \"%s\" ...", mf->mf_info);
649 * Just call mounted()
658 * Will usually need to play around with the mount nodes
659 * file attribute structure. This must be done here.
660 * Try and get things initialised, even if the fileserver
661 * is not known to be up. In the common case this will
662 * progress things faster.
666 * Fill in attribute fields.
668 if (mf->mf_ops->fs_flags & FS_DIRECTORY)
673 mp->am_fattr.na_fileid = mp->am_gen;
676 this_error = (*p->fs_init) (mf);
680 * Make sure the fileserver is UP before doing any more work
682 if (!FSRV_ISUP(mf->mf_server)) {
684 dlog("waiting for server %s to become available", mf->mf_server->fs_host);
689 if (!this_error && mf->mf_fo->opt_delay) {
691 * If there is a delay timer on the mount
692 * then don't try to mount if the timer
695 int i = atoi(mf->mf_fo->opt_delay);
696 if (i > 0 && clocktime() < (cp->start + i)) {
698 dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
704 if (this_error < 0 && !dont_retry) {
706 mf_retry = dup_mntfs(mf);
711 if (p->fs_flags & FS_MBACKGROUND) {
712 mf->mf_flags |= MFF_MOUNTING; /* XXX */
714 dlog("backgrounding mount of \"%s\"", mf->mf_mount);
717 untimeout(cp->callout);
720 run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
721 mf->mf_flags |= MFF_MKMNT; /* XXX */
723 free_mntfs(mf_retry);
727 dlog("foreground mount of \"%s\" ...", mf->mf_info);
729 this_error = try_mount((voidp) mp);
730 if (this_error < 0) {
732 mf_retry = dup_mntfs(mf);
738 if (this_error >= 0) {
739 if (this_error > 0) {
741 if (mf != mf_retry) {
742 mf->mf_error = this_error;
743 mf->mf_flags |= MFF_ERROR;
748 * Wakeup anything waiting for this mount
754 if (this_error && cp->retry) {
756 mf = cp->mp->am_mnt = mf_retry;
758 * Not retrying again (so far)
763 * Start at the beginning.
764 * Rewind the location vector and
765 * reset the default options.
767 cp->ivec = cp->xivec;
768 cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
770 * Arrange that amfs_auto_bgmount is called
771 * after anything else happens.
774 dlog("Arranging to retry mount of %s", cp->mp->am_path);
776 sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
778 untimeout(cp->callout);
779 cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
781 cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
784 * Not done yet - so don't return anything
789 if (hard_error < 0 || this_error == 0)
790 hard_error = this_error;
793 * Discard handle on duff filesystem.
794 * This should never happen since it
795 * should be caught by the case above.
799 plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
800 free_mntfs(mf_retry);
804 * If we get here, then either the mount succeeded or
805 * there is no more mount information available.
807 if (hard_error < 0 && mp_error)
808 hard_error = cp->mp->am_error = mp_error;
809 if (hard_error > 0) {
811 * Set a small(ish) timeout on an error node if
812 * the error was not a time out.
814 switch (hard_error) {
817 cp->mp->am_timeo = 17;
820 cp->mp->am_timeo = 5;
827 * Make sure that the error value in the mntfs has a
830 if (mf->mf_error < 0) {
831 mf->mf_error = hard_error;
833 mf->mf_flags |= MFF_ERROR;
837 * In any case we don't need the continuation any more
839 free_continuation(cp);
846 * Automount interface to RPC lookup routine
847 * Find the corresponding entry and return
848 * the file handle for it.
851 amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
853 am_node *ap, *new_mp, *ap_hung;
854 char *info; /* Mount info - where to get the file system */
855 char **ivec, **xivec; /* Split version of info */
856 char *auto_opts; /* Automount options */
857 int error = 0; /* Error so far */
858 char path_name[MAXPATHLEN]; /* General path name buffer */
859 char *pfname; /* Path for database lookup */
860 struct continuation *cp; /* Continuation structure if need to mount */
861 int in_progress = 0; /* # of (un)mount in progress */
866 dlog("in amfs_auto_lookuppn");
870 * If the server is shutting down
871 * then don't return information
872 * about the mount point.
874 if (amd_state == Finishing) {
876 if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
877 dlog("%s mount ignored - going down", fname);
879 dlog("%s/%s mount ignored - going down", mp->am_path, fname);
886 * Handle special case of "." and ".."
888 if (fname[0] == '.') {
889 if (fname[1] == '\0')
890 return mp; /* "." is the current node */
891 if (fname[1] == '.' && fname[2] == '\0') {
894 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
896 return mp->am_parent; /* ".." is the parent node */
903 * Check for valid key name.
904 * If it is invalid then pretend it doesn't exist.
906 if (!valid_key(fname)) {
907 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
913 * fname is now a private copy.
915 fname = expand_key(fname);
917 for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
919 * Otherwise search children of this node
921 if (FSTREQ(ap->am_name, fname)) {
924 error = ap->am_error;
928 * If the error code is undefined then it must be
931 if (mf->mf_error < 0)
935 * Check for a hung node
937 if (FSRV_ISDOWN(mf->mf_server)) {
941 error = ap->am_error;
946 * If there was a previous error with this node
947 * then return that error code.
949 if (mf->mf_flags & MFF_ERROR) {
950 error = mf->mf_error;
953 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
956 * If the fs is not mounted or it is unmounting then there
957 * is a background (un)mount in progress. In this case
958 * we just drop the RPC request (return nil) and
959 * wait for a retry, by which time the (un)mount may
963 dlog("ignoring mount of %s in %s -- flags (%x) in progress",
964 fname, mf->mf_mount, mf->mf_flags);
971 * Otherwise we have a hit: return the current mount point.
974 dlog("matched %s in %s", fname, ap->am_path);
983 dlog("Waiting while %d mount(s) in progress", in_progress);
990 * If an error occured then return it.
994 errno = error; /* XXX */
995 dlog("Returning error: %m", error);
1002 * If doing a delete then don't create again!
1012 plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
1017 * If the server is going down then just return,
1018 * don't try to mount any more file systems
1020 if ((int) amd_state >= (int) Finishing) {
1022 dlog("not found - server going down anyway");
1029 * If we get there then this is a reference to an,
1030 * as yet, unknown name so we need to search the mount
1034 sprintf(path_name, "%s%s", mp->am_pref, fname);
1043 dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1046 * Consult the oracle for some mount information.
1047 * info is malloc'ed and belongs to this routine.
1048 * It ends up being free'd in free_continuation().
1050 * Note that this may return -1 indicating that information
1051 * is not yet available.
1053 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
1056 plog(XLOG_MAP, "No map entry for %s", pfname);
1058 plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
1063 dlog("mount info is %s", info);
1067 * Split info into an argument vector.
1068 * The vector is malloc'ed and belongs to
1069 * this routine. It is free'd in free_continuation()
1071 xivec = ivec = strsplit(info, ' ', '\"');
1074 * Default error code...
1077 error = EWOULDBLOCK;
1082 * Allocate a new map
1084 new_mp = exported_ap_alloc();
1092 auto_opts = mf->mf_auto;
1096 auto_opts = strdup(auto_opts);
1099 dlog("searching for /defaults entry");
1101 if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
1105 dlog("/defaults gave %s", dflts);
1113 * Chop the defaults up
1115 rvec = strsplit(dfl, ' ', '\"');
1117 if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
1119 * Pick whichever first entry matched the list of selectors.
1120 * Strip the selectors from the string, and assign to dfl the
1121 * rest of the string.
1127 while (*sp) { /* loop until you find something, if any */
1128 memset((char *) &ap, 0, sizeof(am_opts));
1129 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1130 mp->am_parent->am_mnt->mf_info);
1131 free_opts(&ap); /* don't leak */
1132 if (pt == &amfs_error_ops) {
1133 plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
1135 dfl = strip_selectors(*sp, "/defaults");
1136 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1142 } else { /* not enable_default_selectors */
1144 * Extract first value
1150 * If there were any values at all...
1154 * Log error if there were other values
1156 if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
1158 dlog("/defaults chopped into %s", dfl);
1160 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1164 * Prepend to existing defaults if they exist,
1165 * otherwise just use these defaults.
1167 if (*auto_opts && *dfl) {
1168 char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
1169 sprintf(nopts, "%s;%s", dfl, auto_opts);
1173 auto_opts = strealloc(auto_opts, dfl);
1178 * Don't need info vector any more
1186 init_map(new_mp, fname);
1189 * Put it in the table
1191 insert_am(new_mp, mp);
1194 * Fill in some other fields,
1195 * path and mount point.
1197 * bugfix: do not prepend old am_path if direct map
1198 * <wls@astro.umd.edu> William Sebok
1200 new_mp->am_path = str3cat(new_mp->am_path,
1201 mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
1202 *fname == '/' ? "" : "/", fname);
1205 dlog("setting path to %s", new_mp->am_path);
1209 * Take private copy of pfname
1211 pfname = strdup(pfname);
1214 * Construct a continuation
1216 cp = ALLOC(struct continuation);
1223 cp->auto_opts = auto_opts;
1226 cp->start = clocktime();
1227 cp->def_opts = strdup(auto_opts);
1228 memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
1231 * Try and mount the file system. If this succeeds immediately (possible
1232 * for a ufs file system) then return the attributes, otherwise just
1235 error = amfs_auto_bgmount(cp, error);
1236 reschedule_timeout_mp();
1243 * Code for quick reply. If nfs_program_2_transp is set, then
1244 * its the transp that's been passed down from nfs_program_2().
1245 * If new_mp->am_transp is not already set, set it by copying in
1246 * nfs_program_2_transp. Once am_transp is set, quick_reply() can
1247 * use it to send a reply to the client that requested this mount.
1249 if (nfs_program_2_transp && !new_mp->am_transp) {
1250 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1251 *(new_mp->am_transp) = *nfs_program_2_transp;
1253 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1254 new_mp->am_error = error;
1256 assign_error_mntfs(new_mp);
1265 * Locate next node in sibling list which is mounted
1266 * and is not an error node.
1269 next_nonerror_node(am_node *xp)
1274 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
1275 * Fixes a race condition when mounting direct automounts.
1276 * Also fixes a problem when doing a readdir on a directory
1277 * containing hung automounts.
1280 (!(mf = xp->am_mnt) || /* No mounted filesystem */
1281 mf->mf_error != 0 || /* There was a mntfs error */
1282 xp->am_error != 0 || /* There was a mount error */
1283 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
1284 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
1293 * This readdir function which call a special version of it that allows
1294 * browsing if browsable_dirs=yes was set on the map.
1297 amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
1299 u_int gen = *(u_int *) cookie;
1303 dp->dl_eof = FALSE; /* assume readdir not done */
1305 /* check if map is browsable */
1306 if (mp->am_mnt && mp->am_mnt->mf_mopts) {
1307 mnt.mnt_opts = mp->am_mnt->mf_mopts;
1308 if (hasmntopt(&mnt, "fullybrowsable"))
1309 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
1310 if (hasmntopt(&mnt, "browsable"))
1311 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
1316 * In the default instance (which is used to start a search) we return
1319 * This assumes that the count is big enough to allow both "." and ".."
1320 * to be returned in a single packet. If it isn't (which would be
1321 * fairly unbelievable) then tough.
1324 dlog("default search");
1327 * Check for enough room. This is extremely approximate but is more
1328 * than enough space. Really need 2 times:
1333 * plus the dirlist structure */
1334 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1337 xp = next_nonerror_node(mp->am_child);
1338 dp->dl_entries = ep;
1341 ep[0].ne_fileid = mp->am_gen;
1342 ep[0].ne_name = ".";
1343 ep[0].ne_nextentry = &ep[1];
1344 *(u_int *) ep[0].ne_cookie = 0;
1346 /* construct ".." */
1348 ep[1].ne_fileid = mp->am_parent->am_gen;
1350 ep[1].ne_fileid = mp->am_gen;
1351 ep[1].ne_name = "..";
1352 ep[1].ne_nextentry = 0;
1353 *(u_int *) ep[1].ne_cookie =
1354 xp ? xp->am_gen : ~(u_int) 0;
1357 dp->dl_eof = TRUE; /* by default assume readdir done */
1365 if (gen == ~(u_int) 0) {
1367 dlog("End of readdir in %s", mp->am_path);
1374 /* non-browsable directories code */
1376 while (xp && xp->am_gen != gen)
1380 int nbytes = count / 2; /* conservative */
1381 int todo = MAX_READDIR_ENTRIES;
1382 dp->dl_entries = ep;
1384 am_node *xp_next = next_nonerror_node(xp->am_osib);
1387 *(u_int *) ep->ne_cookie = xp_next->am_gen;
1389 *(u_int *) ep->ne_cookie = ~(u_int) 0;
1393 ep->ne_fileid = xp->am_gen;
1394 ep->ne_name = xp->am_name;
1395 nbytes -= sizeof(*ep) + 1;
1397 nbytes -= strlen(xp->am_name);
1401 if (nbytes > 0 && !dp->dl_eof && todo > 1) {
1402 ep->ne_nextentry = ep + 1;
1410 ep->ne_nextentry = 0;
1418 /* This one is called only if map is browsable */
1420 amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
1422 u_int gen = *(u_int *) cookie;
1423 int chain_length, i;
1424 static nfsentry *te, *te_next;
1425 #ifdef DEBUG_READDIR
1428 #endif /* DEBUG_READDIR */
1430 dp->dl_eof = FALSE; /* assume readdir not done */
1432 #ifdef DEBUG_READDIR
1433 plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
1435 #endif /* DEBUG_READDIR */
1439 * In the default instance (which is used to start a search) we return
1442 * This assumes that the count is big enough to allow both "." and ".."
1443 * to be returned in a single packet. If it isn't (which would be
1444 * fairly unbelievable) then tough.
1447 dlog("default search");
1450 * Check for enough room. This is extremely approximate but is more
1451 * than enough space. Really need 2 times:
1456 * plus the dirlist structure */
1457 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1461 * compute # of entries to send in this chain.
1462 * heuristics: 128 bytes per entry.
1463 * This is too much probably, but it seems to work better because
1464 * of the re-entrant nature of nfs_readdir, and esp. on systems
1467 chain_length = count / 128;
1469 /* reset static state counters */
1470 te = te_next = NULL;
1472 dp->dl_entries = ep;
1475 ep[0].ne_fileid = mp->am_gen;
1476 ep[0].ne_name = ".";
1477 ep[0].ne_nextentry = &ep[1];
1478 *(u_int *) ep[0].ne_cookie = 0;
1480 /* construct ".." */
1482 ep[1].ne_fileid = mp->am_parent->am_gen;
1484 ep[1].ne_fileid = mp->am_gen;
1485 ep[1].ne_name = "..";
1486 ep[1].ne_nextentry = 0;
1487 *(u_int *) ep[1].ne_cookie = ~(u_int) 0;
1490 * If map is browsable, call a function make_entry_chain() to construct
1491 * a linked list of unmounted keys, and return it. Then link the chain
1492 * to the regular list. Get the chain only once, but return
1493 * chunks of it each time.
1495 te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
1498 #ifdef DEBUG_READDIR
1500 for (ne=te; ne; ne=ne->ne_nextentry)
1501 plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
1502 #endif /* DEBUG_READDIR */
1504 /* return only "chain_length" entries */
1506 for (i=1; i<chain_length; ++i) {
1507 te_next = te_next->ne_nextentry;
1512 nfsentry *te_saved = te_next->ne_nextentry;
1513 te_next->ne_nextentry = NULL; /* terminate "te" chain */
1514 te_next = te_saved; /* save rest of "te" for next interation */
1515 dp->dl_eof = FALSE; /* tell readdir there's more */
1517 dp->dl_eof = TRUE; /* tell readdir that's it */
1519 ep[1].ne_nextentry = te; /* append this chunk of "te" chain */
1520 #ifdef DEBUG_READDIR
1521 for (ne=te; ne; ne=ne->ne_nextentry)
1522 plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
1523 #endif /* DEBUG_READDIR */
1525 } /* end of "if (gen == 0)" statement */
1531 if (gen == ~(u_int) 0) {
1533 dlog("End of readdir in %s", mp->am_path);
1541 * If browsable directories, then continue serving readdir() with another
1542 * chunk of entries, starting from where we left off (when gen was equal
1543 * to 0). Once again, assume last chunk served to readdir.
1546 dp->dl_entries = ep;
1548 te = te_next; /* reset 'te' from last saved te_next */
1549 if (!te) { /* another indicator of end of readdir */
1554 * compute # of entries to send in this chain.
1555 * heuristics: 128 bytes per entry.
1557 chain_length = count / 128;
1559 /* return only "chain_length" entries */
1560 for (i=1; i<chain_length; ++i) {
1561 te_next = te_next->ne_nextentry;
1566 nfsentry *te_saved = te_next->ne_nextentry;
1567 te_next->ne_nextentry = NULL; /* terminate "te" chain */
1568 te_next = te_saved; /* save rest of "te" for next interation */
1569 dp->dl_eof = FALSE; /* tell readdir there's more */
1571 ep = te; /* send next chunk of "te" chain */
1572 dp->dl_entries = ep;
1573 #ifdef DEBUG_READDIR
1574 plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
1575 dp->dl_entries, te_next, dp->dl_eof);
1576 for (ne=te; ne; ne=ne->ne_nextentry)
1577 plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
1578 #endif /* DEBUG_READDIR */
1584 amfs_auto_fmount(am_node *mp)
1586 mntfs *mf = mp->am_mnt;
1587 return (*mf->mf_ops->fmount_fs) (mf);
1592 amfs_auto_fumount(am_node *mp)
1594 mntfs *mf = mp->am_mnt;
1595 return (*mf->mf_ops->fumount_fs) (mf);