1 /*	$NetBSD: amfs_generic.c,v 1.1.1.2 2009/03/20 20:26:49 christos Exp $	*/
2 
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.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Jan-Simon Pendry at Imperial College, London.
12  *
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.
28  *
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.
40  *
41  *
42  * File: am-utils/amd/amfs_generic.c
43  *
44  */
45 
46 /*
47  * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
48  */
49 
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amd.h>
55 
56 
57 /****************************************************************************
58  *** MACROS                                                               ***
59  ****************************************************************************/
60 #define	IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
61 
62 
63 /****************************************************************************
64  *** STRUCTURES                                                           ***
65  ****************************************************************************/
66 /*
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.
79  */
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 */
86 };
87 
88 
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);
101 
102 
103 /****************************************************************************
104  *** FUNCTIONS                                                             ***
105  ****************************************************************************/
106 static am_node *
107 amfs_lookup_node(am_node *mp, char *fname, int *error_return)
108 {
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;
114 
115   dlog("in amfs_lookup_node");
116 
117   /*
118    * If the server is shutting down
119    * then don't return information
120    * about the mount point.
121    */
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);
127     }
128     ereturn(ENOENT);
129   }
130 
131   /*
132    * Handle special case of "." and ".."
133    */
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 */
141       }
142       ereturn(ESTALE);
143     }
144   }
145 
146   /*
147    * Check for valid key name.
148    * If it is invalid then pretend it doesn't exist.
149    */
150   if (!valid_key(fname)) {
151     plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
152     ereturn(ENOENT);
153   }
154 
155   /*
156    * Expand key name.
157    * expanded_fname is now a private copy.
158    */
159   expanded_fname = expand_selectors(fname);
160 
161   /*
162    * Search children of this node
163    */
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;
169       }
170 
171       /*
172        * If the error code is undefined then it must be
173        * in progress.
174        */
175       mf = new_mp->am_mnt;
176       if (mf->mf_error < 0)
177 	goto in_progrss;
178 
179       /*
180        * If there was a previous error with this node
181        * then return that error code.
182        */
183       if (mf->mf_flags & MFF_ERROR) {
184 	error = mf->mf_error;
185 	continue;
186       }
187       if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
188       in_progrss:
189 	/*
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.
195 	 */
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;
203 	}
204 	continue;
205       }
206 
207       /*
208        * Otherwise we have a hit: return the current mount point.
209        */
210       dlog("matched %s in %s", expanded_fname, new_mp->am_path);
211       XFREE(expanded_fname);
212       return new_mp;
213     }
214   }
215 
216   if (in_progress) {
217     dlog("Waiting while %d mount(s) in progress", in_progress);
218     XFREE(expanded_fname);
219     ereturn(-1);
220   }
221 
222   /*
223    * If an error occurred then return it.
224    */
225   if (error) {
226     dlog("Returning error: %s", strerror(error));
227     XFREE(expanded_fname);
228     ereturn(error);
229   }
230 
231   /*
232    * If the server is going down then just return,
233    * don't try to mount any more file systems
234    */
235   if ((int) amd_state >= (int) Finishing) {
236     dlog("not found - server going down anyway");
237     ereturn(ENOENT);
238   }
239 
240   /*
241    * Allocate a new map
242    */
243   new_mp = get_ap_child(mp, expanded_fname);
244   XFREE(expanded_fname);
245   if (new_mp == NULL)
246     ereturn(ENOSPC);
247 
248   *error_return = -1;
249   return new_mp;
250 }
251 
252 
253 
254 static mntfs *
255 amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
256 		      char *def_opts, char *pfname)
257 {
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 */
265 
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]) {
275       /*
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.
280        */
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;
289       }
290     }
291   } else
292 #endif /* HAVE_FS_AUTOFS */
293     mp_dir = fs_opts->opt_fs;
294 
295   /*
296    * Find or allocate a filesystem for this node.
297    */
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);
304 
305   /*
306    * See whether this is a real filesystem
307    */
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;
313   }
314 
315   dlog("Got a hit with %s", p->fs_type);
316 
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;
321   }
322   /*
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.
328    */
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 */
336 
337   return new_mf;
338 }
339 
340 
341 static mntfs **
342 amfs_lookup_mntfs(am_node *new_mp, int *error_return)
343 {
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;
355 
356   dlog("in amfs_lookup_mntfs");
357 
358   mp = new_mp->am_parent;
359 
360   /*
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.
364    */
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;
372   }
373 
374   mf = mp->am_mnt;
375 
376   dlog("will search map info in %s to find %s", mf->mf_info, pfname);
377   /*
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().
381    *
382    * Note that this may return -1 indicating that information
383    * is not yet available.
384    */
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);
392   }
393   dlog("mount info is %s", info);
394 
395   /*
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.
399    *
400    * Note: the vector pointers point into info, so don't free it!
401    */
402   ivecs = strsplit(info, ' ', '\"');
403 
404   if (mf->mf_auto)
405     def_opts = mf->mf_auto;
406   else
407     def_opts = "";
408 
409   orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts));
410   def_opts = strdup(orig_def_opts);
411 
412   /* first build our defaults */
413   num_ivecs = 0;
414   for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
415     if (**cur_ivec == '-') {
416       /*
417        * Pick up new defaults
418        */
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++;
426   }
427 
428   mf_array = calloc(num_ivecs + 1, sizeof(mntfs *));
429 
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;
433 
434     if (**cur_ivec == '-') {
435       XFREE(def_opts);
436       if ((*cur_ivec)[1] == '\0') {
437 	/*
438 	 * If we have a single dash '-' than we need to reset the
439 	 * default options.
440 	 */
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);
447       }
448       continue;
449     }
450 
451     /*
452      * If a mntfs has already been found, and we find
453      * a cut then don't try any more locations.
454      *
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.
457      */
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;
462       }
463       continue;
464     }
465 
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;
470   }
471 
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);
480   }
481 
482   return mf_array;
483 }
484 
485 
486 /*
487  * The continuation function.  This is called by
488  * the task notifier when a background mount attempt
489  * completes.
490  */
491 static void
492 amfs_cont(int rc, int term, opaque_t arg)
493 {
494   struct continuation *cp = (struct continuation *) arg;
495   am_node *mp = cp->mp;
496   mntfs *mf = mp->am_mnt;
497 
498   dlog("amfs_cont: '%s'", mp->am_path);
499 
500   /*
501    * Definitely not trying to mount at the moment
502    */
503   mf->mf_flags &= ~MFF_MOUNTING;
504 
505   /*
506    * While we are mounting - try to avoid race conditions
507    */
508   new_ttl(mp);
509 
510   /*
511    * Wakeup anything waiting for this mount
512    */
513   wakeup(get_mntfs_wchan(mf));
514 
515   /*
516    * Check for termination signal or exit status...
517    */
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 */
524 
525     if (term) {
526       /*
527        * Not sure what to do for an error code.
528        */
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 {
533       /*
534        * Check for exit status...
535        */
536 #ifdef __linux__
537       /*
538        * HACK ALERT!
539        *
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.
545        *
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...
549        */
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__ */
557       {
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);
563       }
564     }
565 
566     if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
567       /*
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
571        */
572       amd_stats.d_merr++;
573       cp->mf++;
574     }
575     amfs_bgmount(cp);
576     if (mp->am_error > 0)
577       assign_error_mntfs(mp);
578   } else {
579     /*
580      * The mount worked.
581      */
582     dlog("Mounting %s returned success", cp->mp->am_path);
583     am_mounted(cp->mp);
584     free_continuation(cp);
585   }
586 
587   reschedule_timeout_mp();
588 }
589 
590 
591 /*
592  * Retry a mount
593  */
594 static void
595 amfs_retry(int rc, int term, opaque_t arg)
596 {
597   struct continuation *cp = (struct continuation *) arg;
598   am_node *mp = cp->mp;
599   int error = 0;
600 
601   dlog("Commencing retry for mount of %s", mp->am_path);
602 
603   new_ttl(mp);
604 
605   if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) {
606     /*
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.
610      */
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;
617   }
618   if (error || !IN_PROGRESS(cp))
619     error = amfs_bgmount(cp);
620 
621   reschedule_timeout_mp();
622 }
623 
624 
625 /*
626  * Discard an old continuation
627  */
628 static void
629 free_continuation(struct continuation *cp)
630 {
631   mntfs **mfp;
632 
633   dlog("free_continuation");
634   if (cp->callout)
635     untimeout(cp->callout);
636   /*
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.
640    */
641   for (mfp = cp->mp->am_mfarray; *mfp; mfp++) {
642     free_mntfs(*mfp);
643   }
644   XFREE(cp->mp->am_mfarray);
645   XFREE(cp);
646 }
647 
648 
649 /*
650  * Pick a file system to try mounting and
651  * do that in the background if necessary
652  *
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
666 	fi
667 
668 	this_error = initialize mount point
669 
670 	if no error on this mount and mount is delayed then
671 		this_error = -1
672 	fi
673 	if this_error < 0 then
674 		retry = true
675 	fi
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
682 		fi
683 	fi
684 	if an error occurred on this mount then
685 		update stats
686 		save error in mount point
687 	fi
688 endfor
689  */
690 static int
691 amfs_bgmount(struct continuation *cp)
692 {
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 */
697 
698   if (mp->am_mnt)
699     free_mntfs(mp->am_mnt);
700 
701   /*
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
707    */
708   for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) {
709     am_ops *p;
710 
711     mf = dup_mntfs(mp->am_mnt);
712     p = mf->mf_ops;
713 
714     if (hard_error < 0)
715       hard_error = this_error;
716     this_error = 0;
717 
718     if (mf->mf_error > 0) {
719       this_error = mf->mf_error;
720       goto failed;
721     }
722 
723     if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
724       /*
725        * Still mounting - retry later
726        */
727       dlog("mount of \"%s\" already pending", mf->mf_info);
728       goto retry;
729     }
730 
731     if (FSRV_ISDOWN(mf->mf_server)) {
732       /*
733        * Would just mount from the same place
734        * as a hung mount - so give up
735        */
736       dlog("%s is already hung - giving up", mf->mf_server->fs_host);
737       this_error = EIO;
738       goto failed;
739     }
740 
741     if (mp->am_link) {
742       XFREE(mp->am_link);
743       mp->am_link = NULL;
744     }
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);
747 
748     /*
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.
754      */
755 
756     /*
757      * Fill in attribute fields.
758      */
759     if (mf->mf_fsflags & FS_DIRECTORY)
760       mk_fattr(&mp->am_fattr, NFDIR);
761     else
762       mk_fattr(&mp->am_fattr, NFLNK);
763 
764     if (mf->mf_flags & MFF_MOUNTED) {
765       dlog("duplicate mount of \"%s\" ...", mf->mf_info);
766       /*
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.
770        */
771       goto already_mounted;
772     }
773 
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");
778     }
779 
780     if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
781       this_error = p->fs_init(mf);
782 
783     if (this_error > 0)
784       goto failed;
785     if (this_error < 0)
786       goto retry;
787 
788     if (mf->mf_fo && mf->mf_fo->opt_delay) {
789       /*
790        * If there is a delay timer on the mount
791        * then don't try to mount if the timer
792        * has not expired.
793        */
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;
799       }
800     }
801 
802     /*
803      * If the directory is not yet made and it needs to be made, then make it!
804      */
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;
811       }
812       mf->mf_flags |= MFF_MKMNT;
813     }
814 
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 */
820 
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;
828       }
829 
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);
836     }
837 
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 */
844     }
845 
846   retry:
847     if (!cp->retry)
848       continue;
849     dlog("will retry ...\n");
850 
851     /*
852      * Arrange that amfs_bgmount is called
853      * after anything else happens.
854      */
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));
861 
862     mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL;
863 
864     /*
865      * Not done yet - so don't return anything
866      */
867     return -1;
868 
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;
880     }
881     /*
882      * Wakeup anything waiting for this mount
883      */
884     wakeup(get_mntfs_wchan(mf));
885     free_mntfs(mf);
886     /* continue */
887   }
888 
889   /*
890    * If we get here, then either the mount succeeded or
891    * there is no more mount information available.
892    */
893   if (this_error) {
894     mp->am_mnt = mf = new_mntfs();
895 
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);
902 
903     if (hard_error <= 0)
904       hard_error = this_error;
905     if (hard_error < 0)
906       hard_error = ETIMEDOUT;
907 
908     /*
909      * Set a small(ish) timeout on an error node if
910      * the error was not a time out.
911      */
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;
921     }
922     new_ttl(mp);
923   } else {
924     mf = mp->am_mnt;
925     /*
926      * Wakeup anything waiting for this mount
927      */
928     wakeup(get_mntfs_wchan(mf));
929     hard_error = 0;
930   }
931 
932   /*
933    * Make sure that the error value in the mntfs has a
934    * reasonable value.
935    */
936   if (mf->mf_error < 0) {
937     mf->mf_error = hard_error;
938     if (hard_error)
939       mf->mf_flags |= MFF_ERROR;
940   }
941 
942   /*
943    * In any case we don't need the continuation any more
944    */
945   free_continuation(cp);
946 
947   return hard_error;
948 }
949 
950 
951 static char *
952 amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
953 {
954   char *dflts;
955   char *dfl;
956   char **rvec = NULL;
957   struct mnt_map *mm = (mnt_map *) mf->mf_private;
958 
959   dlog("determining /defaults entry value");
960 
961   /*
962    * Find out if amd.conf overrode any map-specific /defaults.
963    */
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 */
971   }
972 
973   /* trim leading '-' in case thee's one */
974   if (*dflts == '-')
975     dfl = dflts + 1;
976   else
977     dfl = dflts;
978 
979   /*
980    * Chop the defaults up
981    */
982   rvec = strsplit(dfl, ' ', '\"');
983 
984   if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
985     /*
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.
989      */
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));
996 	/*
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"
1002 	 */
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;
1012 	}
1013 	++sp;
1014       }
1015     }
1016   } else {			/* not selectors_in_defaults */
1017     /*
1018      * Extract first value
1019      */
1020     dfl = rvec[0];
1021   }
1022 
1023   /*
1024    * If there were any values at all...
1025    */
1026   if (dfl) {
1027     /*
1028      * Log error if there were other values
1029      */
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);
1033     }
1034 
1035     /*
1036      * Prepend to existing defaults if they exist,
1037      * otherwise just use these defaults.
1038      */
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);
1047     }
1048   }
1049 
1050   XFREE(dflts);
1051 
1052   /* don't need info vector any more */
1053   if (rvec)
1054     XFREE(rvec);
1055 
1056   return def_opts;
1057 }
1058 
1059 
1060 am_node *
1061 amfs_generic_mount_child(am_node *new_mp, int *error_return)
1062 {
1063   int error;
1064   struct continuation *cp;	/* Continuation structure if need to mount */
1065 
1066   dlog("in amfs_generic_mount_child");
1067 
1068   *error_return = error = 0;	/* Error so far */
1069 
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;
1073 
1074   /*
1075    * Construct a continuation
1076    */
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;
1083 
1084   /*
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.
1088    */
1089   error = amfs_bgmount(cp);
1090   reschedule_timeout_mp();
1091   if (!error)
1092     return new_mp;
1093 
1094   /*
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.
1102    */
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;
1107   }
1108   if (error && new_mp->am_mnt && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1109     new_mp->am_error = error;
1110 
1111   if (new_mp->am_error > 0)
1112     assign_error_mntfs(new_mp);
1113 
1114   ereturn(error);
1115 }
1116 
1117 
1118 /*
1119  * Automount interface to RPC lookup routine
1120  * Find the corresponding entry and return
1121  * the file handle for it.
1122  */
1123 am_node *
1124 amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1125 {
1126   am_node *new_mp;
1127   mntfs **mf_array;
1128   int mp_error;
1129 
1130   dlog("in amfs_generic_lookup_child");
1131 
1132   *error_return = 0;
1133   new_mp = amfs_lookup_node(mp, fname, error_return);
1134 
1135   /* return if we got an error */
1136   if (!new_mp || *error_return > 0)
1137     return new_mp;
1138 
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;
1142 
1143   switch (op) {
1144   case VLOOK_DELETE:
1145     /*
1146      * If doing a delete then don't create again!
1147      */
1148     ereturn(ENOENT);
1149   case VLOOK_LOOKUP:
1150     return new_mp;
1151   }
1152 
1153   /* save error_return */
1154   mp_error = *error_return;
1155 
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;
1161   }
1162 
1163   /*
1164    * Already mounted but known to be down:
1165    * check if we have any alternatives to mount
1166    */
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) {
1173       /*
1174        * we found an alternative, so try mounting again.
1175        */
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;
1186       }
1187     }
1188   }
1189 
1190   /* store the array inside the am_node */
1191   new_mp->am_mfarray = mf_array;
1192 
1193   /*
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.
1198    */
1199   return new_mp;
1200 }
1201 
1202 
1203 void
1204 amfs_generic_mounted(mntfs *mf)
1205 {
1206   amfs_mkcacheref(mf);
1207 }
1208 
1209 
1210 /*
1211  * Unmount an automount sub-node
1212  */
1213 int
1214 amfs_generic_umount(am_node *mp, mntfs *mf)
1215 {
1216   int error = 0;
1217 
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 */
1223 
1224   return error;
1225 }
1226 
1227 
1228 char *
1229 amfs_generic_match(am_opts *fo)
1230 {
1231   char *p;
1232 
1233   if (!fo->opt_rfs) {
1234     plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)");
1235     return 0;
1236   }
1237   if (!fo->opt_fs) {
1238     plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)");
1239     return 0;
1240   }
1241 
1242   /*
1243    * Swap round fs:= and rfs:= options
1244    * ... historical (jsp)
1245    */
1246   p = fo->opt_rfs;
1247   fo->opt_rfs = fo->opt_fs;
1248   fo->opt_fs = p;
1249 
1250   /*
1251    * mtab entry turns out to be the name of the mount map
1252    */
1253   return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
1254 }
1255