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