No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / am-utils / dist / amd / amfs_generic.c
blob94afbca74c582a999860afdfe5342ad42d49879f
1 /* $NetBSD$ */
3 /*
4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
42 * File: am-utils/amd/amfs_generic.c
47 * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amd.h>
57 /****************************************************************************
58 *** MACROS ***
59 ****************************************************************************/
60 #define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
63 /****************************************************************************
64 *** STRUCTURES ***
65 ****************************************************************************/
67 * Mounting a file system may take a significant period of time. The
68 * problem is that if this is done in the main process thread then the
69 * entire automounter could be blocked, possibly hanging lots of processes
70 * on the system. Instead we use a continuation scheme to allow mounts to
71 * be attempted in a sub-process. When the sub-process exits we pick up the
72 * exit status (by convention a UN*X error number) and continue in a
73 * notifier. The notifier gets handed a data structure and can then
74 * determine whether the mount was successful or not. If not, it updates
75 * the data structure and tries again until there are no more ways to try
76 * the mount, or some other permanent error occurs. In the mean time no RPC
77 * reply is sent, even after the mount is successful. We rely on the RPC
78 * retry mechanism to resend the lookup request which can then be handled.
80 struct continuation {
81 am_node *mp; /* Node we are trying to mount */
82 int retry; /* Try again? */
83 time_t start; /* Time we started this mount */
84 int callout; /* Callout identifier */
85 mntfs **mf; /* Current mntfs */
89 /****************************************************************************
90 *** FORWARD DEFINITIONS ***
91 ****************************************************************************/
92 static am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return);
93 static mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
94 char *def_opts, char *pfname);
95 static mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return);
96 static void amfs_cont(int rc, int term, opaque_t arg);
97 static void amfs_retry(int rc, int term, opaque_t arg);
98 static void free_continuation(struct continuation *cp);
99 static int amfs_bgmount(struct continuation *cp);
100 static char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts);
103 /****************************************************************************
104 *** FUNCTIONS ***
105 ****************************************************************************/
106 static am_node *
107 amfs_lookup_node(am_node *mp, char *fname, int *error_return)
109 am_node *new_mp;
110 int error = 0; /* Error so far */
111 int in_progress = 0; /* # of (un)mount in progress */
112 mntfs *mf;
113 char *expanded_fname = NULL;
115 dlog("in amfs_lookup_node");
118 * If the server is shutting down
119 * then don't return information
120 * about the mount point.
122 if (amd_state == Finishing) {
123 if (mp->am_mnt == 0 || mp->am_mnt->mf_fsflags & FS_DIRECT) {
124 dlog("%s mount ignored - going down", fname);
125 } else {
126 dlog("%s/%s mount ignored - going down", mp->am_path, fname);
128 ereturn(ENOENT);
132 * Handle special case of "." and ".."
134 if (fname[0] == '.') {
135 if (fname[1] == '\0')
136 return mp; /* "." is the current node */
137 if (fname[1] == '.' && fname[2] == '\0') {
138 if (mp->am_parent) {
139 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
140 return mp->am_parent; /* ".." is the parent node */
142 ereturn(ESTALE);
147 * Check for valid key name.
148 * If it is invalid then pretend it doesn't exist.
150 if (!valid_key(fname)) {
151 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
152 ereturn(ENOENT);
156 * Expand key name.
157 * expanded_fname is now a private copy.
159 expanded_fname = expand_selectors(fname);
162 * Search children of this node
164 for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) {
165 if (FSTREQ(new_mp->am_name, expanded_fname)) {
166 if (new_mp->am_error) {
167 error = new_mp->am_error;
168 continue;
172 * If the error code is undefined then it must be
173 * in progress.
175 mf = new_mp->am_mnt;
176 if (mf->mf_error < 0)
177 goto in_progrss;
180 * If there was a previous error with this node
181 * then return that error code.
183 if (mf->mf_flags & MFF_ERROR) {
184 error = mf->mf_error;
185 continue;
187 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
188 in_progrss:
190 * If the fs is not mounted or it is unmounting then there
191 * is a background (un)mount in progress. In this case
192 * we just drop the RPC request (return nil) and
193 * wait for a retry, by which time the (un)mount may
194 * have completed.
196 dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x",
197 expanded_fname, mf->mf_mount,
198 (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags);
199 in_progress++;
200 if (mf->mf_flags & MFF_UNMOUNTING) {
201 dlog("will remount later");
202 new_mp->am_flags |= AMF_REMOUNT;
204 continue;
208 * Otherwise we have a hit: return the current mount point.
210 dlog("matched %s in %s", expanded_fname, new_mp->am_path);
211 XFREE(expanded_fname);
212 return new_mp;
216 if (in_progress) {
217 dlog("Waiting while %d mount(s) in progress", in_progress);
218 XFREE(expanded_fname);
219 ereturn(-1);
223 * If an error occurred then return it.
225 if (error) {
226 dlog("Returning error: %s", strerror(error));
227 XFREE(expanded_fname);
228 ereturn(error);
232 * If the server is going down then just return,
233 * don't try to mount any more file systems
235 if ((int) amd_state >= (int) Finishing) {
236 dlog("not found - server going down anyway");
237 ereturn(ENOENT);
241 * Allocate a new map
243 new_mp = get_ap_child(mp, expanded_fname);
244 XFREE(expanded_fname);
245 if (new_mp == NULL)
246 ereturn(ENOSPC);
248 *error_return = -1;
249 return new_mp;
254 static mntfs *
255 amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
256 char *def_opts, char *pfname)
258 am_ops *p;
259 am_opts *fs_opts;
260 mntfs *new_mf;
261 char *mp_dir = NULL;
262 #ifdef HAVE_FS_AUTOFS
263 int on_autofs = 1;
264 #endif /* HAVE_FS_AUTOFS */
266 /* match the operators */
267 fs_opts = CALLOC(am_opts);
268 p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path,
269 pfname, mf->mf_info);
270 #ifdef HAVE_FS_AUTOFS
271 /* XXX: this should be factored out into an autofs-specific function */
272 if (new_mp->am_flags & AMF_AUTOFS) {
273 /* ignore user-provided fs if we're using autofs */
274 if (fs_opts->opt_sublink && fs_opts->opt_sublink[0]) {
276 * For sublinks we need to use a hack with autofs:
277 * mount the filesystem on the original opt_fs (which is NOT an
278 * autofs mountpoint) and symlink (or lofs-mount) to it from
279 * the autofs mountpoint.
281 on_autofs = 0;
282 mp_dir = fs_opts->opt_fs;
283 } else {
284 if (p->autofs_fs_flags & FS_ON_AUTOFS) {
285 mp_dir = new_mp->am_path;
286 } else {
287 mp_dir = fs_opts->opt_fs;
288 on_autofs = 0;
291 } else
292 #endif /* HAVE_FS_AUTOFS */
293 mp_dir = fs_opts->opt_fs;
296 * Find or allocate a filesystem for this node.
298 new_mf = find_mntfs(p, fs_opts,
299 mp_dir,
300 fs_opts->fs_mtab,
301 def_opts,
302 fs_opts->opt_opts,
303 fs_opts->opt_remopts);
306 * See whether this is a real filesystem
308 p = new_mf->mf_ops;
309 if (p == &amfs_error_ops) {
310 plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path);
311 free_mntfs(new_mf);
312 return NULL;
315 dlog("Got a hit with %s", p->fs_type);
317 #ifdef HAVE_FS_AUTOFS
318 if (new_mp->am_flags & AMF_AUTOFS && on_autofs) {
319 new_mf->mf_flags |= MFF_ON_AUTOFS;
320 new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags;
323 * A new filesystem is an autofs filesystems if:
324 * 1. it claims it can be one (has the FS_AUTOFS flag)
325 * 2. autofs is enabled system-wide
326 * 3. either has an autofs parent,
327 * or it is explicitly requested to be autofs.
329 if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS &&
330 amd_use_autofs &&
331 ((mf->mf_flags & MFF_IS_AUTOFS) ||
332 (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type &&
333 STREQ(new_mf->mf_fo->opt_mount_type, "autofs"))))
334 new_mf->mf_flags |= MFF_IS_AUTOFS;
335 #endif /* HAVE_FS_AUTOFS */
337 return new_mf;
341 static mntfs **
342 amfs_lookup_mntfs(am_node *new_mp, int *error_return)
344 am_node *mp;
345 char *info; /* Mount info - where to get the file system */
346 char **ivecs, **cur_ivec; /* Split version of info */
347 int num_ivecs;
348 char *orig_def_opts; /* Original Automount options */
349 char *def_opts; /* Automount options */
350 int error = 0; /* Error so far */
351 char path_name[MAXPATHLEN]; /* General path name buffer */
352 char *pfname; /* Path for database lookup */
353 mntfs *mf, **mf_array;
354 int count;
356 dlog("in amfs_lookup_mntfs");
358 mp = new_mp->am_parent;
361 * If we get here then this is a reference to an,
362 * as yet, unknown name so we need to search the mount
363 * map for it.
365 if (mp->am_pref) {
366 if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name))
367 ereturn(ENAMETOOLONG);
368 xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name);
369 pfname = path_name;
370 } else {
371 pfname = new_mp->am_name;
374 mf = mp->am_mnt;
376 dlog("will search map info in %s to find %s", mf->mf_info, pfname);
378 * Consult the oracle for some mount information.
379 * info is malloc'ed and belongs to this routine.
380 * It ends up being free'd in free_continuation().
382 * Note that this may return -1 indicating that information
383 * is not yet available.
385 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
386 if (error) {
387 if (error > 0)
388 plog(XLOG_MAP, "No map entry for %s", pfname);
389 else
390 plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
391 ereturn(error);
393 dlog("mount info is %s", info);
396 * Split info into an argument vector.
397 * The vector is malloc'ed and belongs to
398 * this routine. It is free'd further down.
400 * Note: the vector pointers point into info, so don't free it!
402 ivecs = strsplit(info, ' ', '\"');
404 if (mf->mf_auto)
405 def_opts = mf->mf_auto;
406 else
407 def_opts = "";
409 orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts));
410 def_opts = strdup(orig_def_opts);
412 /* first build our defaults */
413 num_ivecs = 0;
414 for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
415 if (**cur_ivec == '-') {
417 * Pick up new defaults
419 char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1);
420 XFREE(def_opts);
421 def_opts = new_def_opts;
422 dlog("Setting def_opts to \"%s\"", def_opts);
423 continue;
424 } else
425 num_ivecs++;
428 mf_array = calloc(num_ivecs + 1, sizeof(mntfs *));
430 /* construct the array of struct mntfs for this mount point */
431 for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
432 mntfs *new_mf;
434 if (**cur_ivec == '-') {
435 XFREE(def_opts);
436 if ((*cur_ivec)[1] == '\0') {
438 * If we have a single dash '-' than we need to reset the
439 * default options.
441 def_opts = strdup(orig_def_opts);
442 dlog("Resetting the default options, a single dash '-' was found.");
443 } else {
444 /* append options to /default options */
445 def_opts = str3cat((char *) NULL, orig_def_opts, ";", *cur_ivec + 1);
446 dlog("Resetting def_opts to \"%s\"", def_opts);
448 continue;
452 * If a mntfs has already been found, and we find
453 * a cut then don't try any more locations.
455 * XXX: we do not know when the "/" was added as an equivalent for "||".
456 * It's undocumented, it might go away at any time. Caveat emptor.
458 if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
459 if (count > 0) {
460 dlog("Cut: not trying any more locations for %s", mp->am_path);
461 break;
463 continue;
466 new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname);
467 if (new_mf == NULL)
468 continue;
469 mf_array[count++] = new_mf;
472 /* We're done with ivecs */
473 XFREE(ivecs);
474 XFREE(info);
475 XFREE(orig_def_opts);
476 XFREE(def_opts);
477 if (count == 0) { /* no match */
478 XFREE(mf_array);
479 ereturn(ENOENT);
482 return mf_array;
487 * The continuation function. This is called by
488 * the task notifier when a background mount attempt
489 * completes.
491 static void
492 amfs_cont(int rc, int term, opaque_t arg)
494 struct continuation *cp = (struct continuation *) arg;
495 am_node *mp = cp->mp;
496 mntfs *mf = mp->am_mnt;
498 dlog("amfs_cont: '%s'", mp->am_path);
501 * Definitely not trying to mount at the moment
503 mf->mf_flags &= ~MFF_MOUNTING;
506 * While we are mounting - try to avoid race conditions
508 new_ttl(mp);
511 * Wakeup anything waiting for this mount
513 wakeup(get_mntfs_wchan(mf));
516 * Check for termination signal or exit status...
518 if (rc || term) {
519 #ifdef HAVE_FS_AUTOFS
520 if (mf->mf_flags & MFF_IS_AUTOFS &&
521 !(mf->mf_flags & MFF_MOUNTED))
522 autofs_release_fh(mp);
523 #endif /* HAVE_FS_AUTOFS */
525 if (term) {
527 * Not sure what to do for an error code.
529 mf->mf_error = EIO; /* XXX ? */
530 mf->mf_flags |= MFF_ERROR;
531 plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
532 } else {
534 * Check for exit status...
536 #ifdef __linux__
538 * HACK ALERT!
540 * On Linux (and maybe not only) it's possible to run
541 * an amd which "knows" how to mount certain combinations
542 * of nfs_proto/nfs_version which the kernel doesn't grok.
543 * So if we got an EINVAL and we have a server that's not
544 * using NFSv2/UDP, try again with NFSv2/UDP.
546 * Too bad that there is no way to dynamically determine
547 * what combinations the _client_ supports, as opposed to
548 * what the _server_ supports...
550 if (rc == EINVAL &&
551 mf->mf_server &&
552 (mf->mf_server->fs_version != 2 ||
553 !STREQ(mf->mf_server->fs_proto, "udp")))
554 mf->mf_flags |= MFF_NFS_SCALEDOWN;
555 else
556 #endif /* __linux__ */
558 mf->mf_error = rc;
559 mf->mf_flags |= MFF_ERROR;
560 errno = rc; /* XXX */
561 if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx"))
562 plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
566 if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
568 * If we get here then that attempt didn't work, so
569 * move the info vector pointer along by one and
570 * call the background mount routine again
572 amd_stats.d_merr++;
573 cp->mf++;
575 amfs_bgmount(cp);
576 if (mp->am_error > 0)
577 assign_error_mntfs(mp);
578 } else {
580 * The mount worked.
582 dlog("Mounting %s returned success", cp->mp->am_path);
583 am_mounted(cp->mp);
584 free_continuation(cp);
587 reschedule_timeout_mp();
592 * Retry a mount
594 static void
595 amfs_retry(int rc, int term, opaque_t arg)
597 struct continuation *cp = (struct continuation *) arg;
598 am_node *mp = cp->mp;
599 int error = 0;
601 dlog("Commencing retry for mount of %s", mp->am_path);
603 new_ttl(mp);
605 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) {
607 * The entire mount has timed out. Set the error code and skip past all
608 * the mntfs's so that amfs_bgmount will not have any more
609 * ways to try the mount, thus causing an error.
611 plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
612 error = ETIMEDOUT;
613 while (*cp->mf)
614 cp->mf++;
615 /* explicitly forbid further retries after timeout */
616 cp->retry = FALSE;
618 if (error || !IN_PROGRESS(cp))
619 error = amfs_bgmount(cp);
621 reschedule_timeout_mp();
626 * Discard an old continuation
628 static void
629 free_continuation(struct continuation *cp)
631 mntfs **mfp;
633 dlog("free_continuation");
634 if (cp->callout)
635 untimeout(cp->callout);
637 * we must free the mntfs's in the list.
638 * so free all of them if there was an error,
639 * or free all but the used one, if the mount succeeded.
641 for (mfp = cp->mp->am_mfarray; *mfp; mfp++) {
642 free_mntfs(*mfp);
644 XFREE(cp->mp->am_mfarray);
645 XFREE(cp);
650 * Pick a file system to try mounting and
651 * do that in the background if necessary
653 For each location:
654 discard previous mount location if required
655 fetch next mount location
656 if the filesystem failed to be mounted then
657 this_error = error from filesystem
658 goto failed
659 if the filesystem is mounting or unmounting then
660 goto retry;
661 if the fileserver is down then
662 this_error = EIO
663 continue;
664 if the filesystem is already mounted
665 break
668 this_error = initialize mount point
670 if no error on this mount and mount is delayed then
671 this_error = -1
673 if this_error < 0 then
674 retry = true
676 if no error on this mount then
677 if mount in background then
678 run mount in background
679 return -1
680 else
681 this_error = mount in foreground
684 if an error occurred on this mount then
685 update stats
686 save error in mount point
688 endfor
690 static int
691 amfs_bgmount(struct continuation *cp)
693 am_node *mp = cp->mp;
694 mntfs *mf; /* Current mntfs */
695 int this_error = -1; /* Per-mount error */
696 int hard_error = -1; /* Cumulative per-node error */
698 if (mp->am_mnt)
699 free_mntfs(mp->am_mnt);
702 * Try to mount each location.
703 * At the end:
704 * hard_error == 0 indicates something was mounted.
705 * hard_error > 0 indicates everything failed with a hard error
706 * hard_error < 0 indicates nothing could be mounted now
708 for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) {
709 am_ops *p;
711 mf = dup_mntfs(mp->am_mnt);
712 p = mf->mf_ops;
714 if (hard_error < 0)
715 hard_error = this_error;
716 this_error = 0;
718 if (mf->mf_error > 0) {
719 this_error = mf->mf_error;
720 goto failed;
723 if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
725 * Still mounting - retry later
727 dlog("mount of \"%s\" already pending", mf->mf_info);
728 goto retry;
731 if (FSRV_ISDOWN(mf->mf_server)) {
733 * Would just mount from the same place
734 * as a hung mount - so give up
736 dlog("%s is already hung - giving up", mf->mf_server->fs_host);
737 this_error = EIO;
738 goto failed;
741 if (mp->am_link) {
742 XFREE(mp->am_link);
743 mp->am_link = NULL;
745 if (mf->mf_fo && mf->mf_fo->opt_sublink && mf->mf_fo->opt_sublink[0])
746 mp->am_link = strdup(mf->mf_fo->opt_sublink);
749 * Will usually need to play around with the mount nodes
750 * file attribute structure. This must be done here.
751 * Try and get things initialized, even if the fileserver
752 * is not known to be up. In the common case this will
753 * progress things faster.
757 * Fill in attribute fields.
759 if (mf->mf_fsflags & FS_DIRECTORY)
760 mk_fattr(&mp->am_fattr, NFDIR);
761 else
762 mk_fattr(&mp->am_fattr, NFLNK);
764 if (mf->mf_flags & MFF_MOUNTED) {
765 dlog("duplicate mount of \"%s\" ...", mf->mf_info);
767 * Skip initial processing of the mountpoint if already mounted.
768 * This could happen if we have multiple sublinks into the same f/s,
769 * or if we are restarting an already-mounted filesystem.
771 goto already_mounted;
774 if (mf->mf_fo && mf->mf_fo->fs_mtab) {
775 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
776 mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
777 mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
780 if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
781 this_error = p->fs_init(mf);
783 if (this_error > 0)
784 goto failed;
785 if (this_error < 0)
786 goto retry;
788 if (mf->mf_fo && mf->mf_fo->opt_delay) {
790 * If there is a delay timer on the mount
791 * then don't try to mount if the timer
792 * has not expired.
794 int i = atoi(mf->mf_fo->opt_delay);
795 time_t now = clocktime(NULL);
796 if (i > 0 && now < (cp->start + i)) {
797 dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start));
798 goto retry;
803 * If the directory is not yet made and it needs to be made, then make it!
805 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
806 plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
807 this_error = mkdirs(mf->mf_mount, 0555);
808 if (this_error) {
809 plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
810 goto failed;
812 mf->mf_flags |= MFF_MKMNT;
815 #ifdef HAVE_FS_AUTOFS
816 if (mf->mf_flags & MFF_IS_AUTOFS)
817 if ((this_error = autofs_get_fh(mp)))
818 goto failed;
819 #endif /* HAVE_FS_AUTOFS */
821 already_mounted:
822 mf->mf_flags |= MFF_MOUNTING;
823 if (mf->mf_fsflags & FS_MBACKGROUND) {
824 dlog("backgrounding mount of \"%s\"", mf->mf_mount);
825 if (cp->callout) {
826 untimeout(cp->callout);
827 cp->callout = 0;
830 /* actually run the task, backgrounding as necessary */
831 run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
832 return -1;
833 } else {
834 dlog("foreground mount of \"%s\" ...", mf->mf_mount);
835 this_error = mount_node((opaque_t) mp);
838 mf->mf_flags &= ~MFF_MOUNTING;
839 if (this_error > 0)
840 goto failed;
841 if (this_error == 0) {
842 am_mounted(mp);
843 break; /* Success */
846 retry:
847 if (!cp->retry)
848 continue;
849 dlog("will retry ...\n");
852 * Arrange that amfs_bgmount is called
853 * after anything else happens.
855 dlog("Arranging to retry mount of %s", mp->am_path);
856 sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
857 if (cp->callout)
858 untimeout(cp->callout);
859 cp->callout = timeout(RETRY_INTERVAL, wakeup,
860 (opaque_t) get_mntfs_wchan(mf));
862 mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;
865 * Not done yet - so don't return anything
867 return -1;
869 failed:
870 amd_stats.d_merr++;
871 mf->mf_error = this_error;
872 mf->mf_flags |= MFF_ERROR;
873 #ifdef HAVE_FS_AUTOFS
874 if (mp->am_autofs_fh)
875 autofs_release_fh(mp);
876 #endif /* HAVE_FS_AUTOFS */
877 if (mf->mf_flags & MFF_MKMNT) {
878 rmdirs(mf->mf_mount);
879 mf->mf_flags &= ~MFF_MKMNT;
882 * Wakeup anything waiting for this mount
884 wakeup(get_mntfs_wchan(mf));
885 free_mntfs(mf);
886 /* continue */
890 * If we get here, then either the mount succeeded or
891 * there is no more mount information available.
893 if (this_error) {
894 mp->am_mnt = mf = new_mntfs();
896 #ifdef HAVE_FS_AUTOFS
897 if (mp->am_flags & AMF_AUTOFS)
898 autofs_mount_failed(mp);
899 else
900 #endif /* HAVE_FS_AUTOFS */
901 nfs_quick_reply(mp, this_error);
903 if (hard_error <= 0)
904 hard_error = this_error;
905 if (hard_error < 0)
906 hard_error = ETIMEDOUT;
909 * Set a small(ish) timeout on an error node if
910 * the error was not a time out.
912 switch (hard_error) {
913 case ETIMEDOUT:
914 case EWOULDBLOCK:
915 case EIO:
916 mp->am_timeo = 17;
917 break;
918 default:
919 mp->am_timeo = 5;
920 break;
922 new_ttl(mp);
923 } else {
924 mf = mp->am_mnt;
926 * Wakeup anything waiting for this mount
928 wakeup(get_mntfs_wchan(mf));
929 hard_error = 0;
933 * Make sure that the error value in the mntfs has a
934 * reasonable value.
936 if (mf->mf_error < 0) {
937 mf->mf_error = hard_error;
938 if (hard_error)
939 mf->mf_flags |= MFF_ERROR;
943 * In any case we don't need the continuation any more
945 free_continuation(cp);
947 return hard_error;
951 static char *
952 amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
954 char *dflts;
955 char *dfl;
956 char **rvec = NULL;
957 struct mnt_map *mm = (mnt_map *) mf->mf_private;
959 dlog("determining /defaults entry value");
962 * Find out if amd.conf overrode any map-specific /defaults.
964 if (mm->cfm && mm->cfm->cfm_defaults) {
965 dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
966 dflts = strdup(mm->cfm->cfm_defaults);
967 } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
968 dlog("/defaults gave %s", dflts);
969 } else {
970 return def_opts; /* if nothing found */
973 /* trim leading '-' in case thee's one */
974 if (*dflts == '-')
975 dfl = dflts + 1;
976 else
977 dfl = dflts;
980 * Chop the defaults up
982 rvec = strsplit(dfl, ' ', '\"');
984 if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
986 * Pick whichever first entry matched the list of selectors.
987 * Strip the selectors from the string, and assign to dfl the
988 * rest of the string.
990 if (rvec) {
991 am_opts ap;
992 am_ops *pt;
993 char **sp = rvec;
994 while (*sp) { /* loop until you find something, if any */
995 memset((char *) &ap, 0, sizeof(am_opts));
997 * This next routine cause many spurious "expansion of ... is"
998 * messages, which are ignored, b/c all we need out of this
999 * routine is to match selectors. These spurious messages may
1000 * be wrong, esp. if they try to expand ${key} b/c it will
1001 * get expanded to "/defaults"
1003 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1004 mp->am_parent->am_mnt->mf_info);
1005 free_opts(&ap); /* don't leak */
1006 if (pt == &amfs_error_ops) {
1007 plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1008 } else {
1009 dfl = strip_selectors(*sp, "/defaults");
1010 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1011 break;
1013 ++sp;
1016 } else { /* not selectors_in_defaults */
1018 * Extract first value
1020 dfl = rvec[0];
1024 * If there were any values at all...
1026 if (dfl) {
1028 * Log error if there were other values
1030 if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1031 dlog("/defaults chopped into %s", dfl);
1032 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1036 * Prepend to existing defaults if they exist,
1037 * otherwise just use these defaults.
1039 if (*def_opts && *dfl) {
1040 size_t l = strlen(def_opts) + strlen(dfl) + 2;
1041 char *nopts = (char *) xmalloc(l);
1042 xsnprintf(nopts, l, "%s;%s", dfl, def_opts);
1043 XFREE(def_opts);
1044 def_opts = nopts;
1045 } else if (*dfl) {
1046 def_opts = strealloc(def_opts, dfl);
1050 XFREE(dflts);
1052 /* don't need info vector any more */
1053 if (rvec)
1054 XFREE(rvec);
1056 return def_opts;
1060 am_node *
1061 amfs_generic_mount_child(am_node *new_mp, int *error_return)
1063 int error;
1064 struct continuation *cp; /* Continuation structure if need to mount */
1066 dlog("in amfs_generic_mount_child");
1068 *error_return = error = 0; /* Error so far */
1070 /* we have an errorfs attached to the am_node, free it */
1071 free_mntfs(new_mp->am_mnt);
1072 new_mp->am_mnt = NULL;
1075 * Construct a continuation
1077 cp = ALLOC(struct continuation);
1078 cp->callout = 0;
1079 cp->mp = new_mp;
1080 cp->retry = TRUE;
1081 cp->start = clocktime(NULL);
1082 cp->mf = new_mp->am_mfarray;
1085 * Try and mount the file system. If this succeeds immediately (possible
1086 * for a ufs file system) then return the attributes, otherwise just
1087 * return an error.
1089 error = amfs_bgmount(cp);
1090 reschedule_timeout_mp();
1091 if (!error)
1092 return new_mp;
1095 * Code for quick reply. If current_transp is set, then it's the
1096 * transp that's been passed down from nfs_program_2() or from
1097 * autofs_program_[123]().
1098 * If new_mp->am_transp is not already set, set it by copying in
1099 * current_transp. Once am_transp is set, nfs_quick_reply() and
1100 * autofs_mount_succeeded() can use it to send a reply to the
1101 * client that requested this mount.
1103 if (current_transp && !new_mp->am_transp) {
1104 dlog("Saving RPC transport for %s", new_mp->am_path);
1105 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1106 *(new_mp->am_transp) = *current_transp;
1108 if (error && new_mp->am_mnt && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1109 new_mp->am_error = error;
1111 if (new_mp->am_error > 0)
1112 assign_error_mntfs(new_mp);
1114 ereturn(error);
1119 * Automount interface to RPC lookup routine
1120 * Find the corresponding entry and return
1121 * the file handle for it.
1123 am_node *
1124 amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1126 am_node *new_mp;
1127 mntfs **mf_array;
1128 int mp_error;
1130 dlog("in amfs_generic_lookup_child");
1132 *error_return = 0;
1133 new_mp = amfs_lookup_node(mp, fname, error_return);
1135 /* return if we got an error */
1136 if (!new_mp || *error_return > 0)
1137 return new_mp;
1139 /* also return if it's already mounted and known to be up */
1140 if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server))
1141 return new_mp;
1143 switch (op) {
1144 case VLOOK_DELETE:
1146 * If doing a delete then don't create again!
1148 ereturn(ENOENT);
1149 case VLOOK_LOOKUP:
1150 return new_mp;
1153 /* save error_return */
1154 mp_error = *error_return;
1156 mf_array = amfs_lookup_mntfs(new_mp, error_return);
1157 if (!mf_array) {
1158 new_mp->am_error = new_mp->am_mnt->mf_error = *error_return;
1159 free_map(new_mp);
1160 return NULL;
1164 * Already mounted but known to be down:
1165 * check if we have any alternatives to mount
1167 if (mp_error == 0) {
1168 mntfs **mfp;
1169 for (mfp = mf_array; *mfp; mfp++)
1170 if (*mfp != new_mp->am_mnt)
1171 break;
1172 if (*mfp != NULL) {
1174 * we found an alternative, so try mounting again.
1176 *error_return = -1;
1177 } else {
1178 for (mfp = mf_array; *mfp; mfp++)
1179 free_mntfs(*mfp);
1180 XFREE(mf_array);
1181 if (new_mp->am_flags & AMF_SOFTLOOKUP) {
1182 ereturn(EIO);
1183 } else {
1184 *error_return = 0;
1185 return new_mp;
1190 /* store the array inside the am_node */
1191 new_mp->am_mfarray = mf_array;
1194 * Note: while it might seem like a good idea to prioritize
1195 * the list of mntfs's we got here, it probably isn't.
1196 * It would ignore the ordering of entries specified by the user,
1197 * which is counterintuitive and confusing.
1199 return new_mp;
1203 void
1204 amfs_generic_mounted(mntfs *mf)
1206 amfs_mkcacheref(mf);
1211 * Unmount an automount sub-node
1214 amfs_generic_umount(am_node *mp, mntfs *mf)
1216 int error = 0;
1218 #ifdef HAVE_FS_AUTOFS
1219 int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
1220 if (mf->mf_flags & MFF_IS_AUTOFS)
1221 error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags);
1222 #endif /* HAVE_FS_AUTOFS */
1224 return error;
1228 char *
1229 amfs_generic_match(am_opts *fo)
1231 char *p;
1233 if (!fo->opt_rfs) {
1234 plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)");
1235 return 0;
1237 if (!fo->opt_fs) {
1238 plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)");
1239 return 0;
1243 * Swap round fs:= and rfs:= options
1244 * ... historical (jsp)
1246 p = fo->opt_rfs;
1247 fo->opt_rfs = fo->opt_fs;
1248 fo->opt_fs = p;
1251 * mtab entry turns out to be the name of the mount map
1253 return strdup(fo->opt_rfs ? fo->opt_rfs : ".");