2 * Copyright (c) 1997-2006 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
40 * File: am-utils/amd/amfs_generic.c
45 * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
50 #endif /* HAVE_CONFIG_H */
55 /****************************************************************************
57 ****************************************************************************/
58 #define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
61 /****************************************************************************
63 ****************************************************************************/
65 * Mounting a file system may take a significant period of time. The
66 * problem is that if this is done in the main process thread then the
67 * entire automounter could be blocked, possibly hanging lots of processes
68 * on the system. Instead we use a continuation scheme to allow mounts to
69 * be attempted in a sub-process. When the sub-process exits we pick up the
70 * exit status (by convention a UN*X error number) and continue in a
71 * notifier. The notifier gets handed a data structure and can then
72 * determine whether the mount was successful or not. If not, it updates
73 * the data structure and tries again until there are no more ways to try
74 * the mount, or some other permanent error occurs. In the mean time no RPC
75 * reply is sent, even after the mount is successful. We rely on the RPC
76 * retry mechanism to resend the lookup request which can then be handled.
79 am_node *mp; /* Node we are trying to mount */
80 int retry; /* Try again? */
81 time_t start; /* Time we started this mount */
82 int callout; /* Callout identifier */
83 mntfs **mf; /* Current mntfs */
87 /****************************************************************************
88 *** FORWARD DEFINITIONS ***
89 ****************************************************************************/
90 static am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return);
91 static mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
92 char *def_opts, char *pfname);
93 static mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return);
94 static void amfs_cont(int rc, int term, opaque_t arg);
95 static void amfs_retry(int rc, int term, opaque_t arg);
96 static void free_continuation(struct continuation *cp);
97 static int amfs_bgmount(struct continuation *cp);
98 static char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts);
101 /****************************************************************************
103 ****************************************************************************/
105 amfs_lookup_node(am_node *mp, char *fname, int *error_return)
108 int error = 0; /* Error so far */
109 int in_progress = 0; /* # of (un)mount in progress */
111 char *expanded_fname = 0;
113 dlog("in amfs_lookup_node");
116 * If the server is shutting down
117 * then don't return information
118 * about the mount point.
120 if (amd_state == Finishing) {
121 if (mp->am_mnt == 0 || mp->am_mnt->mf_fsflags & FS_DIRECT) {
122 dlog("%s mount ignored - going down", fname);
124 dlog("%s/%s mount ignored - going down", mp->am_path, fname);
130 * Handle special case of "." and ".."
132 if (fname[0] == '.') {
133 if (fname[1] == '\0')
134 return mp; /* "." is the current node */
135 if (fname[1] == '.' && fname[2] == '\0') {
137 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
138 return mp->am_parent; /* ".." is the parent node */
145 * Check for valid key name.
146 * If it is invalid then pretend it doesn't exist.
148 if (!valid_key(fname)) {
149 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
155 * expanded_fname is now a private copy.
157 expanded_fname = expand_selectors(fname);
160 * Search children of this node
162 for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) {
163 if (FSTREQ(new_mp->am_name, expanded_fname)) {
164 if (new_mp->am_error) {
165 error = new_mp->am_error;
170 * If the error code is undefined then it must be
174 if (mf->mf_error < 0)
178 * If there was a previous error with this node
179 * then return that error code.
181 if (mf->mf_flags & MFF_ERROR) {
182 error = mf->mf_error;
185 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
188 * If the fs is not mounted or it is unmounting then there
189 * is a background (un)mount in progress. In this case
190 * we just drop the RPC request (return nil) and
191 * wait for a retry, by which time the (un)mount may
194 dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x",
195 expanded_fname, mf->mf_mount,
196 (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags);
198 if (mf->mf_flags & MFF_UNMOUNTING) {
199 dlog("will remount later");
200 new_mp->am_flags |= AMF_REMOUNT;
206 * Otherwise we have a hit: return the current mount point.
208 dlog("matched %s in %s", expanded_fname, new_mp->am_path);
209 XFREE(expanded_fname);
215 dlog("Waiting while %d mount(s) in progress", in_progress);
216 XFREE(expanded_fname);
221 * If an error occurred then return it.
224 dlog("Returning error: %s", strerror(error));
225 XFREE(expanded_fname);
230 * If the server is going down then just return,
231 * don't try to mount any more file systems
233 if ((int) amd_state >= (int) Finishing) {
234 dlog("not found - server going down anyway");
241 new_mp = get_ap_child(mp, expanded_fname);
242 XFREE(expanded_fname);
253 amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
254 char *def_opts, char *pfname)
260 #ifdef HAVE_FS_AUTOFS
262 #endif /* HAVE_FS_AUTOFS */
264 /* match the operators */
265 fs_opts = CALLOC(am_opts);
266 p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path,
267 pfname, mf->mf_info);
268 #ifdef HAVE_FS_AUTOFS
269 /* XXX: this should be factored out into an autofs-specific function */
270 if (new_mp->am_flags & AMF_AUTOFS) {
271 /* ignore user-provided fs if we're using autofs */
272 if (fs_opts->opt_sublink) {
274 * For sublinks we need to use a hack with autofs:
275 * mount the filesystem on the original opt_fs (which is NOT an
276 * autofs mountpoint) and symlink (or lofs-mount) to it from
277 * the autofs mountpoint.
280 mp_dir = fs_opts->opt_fs;
282 if (p->autofs_fs_flags & FS_ON_AUTOFS) {
283 mp_dir = new_mp->am_path;
285 mp_dir = fs_opts->opt_fs;
290 #endif /* HAVE_FS_AUTOFS */
291 mp_dir = fs_opts->opt_fs;
294 * Find or allocate a filesystem for this node.
296 new_mf = find_mntfs(p, fs_opts,
301 fs_opts->opt_remopts);
304 * See whether this is a real filesystem
307 if (p == &amfs_error_ops) {
308 plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path);
313 dlog("Got a hit with %s", p->fs_type);
315 #ifdef HAVE_FS_AUTOFS
316 if (new_mp->am_flags & AMF_AUTOFS && on_autofs) {
317 new_mf->mf_flags |= MFF_ON_AUTOFS;
318 new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags;
321 * A new filesystem is an autofs filesystems if:
322 * 1. it claims it can be one (has the FS_AUTOFS flag)
323 * 2. autofs is enabled system-wide
324 * 3. either has an autofs parent,
325 * or it is explicitly requested to be autofs.
327 if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS &&
329 ((mf->mf_flags & MFF_IS_AUTOFS) ||
330 (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type &&
331 STREQ(new_mf->mf_fo->opt_mount_type, "autofs"))))
332 new_mf->mf_flags |= MFF_IS_AUTOFS;
333 #endif /* HAVE_FS_AUTOFS */
340 amfs_lookup_mntfs(am_node *new_mp, int *error_return)
343 char *info; /* Mount info - where to get the file system */
344 char **ivecs, **cur_ivec; /* Split version of info */
346 char *orig_def_opts; /* Original Automount options */
347 char *def_opts; /* Automount options */
348 int error = 0; /* Error so far */
349 char path_name[MAXPATHLEN]; /* General path name buffer */
350 char *pfname; /* Path for database lookup */
351 mntfs *mf, **mf_array;
354 dlog("in amfs_lookup_mntfs");
356 mp = new_mp->am_parent;
359 * If we get here then this is a reference to an,
360 * as yet, unknown name so we need to search the mount
364 if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name))
365 ereturn(ENAMETOOLONG);
366 xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name);
369 pfname = new_mp->am_name;
374 dlog("will search map info in %s to find %s", mf->mf_info, pfname);
376 * Consult the oracle for some mount information.
377 * info is malloc'ed and belongs to this routine.
378 * It ends up being free'd in free_continuation().
380 * Note that this may return -1 indicating that information
381 * is not yet available.
383 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
386 plog(XLOG_MAP, "No map entry for %s", pfname);
388 plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
391 dlog("mount info is %s", info);
394 * Split info into an argument vector.
395 * The vector is malloc'ed and belongs to
396 * this routine. It is free'd further down.
398 * Note: the vector pointers point into info, so don't free it!
400 ivecs = strsplit(info, ' ', '\"');
403 def_opts = mf->mf_auto;
407 orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts));
408 def_opts = strdup(orig_def_opts);
410 /* first build our defaults */
412 for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
413 if (**cur_ivec == '-') {
415 * Pick up new defaults
417 char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1);
419 def_opts = new_def_opts;
420 dlog("Setting def_opts to \"%s\"", def_opts);
426 mf_array = calloc(num_ivecs + 1, sizeof(mntfs *));
428 /* construct the array of struct mntfs for this mount point */
429 for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
432 if (**cur_ivec == '-') {
434 if ((*cur_ivec)[1] == '\0') {
436 * If we have a single dash '-' than we need to reset the
439 def_opts = strdup(orig_def_opts);
440 dlog("Resetting the default options, a single dash '-' was found.");
442 /* append options to /default options */
443 def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1);
444 dlog("Resetting def_opts to \"%s\"", def_opts);
450 * If a mntfs has already been found, and we find
451 * a cut then don't try any more locations.
453 * XXX: we do not know when the "/" was added as an equivalent for "||".
454 * It's undocumented, it might go away at any time. Caveat emptor.
456 if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
458 dlog("Cut: not trying any more locations for %s", mp->am_path);
464 new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname);
467 mf_array[count++] = new_mf;
470 /* We're done with ivecs */
473 XFREE(orig_def_opts);
475 if (count == 0) { /* no match */
485 * The continuation function. This is called by
486 * the task notifier when a background mount attempt
490 amfs_cont(int rc, int term, opaque_t arg)
492 struct continuation *cp = (struct continuation *) arg;
493 am_node *mp = cp->mp;
494 mntfs *mf = mp->am_mnt;
496 dlog("amfs_cont: '%s'", mp->am_path);
499 * Definitely not trying to mount at the moment
501 mf->mf_flags &= ~MFF_MOUNTING;
504 * While we are mounting - try to avoid race conditions
509 * Wakeup anything waiting for this mount
511 wakeup(get_mntfs_wchan(mf));
514 * Check for termination signal or exit status...
517 #ifdef HAVE_FS_AUTOFS
518 if (mf->mf_flags & MFF_IS_AUTOFS &&
519 !(mf->mf_flags & MFF_MOUNTED))
520 autofs_release_fh(mp);
521 #endif /* HAVE_FS_AUTOFS */
525 * Not sure what to do for an error code.
527 mf->mf_error = EIO; /* XXX ? */
528 mf->mf_flags |= MFF_ERROR;
529 plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
532 * Check for exit status...
538 * On Linux (and maybe not only) it's possible to run
539 * an amd which "knows" how to mount certain combinations
540 * of nfs_proto/nfs_version which the kernel doesn't grok.
541 * So if we got an EINVAL and we have a server that's not
542 * using NFSv2/UDP, try again with NFSv2/UDP.
544 * Too bad that there is no way to dynamically determine
545 * what combinations the _client_ supports, as opposed to
546 * what the _server_ supports...
550 (mf->mf_server->fs_version != 2 ||
551 !STREQ(mf->mf_server->fs_proto, "udp")))
552 mf->mf_flags |= MFF_NFS_SCALEDOWN;
554 #endif /* __linux__ */
557 mf->mf_flags |= MFF_ERROR;
558 errno = rc; /* XXX */
559 if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx"))
560 plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
564 if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
566 * If we get here then that attempt didn't work, so
567 * move the info vector pointer along by one and
568 * call the background mount routine again
574 if (mp->am_error > 0)
575 assign_error_mntfs(mp);
580 dlog("Mounting %s returned success", cp->mp->am_path);
582 free_continuation(cp);
585 reschedule_timeout_mp();
593 amfs_retry(int rc, int term, opaque_t arg)
595 struct continuation *cp = (struct continuation *) arg;
596 am_node *mp = cp->mp;
599 dlog("Commencing retry for mount of %s", mp->am_path);
603 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) {
605 * The entire mount has timed out. Set the error code and skip past all
606 * the mntfs's so that amfs_bgmount will not have any more
607 * ways to try the mount, thus causing an error.
609 plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
613 /* explicitly forbid further retries after timeout */
616 if (error || !IN_PROGRESS(cp))
617 error = amfs_bgmount(cp);
619 reschedule_timeout_mp();
624 * Discard an old continuation
627 free_continuation(struct continuation *cp)
631 dlog("free_continuation");
633 untimeout(cp->callout);
635 * we must free the mntfs's in the list.
636 * so free all of them if there was an error,
637 * or free all but the used one, if the mount succeeded.
639 for (mfp = cp->mp->am_mfarray; *mfp; mfp++) {
642 XFREE(cp->mp->am_mfarray);
643 cp->mp->am_mfarray = 0;
649 * Pick a file system to try mounting and
650 * do that in the background if necessary
653 discard previous mount location if required
654 fetch next mount location
655 if the filesystem failed to be mounted then
656 this_error = error from filesystem
658 if the filesystem is mounting or unmounting then
660 if the fileserver is down then
663 if the filesystem is already mounted
667 this_error = initialize mount point
669 if no error on this mount and mount is delayed then
672 if this_error < 0 then
675 if no error on this mount then
676 if mount in background then
677 run mount in background
680 this_error = mount in foreground
683 if an error occurred on this mount then
685 save error in mount point
690 amfs_bgmount(struct continuation *cp)
692 am_node *mp = cp->mp;
693 mntfs *mf; /* Current mntfs */
694 int this_error = -1; /* Per-mount error */
695 int hard_error = -1; /* Cumulative per-node error */
698 free_mntfs(mp->am_mnt);
701 * Try to mount each location.
703 * hard_error == 0 indicates something was mounted.
704 * hard_error > 0 indicates everything failed with a hard error
705 * hard_error < 0 indicates nothing could be mounted now
707 for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) {
710 mf = dup_mntfs(mp->am_mnt);
714 hard_error = this_error;
717 if (mf->mf_error > 0) {
718 this_error = mf->mf_error;
722 if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
724 * Still mounting - retry later
726 dlog("mount of \"%s\" already pending", mf->mf_info);
730 if (FSRV_ISDOWN(mf->mf_server)) {
732 * Would just mount from the same place
733 * as a hung mount - so give up
735 dlog("%s is already hung - giving up", mf->mf_server->fs_host);
744 if (mf->mf_fo && mf->mf_fo->opt_sublink)
745 mp->am_link = strdup(mf->mf_fo->opt_sublink);
748 * Will usually need to play around with the mount nodes
749 * file attribute structure. This must be done here.
750 * Try and get things initialized, even if the fileserver
751 * is not known to be up. In the common case this will
752 * progress things faster.
756 * Fill in attribute fields.
758 if (mf->mf_fsflags & FS_DIRECTORY)
759 mk_fattr(&mp->am_fattr, NFDIR);
761 mk_fattr(&mp->am_fattr, NFLNK);
763 if (mf->mf_flags & MFF_MOUNTED) {
764 dlog("duplicate mount of \"%s\" ...", mf->mf_info);
766 * Skip initial processing of the mountpoint if already mounted.
767 * This could happen if we have multiple sublinks into the same f/s,
768 * or if we are restarting an already-mounted filesystem.
770 goto already_mounted;
773 if (mf->mf_fo && mf->mf_fo->fs_mtab) {
774 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
775 mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
776 mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
779 if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
780 this_error = p->fs_init(mf);
787 if (mf->mf_fo && mf->mf_fo->opt_delay) {
789 * If there is a delay timer on the mount
790 * then don't try to mount if the timer
793 int i = atoi(mf->mf_fo->opt_delay);
794 time_t now = clocktime(NULL);
795 if (i > 0 && now < (cp->start + i)) {
796 dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start));
802 * If the directory is not yet made and it needs to be made, then make it!
804 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
805 plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
806 this_error = mkdirs(mf->mf_mount, 0555);
808 plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
811 mf->mf_flags |= MFF_MKMNT;
814 #ifdef HAVE_FS_AUTOFS
815 if (mf->mf_flags & MFF_IS_AUTOFS)
816 if ((this_error = autofs_get_fh(mp)))
818 #endif /* HAVE_FS_AUTOFS */
821 mf->mf_flags |= MFF_MOUNTING;
822 if (mf->mf_fsflags & FS_MBACKGROUND) {
823 dlog("backgrounding mount of \"%s\"", mf->mf_mount);
825 untimeout(cp->callout);
829 /* actually run the task, backgrounding as necessary */
830 run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
833 dlog("foreground mount of \"%s\" ...", mf->mf_mount);
834 this_error = mount_node((opaque_t) mp);
837 mf->mf_flags &= ~MFF_MOUNTING;
840 if (this_error == 0) {
848 dlog("will retry ...\n");
851 * Arrange that amfs_bgmount is called
852 * after anything else happens.
854 dlog("Arranging to retry mount of %s", mp->am_path);
855 sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
857 untimeout(cp->callout);
858 cp->callout = timeout(RETRY_INTERVAL, wakeup,
859 (opaque_t) get_mntfs_wchan(mf));
861 mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;
864 * Not done yet - so don't return anything
870 mf->mf_error = this_error;
871 mf->mf_flags |= MFF_ERROR;
872 #ifdef HAVE_FS_AUTOFS
873 if (mp->am_autofs_fh)
874 autofs_release_fh(mp);
875 #endif /* HAVE_FS_AUTOFS */
876 if (mf->mf_flags & MFF_MKMNT) {
877 rmdirs(mf->mf_mount);
878 mf->mf_flags &= ~MFF_MKMNT;
881 * Wakeup anything waiting for this mount
883 wakeup(get_mntfs_wchan(mf));
889 * If we get here, then either the mount succeeded or
890 * there is no more mount information available.
893 mp->am_mnt = mf = new_mntfs();
895 #ifdef HAVE_FS_AUTOFS
896 if (mp->am_flags & AMF_AUTOFS)
897 autofs_mount_failed(mp);
899 #endif /* HAVE_FS_AUTOFS */
900 nfs_quick_reply(mp, this_error);
903 hard_error = this_error;
905 hard_error = ETIMEDOUT;
908 * Set a small(ish) timeout on an error node if
909 * the error was not a time out.
911 switch (hard_error) {
925 * Wakeup anything waiting for this mount
927 wakeup(get_mntfs_wchan(mf));
932 * Make sure that the error value in the mntfs has a
935 if (mf->mf_error < 0) {
936 mf->mf_error = hard_error;
938 mf->mf_flags |= MFF_ERROR;
942 * In any case we don't need the continuation any more
944 free_continuation(cp);
951 amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
956 struct mnt_map *mm = (mnt_map *) mf->mf_private;
958 dlog("determining /defaults entry value");
961 * Find out if amd.conf overrode any map-specific /defaults.
963 * HACK ALERT: there's no easy way to find out what the map mount point is
964 * at this point, so I am forced to initialize the mnt_map->cfm field here
965 * for the first time, upon the very first search for a /defaults entry in
966 * this map. This initialization is much better done in mapc_create(),
967 * but it's impossible to do that there with the current code structure.
969 if (mm->cfm == NULL) { /* then initialize it for first time */
970 mm->cfm = find_cf_map(mf->mf_mount);
972 if (mm->cfm && mm->cfm->cfm_defaults) {
973 dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
974 dflts = strdup(mm->cfm->cfm_defaults);
975 } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
976 dlog("/defaults gave %s", dflts);
978 return def_opts; /* if nothing found */
981 /* trim leading '-' in case thee's one */
988 * Chop the defaults up
990 rvec = strsplit(dfl, ' ', '\"');
992 if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
994 * Pick whichever first entry matched the list of selectors.
995 * Strip the selectors from the string, and assign to dfl the
996 * rest of the string.
1002 while (*sp) { /* loop until you find something, if any */
1003 memset((char *) &ap, 0, sizeof(am_opts));
1005 * This next routine cause many spurious "expansion of ... is"
1006 * messages, which are ignored, b/c all we need out of this
1007 * routine is to match selectors. These spurious messages may
1008 * be wrong, esp. if they try to expand ${key} b/c it will
1009 * get expanded to "/defaults"
1011 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1012 mp->am_parent->am_mnt->mf_info);
1013 free_opts(&ap); /* don't leak */
1014 if (pt == &amfs_error_ops) {
1015 plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1017 dfl = strip_selectors(*sp, "/defaults");
1018 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1024 } else { /* not selectors_in_defaults */
1026 * Extract first value
1032 * If there were any values at all...
1036 * Log error if there were other values
1038 if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1039 dlog("/defaults chopped into %s", dfl);
1040 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1044 * Prepend to existing defaults if they exist,
1045 * otherwise just use these defaults.
1047 if (*def_opts && *dfl) {
1048 size_t l = strlen(def_opts) + strlen(dfl) + 2;
1049 char *nopts = (char *) xmalloc(l);
1050 xsnprintf(nopts, l, "%s;%s", dfl, def_opts);
1054 def_opts = strealloc(def_opts, dfl);
1060 /* don't need info vector any more */
1069 amfs_generic_mount_child(am_node *new_mp, int *error_return)
1072 struct continuation *cp; /* Continuation structure if need to mount */
1074 dlog("in amfs_generic_mount_child");
1076 *error_return = error = 0; /* Error so far */
1078 /* we have an errorfs attached to the am_node, free it */
1079 free_mntfs(new_mp->am_mnt);
1083 * Construct a continuation
1085 cp = ALLOC(struct continuation);
1089 cp->start = clocktime(NULL);
1090 cp->mf = new_mp->am_mfarray;
1093 * Try and mount the file system. If this succeeds immediately (possible
1094 * for a ufs file system) then return the attributes, otherwise just
1097 error = amfs_bgmount(cp);
1098 reschedule_timeout_mp();
1103 * Code for quick reply. If current_transp is set, then it's the
1104 * transp that's been passed down from nfs_program_2() or from
1105 * autofs_program_[123]().
1106 * If new_mp->am_transp is not already set, set it by copying in
1107 * current_transp. Once am_transp is set, nfs_quick_reply() and
1108 * autofs_mount_succeeded() can use it to send a reply to the
1109 * client that requested this mount.
1111 if (current_transp && !new_mp->am_transp) {
1112 dlog("Saving RPC transport for %s", new_mp->am_path);
1113 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1114 *(new_mp->am_transp) = *current_transp;
1116 if (error && new_mp->am_mnt && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1117 new_mp->am_error = error;
1119 if (new_mp->am_error > 0)
1120 assign_error_mntfs(new_mp);
1127 * Automount interface to RPC lookup routine
1128 * Find the corresponding entry and return
1129 * the file handle for it.
1132 amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1138 dlog("in amfs_generic_lookup_child");
1141 new_mp = amfs_lookup_node(mp, fname, error_return);
1143 /* return if we got an error */
1144 if (!new_mp || *error_return > 0)
1147 /* also return if it's already mounted and known to be up */
1148 if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server))
1154 * If doing a delete then don't create again!
1161 /* save error_return */
1162 mp_error = *error_return;
1164 mf_array = amfs_lookup_mntfs(new_mp, error_return);
1166 new_mp->am_error = new_mp->am_mnt->mf_error = *error_return;
1172 * Already mounted but known to be down:
1173 * check if we have any alternatives to mount
1175 if (mp_error == 0) {
1177 for (mfp = mf_array; *mfp; mfp++)
1178 if (*mfp != new_mp->am_mnt)
1182 * we found an alternative, so try mounting again.
1186 for (mfp = mf_array; *mfp; mfp++)
1189 if (new_mp->am_flags & AMF_SOFTLOOKUP) {
1198 /* store the array inside the am_node */
1199 new_mp->am_mfarray = mf_array;
1202 * Note: while it might seem like a good idea to prioritize
1203 * the list of mntfs's we got here, it probably isn't.
1204 * It would ignore the ordering of entries specified by the user,
1205 * which is counterintuitive and confusing.
1212 amfs_generic_mounted(mntfs *mf)
1214 amfs_mkcacheref(mf);
1219 * Unmount an automount sub-node
1222 amfs_generic_umount(am_node *mp, mntfs *mf)
1226 #ifdef HAVE_FS_AUTOFS
1227 int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
1228 if (mf->mf_flags & MFF_IS_AUTOFS)
1229 error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
1230 #endif /* HAVE_FS_AUTOFS */
1237 amfs_generic_match(am_opts *fo)
1242 plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)");
1246 plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)");
1251 * Swap round fs:= and rfs:= options
1252 * ... historical (jsp)
1255 fo->opt_rfs = fo->opt_fs;
1259 * mtab entry turns out to be the name of the mount map
1261 return strdup(fo->opt_rfs ? fo->opt_rfs : ".");