2 * Copyright (c) 1997-2003 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.9.2.11 2003/07/18 04:50:18 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 #define DOT_DOT_COOKIE (u_int) 1
62 /****************************************************************************
64 ****************************************************************************/
67 /****************************************************************************
68 *** FORWARD DEFINITIONS ***
69 ****************************************************************************/
70 static int amfs_auto_bgmount(struct continuation *cp, int mpe);
71 static int amfs_auto_mount(am_node *mp);
72 static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
73 static void amfs_auto_umounted(am_node *mp);
76 /****************************************************************************
77 *** OPS STRUCTURES ***
78 ****************************************************************************/
79 am_ops amfs_auto_ops =
83 0, /* amfs_auto_init */
90 0, /* amfs_auto_readlink */
91 0, /* amfs_auto_mounted */
94 FS_AMQINFO | FS_DIRECTORY
98 /****************************************************************************
100 ****************************************************************************/
102 * AMFS_AUTO needs nothing in particular.
105 amfs_auto_match(am_opts *fo)
107 char *p = fo->opt_rfs;
110 plog(XLOG_USER, "auto: no mount point named (rfs:=)");
114 plog(XLOG_USER, "auto: no map named (fs:=)");
119 * Swap round fs:= and rfs:= options
120 * ... historical (jsp)
122 fo->opt_rfs = fo->opt_fs;
126 * mtab entry turns out to be the name of the mount map
128 return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
135 * Build a new map cache for this node, or re-use
136 * an existing cache for the same map.
139 amfs_auto_mkcacheref(mntfs *mf)
143 if (mf->mf_fo && mf->mf_fo->opt_cache)
144 cache = mf->mf_fo->opt_cache;
147 mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
148 mf->mf_fo->opt_maptype);
149 mf->mf_prfree = mapc_free;
157 amfs_auto_mount(am_node *mp)
159 mntfs *mf = mp->am_mnt;
162 * Pseudo-directories are used to provide some structure
163 * to the automounted directories instead
164 * of putting them all in the top-level automount directory.
166 * Here, just increment the parent's link count.
168 mp->am_parent->am_fattr.na_nlink++;
171 * Info field of . means use parent's info field.
172 * Historical - not documented.
174 if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
175 mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
180 * If there is an option prefix then use that else
181 * If the parent had a prefix then use that with name
182 * of this node appended else
183 * Use the name of this node.
185 * That means if you want no prefix you must say so
188 if (mf->mf_fo->opt_pref) {
189 /* allow pref:=null to set a real null prefix */
190 if (STREQ(mf->mf_fo->opt_pref, "null")) {
191 mp->am_pref = strdup("");
194 * the prefix specified as an option
196 mp->am_pref = strdup(mf->mf_fo->opt_pref);
200 * else the parent's prefix
201 * followed by the name
204 char *ppref = mp->am_parent->am_pref;
207 mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
213 amfs_auto_mkcacheref(mf);
222 * Unmount an automount sub-node
225 amfs_auto_umount(am_node *mp)
232 * Unmount an automount node
235 amfs_auto_umounted(am_node *mp)
238 * If this is a pseudo-directory then just adjust the link count
239 * in the parent, otherwise call the generic unmount routine
241 if (mp->am_parent && mp->am_parent->am_parent)
242 --mp->am_parent->am_fattr.na_nlink;
247 * Discard an old continuation
250 free_continuation(struct continuation *cp)
253 untimeout(cp->callout);
257 XFREE(cp->auto_opts);
259 free_opts(&cp->fs_opts);
265 * Discard the underlying mount point and replace
266 * with a reference to an error filesystem.
269 assign_error_mntfs(am_node *mp)
271 if (mp->am_error > 0) {
273 * Save the old error code
275 int error = mp->am_error;
277 error = mp->am_mnt->mf_error;
279 * Discard the old filesystem
281 free_mntfs(mp->am_mnt);
283 * Allocate a new error reference
285 mp->am_mnt = new_mntfs();
287 * Put back the error code
289 mp->am_mnt->mf_error = error;
290 mp->am_mnt->mf_flags |= MFF_ERROR;
292 * Zero the error in the mount point
300 * The continuation function. This is called by
301 * the task notifier when a background mount attempt
305 amfs_auto_cont(int rc, int term, voidp closure)
307 struct continuation *cp = (struct continuation *) closure;
308 mntfs *mf = cp->mp->am_mnt;
311 * Definitely not trying to mount at the moment
313 mf->mf_flags &= ~MFF_MOUNTING;
316 * While we are mounting - try to avoid race conditions
321 * Wakeup anything waiting for this mount
326 * Check for termination signal or exit status...
333 * Not sure what to do for an error code.
335 mf->mf_error = EIO; /* XXX ? */
336 mf->mf_flags |= MFF_ERROR;
337 plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
340 * Check for exit status...
343 mf->mf_flags |= MFF_ERROR;
344 errno = rc; /* XXX */
345 if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
346 plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
350 * If we get here then that attempt didn't work, so
351 * move the info vector pointer along by one and
352 * call the background mount routine again
357 (void) amfs_auto_bgmount(cp, 0);
358 assign_error_mntfs(xmp);
364 free_continuation(cp);
367 reschedule_timeout_mp();
375 amfs_auto_retry(int rc, int term, voidp closure)
377 struct continuation *cp = (struct continuation *) closure;
381 dlog("Commencing retry for mount of %s", cp->mp->am_path);
386 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
388 * The entire mount has timed out. Set the error code and skip past all
389 * the info vectors so that amfs_auto_bgmount will not have any more
390 * ways to try the mount, so causing an error.
392 plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
396 /* explicitly forbid further retries after timeout */
399 if (error || !IN_PROGRESS(cp)) {
400 (void) amfs_auto_bgmount(cp, error);
402 reschedule_timeout_mp();
407 * Try to mount a file system. Can be called
408 * directly or in a sub-process by run_task.
414 am_node *mp = (am_node *) mvp;
415 mntfs *mf = mp->am_mnt;
418 * If the directory is not yet made and it needs to be made, then make it!
419 * This may be run in a background process in which case the flag setting
420 * won't be noticed later - but it is set anyway just after run_task is
421 * called. It should probably go away totally...
423 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
424 error = mkdirs(mf->mf_mount, 0555);
426 mf->mf_flags |= MFF_MKMNT;
432 error = mount_node(mp);
437 dlog("amfs_auto: call to mount_node(%s) failed: %m", mp->am_path);
446 * Pick a file system to try mounting and
447 * do that in the background if necessary
450 if it is new -defaults then
455 if a location has been tried then
461 discard previous mount location if required
462 find matching mounted filesystem
463 if not applicable then
464 this_error = No such file or directory
467 if the filesystem failed to be mounted then
468 this_error = error from filesystem
469 elif the filesystem is mounting or unmounting then
471 elif the fileserver is down then
473 elif the filesystem is already mounted
477 if no error on this mount then
478 this_error = initialize mount point
480 if no error on this mount and mount is delayed then
483 if this_error < 0 then
486 if no error on this mount then
487 make mount point if required
489 if no error on this mount then
490 if mount in background then
491 run mount in background
494 this_error = mount in foreground
497 if an error occurred on this mount then
499 save error in mount point
504 amfs_auto_bgmount(struct continuation *cp, int mpe)
506 mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
507 mntfs *mf_retry = 0; /* First mntfs which needed retrying */
508 int this_error = -1; /* Per-mount error */
513 * Try to mount each location.
515 * hard_error == 0 indicates something was mounted.
516 * hard_error > 0 indicates everything failed with a hard error
517 * hard_error < 0 indicates nothing could be mounted now
519 for (; this_error && *cp->ivec; cp->ivec++) {
521 am_node *mp = cp->mp;
526 hard_error = this_error;
530 if (**cp->ivec == '-') {
532 * Pick up new defaults
534 if (cp->auto_opts && *cp->auto_opts)
535 cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
537 cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
539 dlog("Setting def_opts to \"%s\"", cp->def_opts);
544 * If a mount has been attempted, and we find
545 * a cut then don't try any more locations.
547 if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
550 dlog("Cut: not trying any more locations for %s",
558 /* match the operators */
559 p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
562 * Find a mounted filesystem for this node.
564 mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
568 cp->fs_opts.opt_opts,
569 cp->fs_opts.opt_remopts);
573 dlog("Got a hit with %s", p->fs_type);
577 * Note whether this is a real mount attempt
579 if (p == &amfs_error_ops) {
580 plog(XLOG_MAP, "Map entry %s for %s did not match", *cp->ivec, mp->am_path);
585 if (cp->fs_opts.fs_mtab) {
586 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
587 cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
599 link_dir = mf->mf_fo->opt_sublink;
601 if (link_dir && *link_dir) {
602 if (*link_dir == '/') {
603 mp->am_link = strdup(link_dir);
606 * Try getting fs option from continuation, not mountpoint!
607 * Don't try logging the string from mf, since it may be bad!
609 if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
610 plog(XLOG_ERROR, "use %s instead of 0x%lx",
611 cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs);
613 mp->am_link = str3cat((char *) 0,
614 cp->fs_opts.opt_fs, "/", link_dir);
616 normalize_slash(mp->am_link);
620 if (mf->mf_error > 0) {
621 this_error = mf->mf_error;
622 } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
624 * Still mounting - retry later
627 dlog("Duplicate pending mount fstype %s", p->fs_type);
630 } else if (FSRV_ISDOWN(mf->mf_server)) {
632 * Would just mount from the same place
633 * as a hung mount - so give up
636 dlog("%s is already hung - giving up", mf->mf_mount);
638 mp_error = EWOULDBLOCK;
641 } else if (mf->mf_flags & MFF_MOUNTED) {
643 dlog("duplicate mount of \"%s\" ...", mf->mf_info);
647 * Just call mounted()
656 * Will usually need to play around with the mount nodes
657 * file attribute structure. This must be done here.
658 * Try and get things initialized, even if the fileserver
659 * is not known to be up. In the common case this will
660 * progress things faster.
664 * Fill in attribute fields.
666 if (mf->mf_ops->fs_flags & FS_DIRECTORY)
672 this_error = (*p->fs_init) (mf);
676 * Make sure the fileserver is UP before doing any more work
678 if (!FSRV_ISUP(mf->mf_server)) {
680 dlog("waiting for server %s to become available", mf->mf_server->fs_host);
685 if (!this_error && mf->mf_fo->opt_delay) {
687 * If there is a delay timer on the mount
688 * then don't try to mount if the timer
691 int i = atoi(mf->mf_fo->opt_delay);
692 if (i > 0 && clocktime() < (cp->start + i)) {
694 dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start));
700 if (this_error < 0 && !dont_retry) {
702 mf_retry = dup_mntfs(mf);
705 dlog("will retry ...\n");
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);
721 /* actually run the task, backgrounding as necessary */
722 run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
724 mf->mf_flags |= MFF_MKMNT; /* XXX */
726 free_mntfs(mf_retry);
730 dlog("foreground mount of \"%s\" ...", mf->mf_info);
732 this_error = try_mount((voidp) mp);
733 if (this_error < 0) {
735 mf_retry = dup_mntfs(mf);
741 if (this_error >= 0) {
742 if (this_error > 0) {
744 if (mf != mf_retry) {
745 mf->mf_error = this_error;
746 mf->mf_flags |= MFF_ERROR;
751 * Wakeup anything waiting for this mount
757 if (this_error && cp->retry) {
759 mf = cp->mp->am_mnt = mf_retry;
761 * Not retrying again (so far)
766 * Start at the beginning.
767 * Rewind the location vector and
768 * reset the default options.
771 dlog("(skipping rewind)\n");
774 * Arrange that amfs_auto_bgmount is called
775 * after anything else happens.
778 dlog("Arranging to retry mount of %s", cp->mp->am_path);
780 sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
782 untimeout(cp->callout);
783 cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
785 cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
788 * Not done yet - so don't return anything
793 if (hard_error < 0 || this_error == 0)
794 hard_error = this_error;
797 * Discard handle on duff filesystem.
798 * This should never happen since it
799 * should be caught by the case above.
803 plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
804 free_mntfs(mf_retry);
808 * If we get here, then either the mount succeeded or
809 * there is no more mount information available.
811 if (hard_error < 0 && mp_error)
812 hard_error = cp->mp->am_error = mp_error;
813 if (hard_error > 0) {
815 * Set a small(ish) timeout on an error node if
816 * the error was not a time out.
818 switch (hard_error) {
821 cp->mp->am_timeo = 17;
824 cp->mp->am_timeo = 5;
831 * Make sure that the error value in the mntfs has a
834 if (mf->mf_error < 0) {
835 mf->mf_error = hard_error;
837 mf->mf_flags |= MFF_ERROR;
841 * In any case we don't need the continuation any more
843 free_continuation(cp);
850 * Automount interface to RPC lookup routine
851 * Find the corresponding entry and return
852 * the file handle for it.
855 amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
857 am_node *ap, *new_mp, *ap_hung;
858 char *info; /* Mount info - where to get the file system */
859 char **ivec, **xivec; /* Split version of info */
860 char *auto_opts; /* Automount options */
861 int error = 0; /* Error so far */
862 char path_name[MAXPATHLEN]; /* General path name buffer */
863 char *pfname; /* Path for database lookup */
864 struct continuation *cp; /* Continuation structure if need to mount */
865 int in_progress = 0; /* # of (un)mount in progress */
870 dlog("in amfs_auto_lookuppn");
874 * If the server is shutting down
875 * then don't return information
876 * about the mount point.
878 if (amd_state == Finishing) {
880 if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
881 dlog("%s mount ignored - going down", fname);
883 dlog("%s/%s mount ignored - going down", mp->am_path, fname);
890 * Handle special case of "." and ".."
892 if (fname[0] == '.') {
893 if (fname[1] == '\0')
894 return mp; /* "." is the current node */
895 if (fname[1] == '.' && fname[2] == '\0') {
898 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
900 return mp->am_parent; /* ".." is the parent node */
907 * Check for valid key name.
908 * If it is invalid then pretend it doesn't exist.
910 if (!valid_key(fname)) {
911 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
917 * fname is now a private copy.
919 fname = expand_key(fname);
921 for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
923 * Otherwise search children of this node
925 if (FSTREQ(ap->am_name, fname)) {
928 error = ap->am_error;
932 * If the error code is undefined then it must be
935 if (mf->mf_error < 0)
939 * Check for a hung node
941 if (FSRV_ISDOWN(mf->mf_server)) {
945 error = ap->am_error;
950 * If there was a previous error with this node
951 * then return that error code.
953 if (mf->mf_flags & MFF_ERROR) {
954 error = mf->mf_error;
957 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
960 * If the fs is not mounted or it is unmounting then there
961 * is a background (un)mount in progress. In this case
962 * we just drop the RPC request (return nil) and
963 * wait for a retry, by which time the (un)mount may
967 dlog("ignoring mount of %s in %s -- flags (%x) in progress",
968 fname, mf->mf_mount, mf->mf_flags);
975 * Otherwise we have a hit: return the current mount point.
978 dlog("matched %s in %s", fname, ap->am_path);
987 dlog("Waiting while %d mount(s) in progress", in_progress);
994 * If an error occurred then return it.
998 errno = error; /* XXX */
999 dlog("Returning error: %m");
1006 * If doing a delete then don't create again!
1016 plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
1021 * If the server is going down then just return,
1022 * don't try to mount any more file systems
1024 if ((int) amd_state >= (int) Finishing) {
1026 dlog("not found - server going down anyway");
1033 * If we get there then this is a reference to an,
1034 * as yet, unknown name so we need to search the mount
1038 sprintf(path_name, "%s%s", mp->am_pref, fname);
1047 dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1050 * Consult the oracle for some mount information.
1051 * info is malloc'ed and belongs to this routine.
1052 * It ends up being free'd in free_continuation().
1054 * Note that this may return -1 indicating that information
1055 * is not yet available.
1057 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
1060 plog(XLOG_MAP, "No map entry for %s", pfname);
1062 plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
1067 dlog("mount info is %s", info);
1071 * Split info into an argument vector.
1072 * The vector is malloc'ed and belongs to
1073 * this routine. It is free'd in free_continuation()
1075 xivec = ivec = strsplit(info, ' ', '\"');
1078 * Default error code...
1081 error = EWOULDBLOCK;
1086 * Allocate a new map
1088 new_mp = exported_ap_alloc();
1096 auto_opts = mf->mf_auto;
1100 auto_opts = strdup(auto_opts);
1103 dlog("searching for /defaults entry");
1105 if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
1109 dlog("/defaults gave %s", dflts);
1117 * Chop the defaults up
1119 rvec = strsplit(dfl, ' ', '\"');
1121 if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
1123 * Pick whichever first entry matched the list of selectors.
1124 * Strip the selectors from the string, and assign to dfl the
1125 * rest of the string.
1131 while (*sp) { /* loop until you find something, if any */
1132 memset((char *) &ap, 0, sizeof(am_opts));
1134 * This next routine cause many spurious "expansion of ... is"
1135 * messages, which are ignored, b/c all we need out of this
1136 * routine is to match selectors. These spurious messages may
1137 * be wrong, esp. if they try to expand ${key} b/c it will
1138 * get expanded to "/defaults"
1140 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1141 mp->am_parent->am_mnt->mf_info);
1142 free_opts(&ap); /* don't leak */
1143 if (pt == &amfs_error_ops) {
1144 plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1146 dfl = strip_selectors(*sp, "/defaults");
1147 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1153 } else { /* not enable_default_selectors */
1155 * Extract first value
1161 * If there were any values at all...
1165 * Log error if there were other values
1167 if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1169 dlog("/defaults chopped into %s", dfl);
1171 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1175 * Prepend to existing defaults if they exist,
1176 * otherwise just use these defaults.
1178 if (*auto_opts && *dfl) {
1179 char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
1180 sprintf(nopts, "%s;%s", dfl, auto_opts);
1184 auto_opts = strealloc(auto_opts, dfl);
1189 * Don't need info vector any more
1197 init_map(new_mp, fname);
1200 * Put it in the table
1202 insert_am(new_mp, mp);
1205 * Fill in some other fields,
1206 * path and mount point.
1208 * bugfix: do not prepend old am_path if direct map
1209 * <wls@astro.umd.edu> William Sebok
1211 new_mp->am_path = str3cat(new_mp->am_path,
1212 mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
1213 *fname == '/' ? "" : "/", fname);
1216 dlog("setting path to %s", new_mp->am_path);
1220 * Take private copy of pfname
1222 pfname = strdup(pfname);
1225 * Construct a continuation
1227 cp = ALLOC(struct continuation);
1234 cp->auto_opts = auto_opts;
1237 cp->start = clocktime();
1238 cp->def_opts = strdup(auto_opts);
1239 memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
1242 * Try and mount the file system. If this succeeds immediately (possible
1243 * for a ufs file system) then return the attributes, otherwise just
1246 error = amfs_auto_bgmount(cp, error);
1247 reschedule_timeout_mp();
1254 * Code for quick reply. If nfs_program_2_transp is set, then
1255 * its the transp that's been passed down from nfs_program_2().
1256 * If new_mp->am_transp is not already set, set it by copying in
1257 * nfs_program_2_transp. Once am_transp is set, quick_reply() can
1258 * use it to send a reply to the client that requested this mount.
1260 if (nfs_program_2_transp && !new_mp->am_transp) {
1261 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1262 *(new_mp->am_transp) = *nfs_program_2_transp;
1264 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1265 new_mp->am_error = error;
1267 assign_error_mntfs(new_mp);
1276 * Locate next node in sibling list which is mounted
1277 * and is not an error node.
1280 next_nonerror_node(am_node *xp)
1285 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
1286 * Fixes a race condition when mounting direct automounts.
1287 * Also fixes a problem when doing a readdir on a directory
1288 * containing hung automounts.
1291 (!(mf = xp->am_mnt) || /* No mounted filesystem */
1292 mf->mf_error != 0 || /* There was a mntfs error */
1293 xp->am_error != 0 || /* There was a mount error */
1294 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
1295 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
1304 * This readdir function which call a special version of it that allows
1305 * browsing if browsable_dirs=yes was set on the map.
1308 amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
1310 u_int gen = *(u_int *) cookie;
1318 dp->dl_eof = FALSE; /* assume readdir not done */
1320 /* check if map is browsable */
1321 if (mp->am_mnt && mp->am_mnt->mf_mopts) {
1322 mnt.mnt_opts = mp->am_mnt->mf_mopts;
1323 if (hasmntopt(&mnt, "fullybrowsable"))
1324 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
1325 if (hasmntopt(&mnt, "browsable"))
1326 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
1329 /* when gen is 0, we start reading from the beginning of the directory */
1332 * In the default instance (which is used to start a search) we return
1335 * This assumes that the count is big enough to allow both "." and ".."
1336 * to be returned in a single packet. If it isn't (which would be
1337 * fairly unbelievable) then tough.
1340 dlog("amfs_auto_readdir: default search");
1343 * Check for enough room. This is extremely approximate but is more
1344 * than enough space. Really need 2 times:
1349 * plus the dirlist structure */
1350 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1353 xp = next_nonerror_node(mp->am_child);
1354 dp->dl_entries = ep;
1357 ep[0].ne_fileid = mp->am_gen;
1358 ep[0].ne_name = ".";
1359 ep[0].ne_nextentry = &ep[1];
1360 *(u_int *) ep[0].ne_cookie = 0;
1362 /* construct ".." */
1364 ep[1].ne_fileid = mp->am_parent->am_gen;
1366 ep[1].ne_fileid = mp->am_gen;
1367 ep[1].ne_name = "..";
1368 ep[1].ne_nextentry = 0;
1369 *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE);
1372 dp->dl_eof = TRUE; /* by default assume readdir done */
1376 for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
1377 plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d",
1378 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
1383 dlog("amfs_auto_readdir: real child");
1386 if (gen == DOT_DOT_COOKIE) {
1388 dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path);
1394 plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n");
1399 /* non-browsable directories code */
1401 while (xp && xp->am_gen != gen)
1405 int nbytes = count / 2; /* conservative */
1406 int todo = MAX_READDIR_ENTRIES;
1408 dp->dl_entries = ep;
1410 am_node *xp_next = next_nonerror_node(xp->am_osib);
1413 *(u_int *) ep->ne_cookie = xp_next->am_gen;
1415 *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE;
1419 ep->ne_fileid = xp->am_gen;
1420 ep->ne_name = xp->am_name;
1421 nbytes -= sizeof(*ep) + 1;
1423 nbytes -= strlen(xp->am_name);
1427 if (nbytes > 0 && !dp->dl_eof && todo > 1) {
1428 ep->ne_nextentry = ep + 1;
1436 ep->ne_nextentry = 0;
1440 for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
1441 plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d",
1442 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
1450 /* This one is called only if map is browsable */
1452 amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
1454 u_int gen = *(u_int *) cookie;
1455 int chain_length, i;
1456 static nfsentry *te, *te_next;
1462 dp->dl_eof = FALSE; /* assume readdir not done */
1466 plog(XLOG_DEBUG, "amfs_auto_readdir_browsable gen=%u, count=%d",
1472 * In the default instance (which is used to start a search) we return
1475 * This assumes that the count is big enough to allow both "." and ".."
1476 * to be returned in a single packet. If it isn't (which would be
1477 * fairly unbelievable) then tough.
1480 dlog("amfs_auto_readdir_browsable: default search");
1483 * Check for enough room. This is extremely approximate but is more
1484 * than enough space. Really need 2 times:
1489 * plus the dirlist structure */
1490 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
1494 * compute # of entries to send in this chain.
1495 * heuristics: 128 bytes per entry.
1496 * This is too much probably, but it seems to work better because
1497 * of the re-entrant nature of nfs_readdir, and esp. on systems
1500 chain_length = count / 128;
1502 /* reset static state counters */
1503 te = te_next = NULL;
1505 dp->dl_entries = ep;
1508 ep[0].ne_fileid = mp->am_gen;
1509 ep[0].ne_name = ".";
1510 ep[0].ne_nextentry = &ep[1];
1511 *(u_int *) ep[0].ne_cookie = 0;
1513 /* construct ".." */
1515 ep[1].ne_fileid = mp->am_parent->am_gen;
1517 ep[1].ne_fileid = mp->am_gen;
1519 ep[1].ne_name = "..";
1520 ep[1].ne_nextentry = 0;
1521 *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE;
1524 * If map is browsable, call a function make_entry_chain() to construct
1525 * a linked list of unmounted keys, and return it. Then link the chain
1526 * to the regular list. Get the chain only once, but return
1527 * chunks of it each time.
1529 te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
1534 for (j=0,ne=te; ne; ne=ne->ne_nextentry)
1535 plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name);
1538 /* return only "chain_length" entries */
1540 for (i=1; i<chain_length; ++i) {
1541 te_next = te_next->ne_nextentry;
1546 nfsentry *te_saved = te_next->ne_nextentry;
1547 te_next->ne_nextentry = NULL; /* terminate "te" chain */
1548 te_next = te_saved; /* save rest of "te" for next iteration */
1549 dp->dl_eof = FALSE; /* tell readdir there's more */
1551 dp->dl_eof = TRUE; /* tell readdir that's it */
1553 ep[1].ne_nextentry = te; /* append this chunk of "te" chain */
1555 amuDebug(D_READDIR) {
1556 for (j=0,ne=te; ne; ne=ne->ne_nextentry)
1557 plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name);
1558 for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
1559 plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d",
1560 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
1561 plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof);
1563 #endif /* DEBUG_READDIR */
1565 } /* end of "if (gen == 0)" statement */
1568 dlog("amfs_auto_readdir_browsable: real child");
1571 if (gen == DOT_DOT_COOKIE) {
1573 dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path);
1581 * If browsable directories, then continue serving readdir() with another
1582 * chunk of entries, starting from where we left off (when gen was equal
1583 * to 0). Once again, assume last chunk served to readdir.
1586 dp->dl_entries = ep;
1588 te = te_next; /* reset 'te' from last saved te_next */
1589 if (!te) { /* another indicator of end of readdir */
1594 * compute # of entries to send in this chain.
1595 * heuristics: 128 bytes per entry.
1597 chain_length = count / 128;
1599 /* return only "chain_length" entries */
1600 for (i=1; i<chain_length; ++i) {
1601 te_next = te_next->ne_nextentry;
1606 nfsentry *te_saved = te_next->ne_nextentry;
1607 te_next->ne_nextentry = NULL; /* terminate "te" chain */
1608 te_next = te_saved; /* save rest of "te" for next iteration */
1609 dp->dl_eof = FALSE; /* tell readdir there's more */
1611 ep = te; /* send next chunk of "te" chain */
1612 dp->dl_entries = ep;
1614 amuDebug(D_READDIR) {
1615 plog(XLOG_DEBUG, "dl_entries=0x%lx, te_next=0x%lx, dl_eof=%d",
1616 (long) dp->dl_entries, (long) te_next, dp->dl_eof);
1617 for (ne=te; ne; ne=ne->ne_nextentry)
1618 plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name);
1626 amfs_auto_fmount(am_node *mp)
1628 mntfs *mf = mp->am_mnt;
1629 return (*mf->mf_ops->fmount_fs) (mf);
1634 amfs_auto_fumount(am_node *mp)
1636 mntfs *mf = mp->am_mnt;
1637 return (*mf->mf_ops->fumount_fs) (mf);