Am-Utils Cross Reference
am-utils/amd/amfs_generic.c

source navigation ]
diff markup ]
identifier search ]
freetext search ]
file search ]
 
Version: 6.0.1 ] [ 6.0.2 ] [ 6.0.3 ] [ 6.0.4 ] [ 6.0.5 ] [ 6.0.6 ] [ 6.0.7 ] [ 6.0.8 ] [ 6.0.9 ] [ 6.0.10 ] [ 6.1 ] [ 6.1.1 ]

  1 /*
  2  * Copyright (c) 1997-2005 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.
  6  * All rights reserved.
  7  *
  8  * This code is derived from software contributed to Berkeley by
  9  * Jan-Simon Pendry at Imperial College, London.
 10  *
 11  * Redistribution and use in source and binary forms, with or without
 12  * modification, are permitted provided that the following conditions
 13  * are met:
 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.
 26  *
 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
 37  * SUCH DAMAGE.
 38  *
 39  *
 40  * File: am-utils/amd/amfs_generic.c
 41  *
 42  */
 43 
 44 /*
 45  * generic functions used by amfs filesystems, ripped out of amfs_auto.c.
 46  */
 47 
 48 #ifdef HAVE_CONFIG_H
 49 # include <config.h>
 50 #endif /* HAVE_CONFIG_H */
 51 #include <am_defs.h>
 52 #include <amd.h>
 53 
 54 
 55 /****************************************************************************
 56  *** MACROS                                                               ***
 57  ****************************************************************************/
 58 #define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
 59 
 60 
 61 /****************************************************************************
 62  *** STRUCTURES                                                           ***
 63  ****************************************************************************/
 64 /*
 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.
 77  */
 78 struct continuation {
 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 */
 84 };
 85 
 86 
 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);
 99 
100 
101 /****************************************************************************
102  *** FUNCTIONS                                                             ***
103  ****************************************************************************/
104 static am_node *
105 amfs_lookup_node(am_node *mp, char *fname, int *error_return)
106 {
107   am_node *new_mp;
108   int error = 0;                /* Error so far */
109   int in_progress = 0;          /* # of (un)mount in progress */
110   mntfs *mf;
111   char *expanded_fname = 0;
112 
113   dlog("in amfs_lookup_node");
114 
115   /*
116    * If the server is shutting down
117    * then don't return information
118    * about the mount point.
119    */
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);
123     } else {
124       dlog("%s/%s mount ignored - going down", mp->am_path, fname);
125     }
126     ereturn(ENOENT);
127   }
128 
129   /*
130    * Handle special case of "." and ".."
131    */
132   if (fname[0] == '.') {
133     if (fname[1] == '\0')
134       return mp;                /* "." is the current node */
135     if (fname[1] == '.' && fname[2] == '\0') {
136       if (mp->am_parent) {
137         dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
138         return mp->am_parent;   /* ".." is the parent node */
139       }
140       ereturn(ESTALE);
141     }
142   }
143 
144   /*
145    * Check for valid key name.
146    * If it is invalid then pretend it doesn't exist.
147    */
148   if (!valid_key(fname)) {
149     plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
150     ereturn(ENOENT);
151   }
152 
153   /*
154    * Expand key name.
155    * expanded_fname is now a private copy.
156    */
157   expanded_fname = expand_selectors(fname);
158 
159   /*
160    * Search children of this node
161    */
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;
166         continue;
167       }
168 
169       /*
170        * If the error code is undefined then it must be
171        * in progress.
172        */
173       mf = new_mp->am_mnt;
174       if (mf->mf_error < 0)
175         goto in_progrss;
176 
177       /*
178        * If there was a previous error with this node
179        * then return that error code.
180        */
181       if (mf->mf_flags & MFF_ERROR) {
182         error = mf->mf_error;
183         continue;
184       }
185       if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
186       in_progrss:
187         /*
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
192          * have completed.
193          */
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);
197         in_progress++;
198         if (mf->mf_flags & MFF_UNMOUNTING) {
199           dlog("will remount later");
200           new_mp->am_flags |= AMF_REMOUNT;
201         }
202         continue;
203       }
204 
205       /*
206        * Otherwise we have a hit: return the current mount point.
207        */
208       dlog("matched %s in %s", expanded_fname, new_mp->am_path);
209       XFREE(expanded_fname);
210       return new_mp;
211     }
212   }
213 
214   if (in_progress) {
215     dlog("Waiting while %d mount(s) in progress", in_progress);
216     XFREE(expanded_fname);
217     ereturn(-1);
218   }
219 
220   /*
221    * If an error occurred then return it.
222    */
223   if (error) {
224     dlog("Returning error: %s", strerror(error));
225     XFREE(expanded_fname);
226     ereturn(error);
227   }
228 
229   /*
230    * If the server is going down then just return,
231    * don't try to mount any more file systems
232    */
233   if ((int) amd_state >= (int) Finishing) {
234     dlog("not found - server going down anyway");
235     ereturn(ENOENT);
236   }
237 
238   /*
239    * Allocate a new map
240    */
241   new_mp = get_ap_child(mp, expanded_fname);
242   XFREE(expanded_fname);
243   if (new_mp == 0)
244     ereturn(ENOSPC);
245 
246   *error_return = -1;
247   return new_mp;
248 }
249 
250 
251 
252 static mntfs *
253 amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec,
254                       char *def_opts, char *pfname)
255 {
256   am_ops *p;
257   am_opts *fs_opts;
258   mntfs *new_mf;
259   char *mp_dir = 0;
260 #ifdef HAVE_FS_AUTOFS
261   int on_autofs = 1;
262 #endif /* HAVE_FS_AUTOFS */
263 
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) {
273       /*
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.
278        */
279       on_autofs = 0;
280       mp_dir = fs_opts->opt_fs;
281     } else {
282       if (p->autofs_fs_flags & FS_ON_AUTOFS) {
283         mp_dir = new_mp->am_path;
284       } else {
285         mp_dir = fs_opts->opt_fs;
286         on_autofs = 0;
287       }
288     }
289   } else
290 #endif /* HAVE_FS_AUTOFS */
291     mp_dir = fs_opts->opt_fs;
292 
293   /*
294    * Find or allocate a filesystem for this node.
295    */
296   new_mf = find_mntfs(p, fs_opts,
297                       mp_dir,
298                       fs_opts->fs_mtab,
299                       def_opts,
300                       fs_opts->opt_opts,
301                       fs_opts->opt_remopts);
302 
303   /*
304    * See whether this is a real filesystem
305    */
306   p = new_mf->mf_ops;
307   if (p == &amfs_error_ops) {
308     plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path);
309     free_mntfs(new_mf);
310     return NULL;
311   }
312 
313   dlog("Got a hit with %s", p->fs_type);
314 
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;
319   }
320   /*
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.
326    */
327   if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS &&
328       amd_use_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 */
334 
335   return new_mf;
336 }
337 
338 
339 static mntfs **
340 amfs_lookup_mntfs(am_node *new_mp, int *error_return)
341 {
342   am_node *mp;
343   char *info;                   /* Mount info - where to get the file system */
344   char **ivecs, **cur_ivec;     /* Split version of info */
345   int num_ivecs;
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;
352   int count;
353 
354   dlog("in amfs_lookup_mntfs");
355 
356   mp = new_mp->am_parent;
357 
358   /*
359    * If we get here then this is a reference to an,
360    * as yet, unknown name so we need to search the mount
361    * map for it.
362    */
363   if (mp->am_pref) {
364     if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name))
365       ereturn(ENAMETOOLONG);
366     sprintf(path_name, "%s%s", mp->am_pref, new_mp->am_name);
367     pfname = path_name;
368   } else {
369     pfname = new_mp->am_name;
370   }
371 
372   mf = mp->am_mnt;
373 
374   dlog("will search map info in %s to find %s", mf->mf_info, pfname);
375   /*
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().
379    *
380    * Note that this may return -1 indicating that information
381    * is not yet available.
382    */
383   error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
384   if (error) {
385     if (error > 0)
386       plog(XLOG_MAP, "No map entry for %s", pfname);
387     else
388       plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
389     ereturn(error);
390   }
391   dlog("mount info is %s", info);
392 
393   /*
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.
397    *
398    * Note: the vector pointers point into info, so don't free it!
399    */
400   ivecs = strsplit(info, ' ', '\"');
401 
402   if (mf->mf_auto)
403     def_opts = mf->mf_auto;
404   else
405     def_opts = "";
406 
407   orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts));
408   def_opts = strdup(orig_def_opts);
409 
410   /* first build our defaults */
411   num_ivecs = 0;
412   for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
413     if (**cur_ivec == '-') {
414       /*
415        * Pick up new defaults
416        */
417       def_opts = str3cat((char *) 0, def_opts, ";", *cur_ivec + 1);
418       dlog("Setting def_opts to \"%s\"", def_opts);
419       continue;
420     } else
421       num_ivecs++;
422   }
423 
424   mf_array = calloc(num_ivecs + 1, sizeof(mntfs *));
425 
426   /* construct the array of struct mntfs for this mount point */
427   for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) {
428     mntfs *new_mf;
429 
430     if (**cur_ivec == '-') {
431       XFREE(def_opts);
432       if ((*cur_ivec)[1] == '\0') {
433         /*
434          * If we have a single dash '-' than we need to reset the
435          * default options.
436          */
437         def_opts = strdup(orig_def_opts);
438         dlog("Resetting the default options, a single dash '-' was found.");
439       } else {
440         /* append options to /default options */
441         def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1);
442         dlog("Resetting def_opts to \"%s\"", def_opts);
443       }
444       continue;
445     }
446 
447     /*
448      * If a mntfs has already been found, and we find
449      * a cut then don't try any more locations.
450      *
451      * XXX: we do not know when the "/" was added as an equivalent for "||".
452      * It's undocumented, it might go away at any time. Caveat emptor.
453      */
454     if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) {
455       if (count > 0) {
456         dlog("Cut: not trying any more locations for %s", mp->am_path);
457         break;
458       }
459       continue;
460     }
461 
462     new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname);
463     if (new_mf == NULL)
464       continue;
465     mf_array[count++] = new_mf;
466   }
467 
468   /* We're done with ivecs */
469   XFREE(ivecs);
470   XFREE(info);
471   XFREE(orig_def_opts);
472   XFREE(def_opts);
473   if (count == 0) {                     /* no match */
474     XFREE(mf_array);
475     ereturn(ENOENT);
476   }
477 
478   return mf_array;
479 }
480 
481 
482 /*
483  * The continuation function.  This is called by
484  * the task notifier when a background mount attempt
485  * completes.
486  */
487 static void
488 amfs_cont(int rc, int term, opaque_t arg)
489 {
490   struct continuation *cp = (struct continuation *) arg;
491   am_node *mp = cp->mp;
492   mntfs *mf = mp->am_mnt;
493 
494   dlog("amfs_cont: '%s'", mp->am_path);
495 
496   /*
497    * Definitely not trying to mount at the moment
498    */
499   mf->mf_flags &= ~MFF_MOUNTING;
500 
501   /*
502    * While we are mounting - try to avoid race conditions
503    */
504   new_ttl(mp);
505 
506   /*
507    * Wakeup anything waiting for this mount
508    */
509   wakeup(get_mntfs_wchan(mf));
510 
511   /*
512    * Check for termination signal or exit status...
513    */
514   if (rc || term) {
515 #ifdef HAVE_FS_AUTOFS
516     if (mf->mf_flags & MFF_IS_AUTOFS &&
517         !(mf->mf_flags & MFF_MOUNTED))
518       autofs_release_fh(mp);
519 #endif /* HAVE_FS_AUTOFS */
520 
521     if (term) {
522       /*
523        * Not sure what to do for an error code.
524        */
525       mf->mf_error = EIO;       /* XXX ? */
526       mf->mf_flags |= MFF_ERROR;
527       plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term);
528     } else {
529       /*
530        * Check for exit status...
531        */
532 #ifdef __linux__
533       /*
534        * HACK ALERT!
535        *
536        * On Linux (and maybe not only) it's possible to run
537        * an amd which "knows" how to mount certain combinations
538        * of nfs_proto/nfs_version which the kernel doesn't grok.
539        * So if we got an EINVAL and we have a server that's not
540        * using NFSv2/UDP, try again with NFSv2/UDP.
541        *
542        * Too bad that there is no way to dynamically determine
543        * what combinations the _client_ supports, as opposed to
544        * what the _server_ supports...
545        */
546       if (rc == EINVAL &&
547           mf->mf_server &&
548           (mf->mf_server->fs_version != 2 ||
549            !STREQ(mf->mf_server->fs_proto, "udp")))
550         mf->mf_flags |= MFF_NFS_SCALEDOWN;
551       else
552 #endif /* __linux__ */
553       {
554         mf->mf_error = rc;
555         mf->mf_flags |= MFF_ERROR;
556         errno = rc;             /* XXX */
557         if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx"))
558           plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path);
559       }
560     }
561 
562     if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) {
563       /*
564        * If we get here then that attempt didn't work, so
565        * move the info vector pointer along by one and
566        * call the background mount routine again
567        */
568       amd_stats.d_merr++;
569       cp->mf++;
570     }
571     amfs_bgmount(cp);
572     if (mp->am_error > 0)
573       assign_error_mntfs(mp);
574   } else {
575     /*
576      * The mount worked.
577      */
578     dlog("Mounting %s returned success", cp->mp->am_path);
579     am_mounted(cp->mp);
580     free_continuation(cp);
581   }
582 
583   reschedule_timeout_mp();
584 }
585 
586 
587 /*
588  * Retry a mount
589  */
590 static void
591 amfs_retry(int rc, int term, opaque_t arg)
592 {
593   struct continuation *cp = (struct continuation *) arg;
594   am_node *mp = cp->mp;
595   int error = 0;
596 
597   dlog("Commencing retry for mount of %s", mp->am_path);
598 
599   new_ttl(mp);
600 
601   if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
602     /*
603      * The entire mount has timed out.  Set the error code and skip past all
604      * the mntfs's so that amfs_bgmount will not have any more
605      * ways to try the mount, thus causing an error.
606      */
607     plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path);
608     error = ETIMEDOUT;
609     while (*cp->mf)
610       cp->mf++;
611     /* explicitly forbid further retries after timeout */
612     cp->retry = FALSE;
613   }
614   if (error || !IN_PROGRESS(cp))
615     error = amfs_bgmount(cp);
616 
617   reschedule_timeout_mp();
618 }
619 
620 
621 /*
622  * Discard an old continuation
623  */
624 static void
625 free_continuation(struct continuation *cp)
626 {
627   mntfs **mfp;
628 
629   dlog("free_continuation");
630   if (cp->callout)
631     untimeout(cp->callout);
632   /*
633    * we must free the mntfs's in the list.
634    * so free all of them if there was an error,
635    * or free all but the used one, if the mount succeeded.
636    */
637   for (mfp = cp->mp->am_mfarray; *mfp; mfp++) {
638     free_mntfs(*mfp);
639   }
640   XFREE(cp->mp->am_mfarray);
641   cp->mp->am_mfarray = 0;
642   XFREE(cp);
643 }
644 
645 
646 /*
647  * Pick a file system to try mounting and
648  * do that in the background if necessary
649  *
650 For each location:
651         discard previous mount location if required
652         fetch next mount location
653         if the filesystem failed to be mounted then
654                 this_error = error from filesystem
655                 goto failed
656         if the filesystem is mounting or unmounting then
657                 goto retry;
658         if the fileserver is down then
659                 this_error = EIO
660                 continue;
661         if the filesystem is already mounted
662                 break
663         fi
664 
665         this_error = initialize mount point
666 
667         if no error on this mount and mount is delayed then
668                 this_error = -1
669         fi
670         if this_error < 0 then
671                 retry = true
672         fi
673         if no error on this mount then
674                 if mount in background then
675                         run mount in background
676                         return -1
677                 else
678                         this_error = mount in foreground
679                 fi
680         fi
681         if an error occurred on this mount then
682                 update stats
683                 save error in mount point
684         fi
685 endfor
686  */
687 static int
688 amfs_bgmount(struct continuation *cp)
689 {
690   am_node *mp = cp->mp;
691   mntfs *mf;                    /* Current mntfs */
692   int this_error = -1;          /* Per-mount error */
693   int hard_error = -1;          /* Cumulative per-node error */
694 
695   if (mp->am_mnt)
696     free_mntfs(mp->am_mnt);
697 
698   /*
699    * Try to mount each location.
700    * At the end:
701    * hard_error == 0 indicates something was mounted.
702    * hard_error > 0 indicates everything failed with a hard error
703    * hard_error < 0 indicates nothing could be mounted now
704    */
705   for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) {
706     am_ops *p;
707 
708     mf = dup_mntfs(mp->am_mnt);
709     p = mf->mf_ops;
710 
711     if (hard_error < 0)
712       hard_error = this_error;
713     this_error = 0;
714 
715     if (mf->mf_error > 0) {
716       this_error = mf->mf_error;
717       goto failed;
718     }
719 
720     if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
721       /*
722        * Still mounting - retry later
723        */
724       dlog("mount of \"%s\" already pending", mf->mf_info);
725       goto retry;
726     }
727 
728     if (FSRV_ISDOWN(mf->mf_server)) {
729       /*
730        * Would just mount from the same place
731        * as a hung mount - so give up
732        */
733       dlog("%s is already hung - giving up", mf->mf_server->fs_host);
734       this_error = EIO;
735       goto failed;
736     }
737 
738     if (mp->am_link) {
739       XFREE(mp->am_link);
740       mp->am_link = NULL;
741     }
742     if (mf->mf_fo && mf->mf_fo->opt_sublink)
743       mp->am_link = strdup(mf->mf_fo->opt_sublink);
744 
745     /*
746      * Will usually need to play around with the mount nodes
747      * file attribute structure.  This must be done here.
748      * Try and get things initialized, even if the fileserver
749      * is not known to be up.  In the common case this will
750      * progress things faster.
751      */
752 
753     /*
754      * Fill in attribute fields.
755      */
756     if (mf->mf_fsflags & FS_DIRECTORY)
757       mk_fattr(&mp->am_fattr, NFDIR);
758     else
759       mk_fattr(&mp->am_fattr, NFLNK);
760 
761     if (mf->mf_flags & MFF_MOUNTED) {
762       dlog("duplicate mount of \"%s\" ...", mf->mf_info);
763       /*
764        * Skip initial processing of the mountpoint if already mounted.
765        * This could happen if we have multiple sublinks into the same f/s,
766        * or if we are restarting an already-mounted filesystem.
767        */
768       goto already_mounted;
769     }
770 
771     if (mf->mf_fo->fs_mtab) {
772       plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s",
773            mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type,
774            mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs");
775     }
776 
777     if (p->fs_init && !(mf->mf_flags & MFF_RESTART))
778       this_error = p->fs_init(mf);
779 
780     if (this_error > 0)
781       goto failed;
782     if (this_error < 0)
783       goto retry;
784 
785     if (mf->mf_fo->opt_delay) {
786       /*
787        * If there is a delay timer on the mount
788        * then don't try to mount if the timer
789        * has not expired.
790        */
791       int i = atoi(mf->mf_fo->opt_delay);
792       if (i > 0 && clocktime() < (cp->start + i)) {
793         dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start));
794         goto retry;
795       }
796     }
797 
798     /*
799      * If the directory is not yet made and it needs to be made, then make it!
800      */
801     if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) {
802       plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount);
803       this_error = mkdirs(mf->mf_mount, 0555);
804       if (this_error) {
805         plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error));
806         goto failed;
807       }
808       mf->mf_flags |= MFF_MKMNT;
809     }
810 
811 #ifdef HAVE_FS_AUTOFS
812     if (mf->mf_flags & MFF_IS_AUTOFS)
813       if ((this_error = autofs_get_fh(mp)))
814         goto failed;
815 #endif /* HAVE_FS_AUTOFS */
816 
817   already_mounted:
818     mf->mf_flags |= MFF_MOUNTING;
819     if (mf->mf_fsflags & FS_MBACKGROUND) {
820       dlog("backgrounding mount of \"%s\"", mf->mf_mount);
821       if (cp->callout) {
822         untimeout(cp->callout);
823         cp->callout = 0;
824       }
825 
826       /* actually run the task, backgrounding as necessary */
827       run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp);
828       return -1;
829     } else {
830       dlog("foreground mount of \"%s\" ...", mf->mf_mount);
831       this_error = mount_node((opaque_t) mp);
832     }
833 
834     mf->mf_flags &= ~MFF_MOUNTING;
835     if (this_error > 0)
836       goto failed;
837     if (this_error == 0) {
838       am_mounted(mp);
839       break;                                    /* Success */
840     }
841 
842   retry:
843     if (!cp->retry)
844       continue;
845     dlog("will retry ...\n");
846 
847     /*
848      * Arrange that amfs_bgmount is called
849      * after anything else happens.
850      */
851     dlog("Arranging to retry mount of %s", mp->am_path);
852     sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf));
853     if (cp->callout)
854       untimeout(cp->callout);
855     cp->callout = timeout(RETRY_INTERVAL, wakeup,
856                           (opaque_t) get_mntfs_wchan(mf));
857 
858     mp->am_ttl = clocktime() + RETRY_INTERVAL;
859 
860     /*
861      * Not done yet - so don't return anything
862      */
863     return -1;
864 
865   failed:
866     amd_stats.d_merr++;
867     mf->mf_error = this_error;
868     mf->mf_flags |= MFF_ERROR;
869 #ifdef HAVE_FS_AUTOFS
870     if (mp->am_autofs_fh)
871       autofs_release_fh(mp);
872 #endif /* HAVE_FS_AUTOFS */
873     if (mf->mf_flags & MFF_MKMNT) {
874       rmdirs(mf->mf_mount);
875       mf->mf_flags &= ~MFF_MKMNT;
876     }
877     /*
878      * Wakeup anything waiting for this mount
879      */
880     wakeup(get_mntfs_wchan(mf));
881     free_mntfs(mf);
882     /* continue */
883   }
884 
885   /*
886    * If we get here, then either the mount succeeded or
887    * there is no more mount information available.
888    */
889   if (this_error) {
890     mp->am_mnt = mf = new_mntfs();
891 
892 #ifdef HAVE_FS_AUTOFS
893     if (mp->am_flags & AMF_AUTOFS)
894       autofs_mount_failed(mp);
895     else
896 #endif /* HAVE_FS_AUTOFS */
897       nfs_quick_reply(mp, this_error);
898 
899     if (hard_error <= 0)
900       hard_error = this_error;
901     if (hard_error < 0)
902       hard_error = ETIMEDOUT;
903 
904     /*
905      * Set a small(ish) timeout on an error node if
906      * the error was not a time out.
907      */
908     switch (hard_error) {
909     case ETIMEDOUT:
910     case EWOULDBLOCK:
911     case EIO:
912       mp->am_timeo = 17;
913       break;
914     default:
915       mp->am_timeo = 5;
916       break;
917     }
918     new_ttl(mp);
919   } else {
920     mf = mp->am_mnt;
921     /*
922      * Wakeup anything waiting for this mount
923      */
924     wakeup(get_mntfs_wchan(mf));
925     hard_error = 0;
926   }
927 
928   /*
929    * Make sure that the error value in the mntfs has a
930    * reasonable value.
931    */
932   if (mf->mf_error < 0) {
933     mf->mf_error = hard_error;
934     if (hard_error)
935       mf->mf_flags |= MFF_ERROR;
936   }
937 
938   /*
939    * In any case we don't need the continuation any more
940    */
941   free_continuation(cp);
942 
943   return hard_error;
944 }
945 
946 
947 static char *
948 amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts)
949 {
950   char *dflts;
951   char *dfl;
952   char **rvec = NULL;
953   struct mnt_map *mm = (mnt_map *) mf->mf_private;
954 
955   dlog("determining /defaults entry value");
956 
957   /*
958    * Find out if amd.conf overrode any map-specific /defaults.
959    *
960    * HACK ALERT: there's no easy way to find out what the map mount point is
961    * at this point, so I am forced to initialize the mnt_map->cfm field here
962    * for the first time, upon the very first search for a /defaults entry in
963    * this map.  This initialization is much better done in mapc_create(),
964    * but it's impossible to do that there with the current code structure.
965    */
966   if (mm->cfm == NULL) {        /* then initialize it for first time */
967     mm->cfm = find_cf_map(mf->mf_mount);
968   }
969   if (mm->cfm && mm->cfm->cfm_defaults) {
970     dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults);
971     dflts = strdup(mm->cfm->cfm_defaults);
972   } else if (mapc_search(mm, "/defaults", &dflts) == 0) {
973     dlog("/defaults gave %s", dflts);
974   } else {
975     return def_opts;            /* if nothing found */
976   }
977 
978   /* trim leading '-' in case thee's one */
979   if (*dflts == '-')
980     dfl = dflts + 1;
981   else
982     dfl = dflts;
983 
984   /*
985    * Chop the defaults up
986    */
987   rvec = strsplit(dfl, ' ', '\"');
988 
989   if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) {
990     /*
991      * Pick whichever first entry matched the list of selectors.
992      * Strip the selectors from the string, and assign to dfl the
993      * rest of the string.
994      */
995     if (rvec) {
996       am_opts ap;
997       am_ops *pt;
998       char **sp = rvec;
999       while (*sp) {             /* loop until you find something, if any */
1000         memset((char *) &ap, 0, sizeof(am_opts));
1001         /*
1002          * This next routine cause many spurious "expansion of ... is"
1003          * messages, which are ignored, b/c all we need out of this
1004          * routine is to match selectors.  These spurious messages may
1005          * be wrong, esp. if they try to expand ${key} b/c it will
1006          * get expanded to "/defaults"
1007          */
1008         pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1009                        mp->am_parent->am_mnt->mf_info);
1010         free_opts(&ap); /* don't leak */
1011         if (pt == &amfs_error_ops) {
1012           plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp);
1013         } else {
1014           dfl = strip_selectors(*sp, "/defaults");
1015           plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1016           break;
1017         }
1018         ++sp;
1019       }
1020     }
1021   } else {                      /* not selectors_in_defaults */
1022     /*
1023      * Extract first value
1024      */
1025     dfl = rvec[0];
1026   }
1027 
1028   /*
1029    * If there were any values at all...
1030    */
1031   if (dfl) {
1032     /*
1033      * Log error if there were other values
1034      */
1035     if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) {
1036       dlog("/defaults chopped into %s", dfl);
1037       plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1038     }
1039 
1040     /*
1041      * Prepend to existing defaults if they exist,
1042      * otherwise just use these defaults.
1043      */
1044     if (*def_opts && *dfl) {
1045       char *nopts = (char *) xmalloc(strlen(def_opts) + strlen(dfl) + 2);
1046       sprintf(nopts, "%s;%s", dfl, def_opts);
1047       XFREE(def_opts);
1048       def_opts = nopts;
1049     } else if (*dfl) {
1050       def_opts = strealloc(def_opts, dfl);
1051     }
1052   }
1053 
1054   XFREE(dflts);
1055 
1056   /* don't need info vector any more */
1057   if (rvec)
1058     XFREE(rvec);
1059 
1060   return def_opts;
1061 }
1062 
1063 
1064 am_node *
1065 amfs_generic_mount_child(am_node *new_mp, int *error_return)
1066 {
1067   int error;
1068   struct continuation *cp;      /* Continuation structure if need to mount */
1069 
1070   dlog("in amfs_generic_mount_child");
1071 
1072   *error_return = error = 0;    /* Error so far */
1073 
1074   /* we have an errorfs attached to the am_node, free it */
1075   free_mntfs(new_mp->am_mnt);
1076   new_mp->am_mnt = 0;
1077 
1078   /*
1079    * Construct a continuation
1080    */
1081   cp = ALLOC(struct continuation);
1082   cp->callout = 0;
1083   cp->mp = new_mp;
1084   cp->retry = TRUE;
1085   cp->start = clocktime();
1086   cp->mf = new_mp->am_mfarray;
1087 
1088   /*
1089    * Try and mount the file system.  If this succeeds immediately (possible
1090    * for a ufs file system) then return the attributes, otherwise just
1091    * return an error.
1092    */
1093   error = amfs_bgmount(cp);
1094   reschedule_timeout_mp();
1095   if (!error)
1096     return new_mp;
1097 
1098   /*
1099    * Code for quick reply.  If current_transp is set, then it's the
1100    * transp that's been passed down from nfs_program_2() or from
1101    * autofs_program_[123]().
1102    * If new_mp->am_transp is not already set, set it by copying in
1103    * current_transp.  Once am_transp is set, nfs_quick_reply() and
1104    * autofs_mount_succeeded() can use it to send a reply to the
1105    * client that requested this mount.
1106    */
1107   if (current_transp && !new_mp->am_transp) {
1108     dlog("Saving RPC transport for %s", new_mp->am_path);
1109     new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1110     *(new_mp->am_transp) = *current_transp;
1111   }
1112   if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1113     new_mp->am_error = error;
1114 
1115   if (new_mp->am_error > 0)
1116     assign_error_mntfs(new_mp);
1117 
1118   ereturn(error);
1119 }
1120 
1121 
1122 /*
1123  * Automount interface to RPC lookup routine
1124  * Find the corresponding entry and return
1125  * the file handle for it.
1126  */
1127 am_node *
1128 amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op)
1129 {
1130   am_node *new_mp;
1131   mntfs **mf_array;
1132   int mp_error;
1133 
1134   dlog("in amfs_generic_lookup_child");
1135 
1136   *error_return = 0;
1137   new_mp = amfs_lookup_node(mp, fname, error_return);
1138 
1139   /* return if we got an error */
1140   if (!new_mp || *error_return > 0)
1141     return new_mp;
1142 
1143   /* also return if it's already mounted and known to be up */
1144   if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server))
1145     return new_mp;
1146 
1147   switch (op) {
1148   case VLOOK_DELETE:
1149     /*
1150      * If doing a delete then don't create again!
1151      */
1152     ereturn(ENOENT);
1153   case VLOOK_LOOKUP:
1154     return new_mp;
1155   }
1156 
1157   /* save error_return */
1158   mp_error = *error_return;
1159 
1160   mf_array = amfs_lookup_mntfs(new_mp, error_return);
1161   if (!mf_array) {
1162     new_mp->am_error = new_mp->am_mnt->mf_error = *error_return;
1163     free_map(new_mp);
1164     return NULL;
1165   }
1166 
1167   /*
1168    * Already mounted but known to be down:
1169    * check if we have any alternatives to mount
1170    */
1171   if (mp_error == 0) {
1172     mntfs **mfp;
1173     for (mfp = mf_array; *mfp; mfp++)
1174       if (*mfp != new_mp->am_mnt)
1175         break;
1176     if (*mfp != NULL) {
1177       /*
1178        * we found an alternative, so try mounting again.
1179        */
1180       *error_return = -1;
1181     } else {
1182       for (mfp = mf_array; *mfp; mfp++)
1183         free_mntfs(*mfp);
1184       XFREE(mf_array);
1185       if (new_mp->am_flags & AMF_SOFTLOOKUP) {
1186         ereturn(EIO);
1187       } else {
1188         *error_return = 0;
1189         return new_mp;
1190       }
1191     }
1192   }
1193 
1194   /* store the array inside the am_node */
1195   new_mp->am_mfarray = mf_array;
1196 
1197   /*
1198    * Note: while it might seem like a good idea to prioritize
1199    * the list of mntfs's we got here, it probably isn't.
1200    * It would ignore the ordering of entries specified by the user,
1201    * which is counterintuitive and confusing.
1202    */
1203   return new_mp;
1204 }
1205 
1206 
1207 void
1208 amfs_generic_mounted(mntfs *mf)
1209 {
1210