Home | History | Annotate | Line # | Download | only in amd
      1 /*	$NetBSD: map.c,v 1.2 2015/01/21 21:48:23 joerg 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/map.c
     39  *
     40  */
     41 
     42 #ifdef HAVE_CONFIG_H
     43 # include <config.h>
     44 #endif /* HAVE_CONFIG_H */
     45 #include <am_defs.h>
     46 #include <amd.h>
     47 
     48 #define	smallest_t(t1, t2) (t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2)
     49 #define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART)
     50 #define new_gen() (am_gen++)
     51 
     52 /*
     53  * Generation Numbers.
     54  *
     55  * Generation numbers are allocated to every node created
     56  * by amd.  When a filehandle is computed and sent to the
     57  * kernel, the generation number makes sure that it is safe
     58  * to reallocate a node slot even when the kernel has a cached
     59  * reference to its old incarnation.
     60  * No garbage collection is done, since it is assumed that
     61  * there is no way that 2^32 generation numbers could ever
     62  * be allocated by a single run of amd - there is simply
     63  * not enough cpu time available.
     64  * Famous last words... -Ion
     65  */
     66 static u_int am_gen = 2;	/* Initial generation number */
     67 static int timeout_mp_id;	/* Id from last call to timeout */
     68 
     69 static am_node *root_node;	/* The root of the mount tree */
     70 static am_node **exported_ap = (am_node **) NULL;
     71 static int exported_ap_size = 0;
     72 static int first_free_map = 0;	/* First available free slot */
     73 static int last_used_map = -1;	/* Last unavailable used slot */
     74 
     75 
     76 /*
     77  * This is the default attributes field which
     78  * is copied into every new node to be created.
     79  * The individual filesystem fs_init() routines
     80  * patch the copy to represent the particular
     81  * details for the relevant filesystem type
     82  */
     83 static nfsfattr gen_fattr =
     84 {
     85   NFLNK,			/* type */
     86   NFSMODE_LNK | 0777,		/* mode */
     87   1,				/* nlink */
     88   0,				/* uid */
     89   0,				/* gid */
     90   0,				/* size */
     91   4096,				/* blocksize */
     92   0,				/* rdev */
     93   1,				/* blocks */
     94   0,				/* fsid */
     95   0,				/* fileid */
     96   {0, 0},			/* atime */
     97   {0, 0},			/* mtime */
     98   {0, 0},			/* ctime */
     99 };
    100 
    101 /* forward declarations */
    102 static int unmount_node(opaque_t arg);
    103 static void exported_ap_free(am_node *mp);
    104 static void remove_am(am_node *mp);
    105 static am_node *get_root_ap(char *dir);
    106 
    107 
    108 /*
    109  * Iterator functions for exported_ap[]
    110  */
    111 am_node *
    112 get_first_exported_ap(int *index)
    113 {
    114   *index = -1;
    115   return get_next_exported_ap(index);
    116 }
    117 
    118 
    119 am_node *
    120 get_next_exported_ap(int *index)
    121 {
    122   (*index)++;
    123   while (*index < exported_ap_size) {
    124     if (exported_ap[*index] != NULL)
    125       return exported_ap[*index];
    126     (*index)++;
    127   }
    128   return NULL;
    129 }
    130 
    131 
    132 /*
    133  * Get exported_ap by index
    134  */
    135 am_node *
    136 get_exported_ap(int index)
    137 {
    138   if (index < 0 || index >= exported_ap_size)
    139     return 0;
    140   return exported_ap[index];
    141 }
    142 
    143 
    144 /*
    145  * Get exported_ap by path
    146  */
    147 am_node *
    148 path_to_exported_ap(char *path)
    149 {
    150   int index;
    151   am_node *mp;
    152 
    153   mp = get_first_exported_ap(&index);
    154   while (mp != NULL) {
    155     if (STREQ(mp->am_path, path))
    156       break;
    157     mp = get_next_exported_ap(&index);
    158   }
    159   return mp;
    160 }
    161 
    162 
    163 /*
    164  * Resize exported_ap map
    165  */
    166 static int
    167 exported_ap_realloc_map(int nsize)
    168 {
    169   /*
    170    * this shouldn't happen, but...
    171    */
    172   if (nsize < 0 || nsize == exported_ap_size)
    173     return 0;
    174 
    175   exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node *));
    176 
    177   if (nsize > exported_ap_size)
    178     memset((char *) (exported_ap + exported_ap_size), 0,
    179 	  (nsize - exported_ap_size) * sizeof(am_node *));
    180   exported_ap_size = nsize;
    181 
    182   return 1;
    183 }
    184 
    185 
    186 
    187 am_node *
    188 get_ap_child(am_node *mp, char *fname)
    189 {
    190   am_node *new_mp;
    191   mntfs *mf = mp->am_al->al_mnt;
    192 
    193   /*
    194    * Allocate a new map
    195    */
    196   new_mp = exported_ap_alloc();
    197   if (new_mp) {
    198     /*
    199      * Fill it in
    200      */
    201     init_map(new_mp, fname);
    202 
    203     /*
    204      * Put it in the table
    205      */
    206     insert_am(new_mp, mp);
    207 
    208     /*
    209      * Fill in some other fields,
    210      * path and mount point.
    211      *
    212      * bugfix: do not prepend old am_path if direct map
    213      *         <wls (at) astro.umd.edu> William Sebok
    214      */
    215     new_mp->am_path = str3cat(new_mp->am_path,
    216 			      (mf->mf_fsflags & FS_DIRECT)
    217 				     ? ""
    218 				     : mp->am_path,
    219 			      *fname == '/' ? "" : "/", fname);
    220     dlog("setting path to %s", new_mp->am_path);
    221   }
    222 
    223   return new_mp;
    224 }
    225 
    226 /*
    227  * Allocate a new mount slot and create
    228  * a new node.
    229  * Fills in the map number of the node,
    230  * but leaves everything else uninitialized.
    231  */
    232 am_node *
    233 exported_ap_alloc(void)
    234 {
    235   am_node *mp, **mpp;
    236 
    237   /*
    238    * First check if there are any slots left, realloc if needed
    239    */
    240   if (first_free_map >= exported_ap_size)
    241     if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP))
    242       return 0;
    243 
    244   /*
    245    * Grab the next free slot
    246    */
    247   mpp = exported_ap + first_free_map;
    248   mp = *mpp = ALLOC(struct am_node);
    249   memset((char *) mp, 0, sizeof(struct am_node));
    250 
    251   mp->am_mapno = first_free_map++;
    252 
    253   /*
    254    * Update free pointer
    255    */
    256   while (first_free_map < exported_ap_size && exported_ap[first_free_map])
    257     first_free_map++;
    258 
    259   if (first_free_map > last_used_map)
    260     last_used_map = first_free_map - 1;
    261 
    262   return mp;
    263 }
    264 
    265 
    266 /*
    267  * Free a mount slot
    268  */
    269 static void
    270 exported_ap_free(am_node *mp)
    271 {
    272   /*
    273    * Sanity check
    274    */
    275   if (!mp)
    276     return;
    277 
    278   /*
    279    * Zero the slot pointer to avoid double free's
    280    */
    281   exported_ap[mp->am_mapno] = NULL;
    282 
    283   /*
    284    * Update the free and last_used indices
    285    */
    286   if (mp->am_mapno == last_used_map)
    287     while (last_used_map >= 0 && exported_ap[last_used_map] == 0)
    288       --last_used_map;
    289 
    290   if (first_free_map > mp->am_mapno)
    291     first_free_map = mp->am_mapno;
    292 
    293   /*
    294    * Free the mount node, and zero out it's internal struct data.
    295    */
    296   memset((char *) mp, 0, sizeof(am_node));
    297   XFREE(mp);
    298 }
    299 
    300 
    301 /*
    302  * Insert mp into the correct place,
    303  * where p_mp is its parent node.
    304  * A new node gets placed as the youngest sibling
    305  * of any other children, and the parent's child
    306  * pointer is adjusted to point to the new child node.
    307  */
    308 void
    309 insert_am(am_node *mp, am_node *p_mp)
    310 {
    311   /*
    312    * If this is going in at the root then flag it
    313    * so that it cannot be unmounted by amq.
    314    */
    315   if (p_mp == root_node)
    316     mp->am_flags |= AMF_ROOT;
    317   /*
    318    * Fill in n-way links
    319    */
    320   mp->am_parent = p_mp;
    321   mp->am_osib = p_mp->am_child;
    322   if (mp->am_osib)
    323     mp->am_osib->am_ysib = mp;
    324   p_mp->am_child = mp;
    325 #ifdef HAVE_FS_AUTOFS
    326   if (p_mp->am_al->al_mnt->mf_flags & MFF_IS_AUTOFS)
    327     mp->am_flags |= AMF_AUTOFS;
    328 #endif /* HAVE_FS_AUTOFS */
    329 }
    330 
    331 
    332 /*
    333  * Remove am from its place in the mount tree
    334  */
    335 static void
    336 remove_am(am_node *mp)
    337 {
    338   /*
    339    * 1.  Consistency check
    340    */
    341   if (mp->am_child && mp->am_parent) {
    342     plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path);
    343   }
    344 
    345   /*
    346    * 2.  Update parent's child pointer
    347    */
    348   if (mp->am_parent && mp->am_parent->am_child == mp)
    349     mp->am_parent->am_child = mp->am_osib;
    350 
    351   /*
    352    * 3.  Unlink from sibling chain
    353    */
    354   if (mp->am_ysib)
    355     mp->am_ysib->am_osib = mp->am_osib;
    356   if (mp->am_osib)
    357     mp->am_osib->am_ysib = mp->am_ysib;
    358 }
    359 
    360 
    361 /*
    362  * Compute a new time to live value for a node.
    363  */
    364 void
    365 new_ttl(am_node *mp)
    366 {
    367   mp->am_timeo_w = 0;
    368   mp->am_ttl = clocktime(&mp->am_fattr.na_atime);
    369   mp->am_ttl += mp->am_timeo;	/* sun's -tl option */
    370 }
    371 
    372 
    373 void
    374 mk_fattr(nfsfattr *fattr, nfsftype vntype)
    375 {
    376   switch (vntype) {
    377   case NFDIR:
    378     fattr->na_type = NFDIR;
    379     fattr->na_mode = NFSMODE_DIR | 0555;
    380     fattr->na_nlink = 2;
    381     fattr->na_size = 512;
    382     break;
    383   case NFLNK:
    384     fattr->na_type = NFLNK;
    385     fattr->na_mode = NFSMODE_LNK | 0777;
    386     fattr->na_nlink = 1;
    387     fattr->na_size = 0;
    388     break;
    389   default:
    390     plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype);
    391     break;
    392   }
    393 }
    394 
    395 
    396 /*
    397  * Initialize an allocated mount node.
    398  * It is assumed that the mount node was b-zero'd
    399  * before getting here so anything that would
    400  * be set to zero isn't done here.
    401  */
    402 void
    403 init_map(am_node *mp, char *dir)
    404 {
    405   /*
    406    * mp->am_mapno is initialized by exported_ap_alloc
    407    * other fields don't need to be set to zero.
    408    */
    409 
    410   mp->am_al = new_loc();
    411   mp->am_alarray = NULL;
    412   mp->am_name = xstrdup(dir);
    413   mp->am_path = xstrdup(dir);
    414   mp->am_gen = new_gen();
    415 #ifdef HAVE_FS_AUTOFS
    416   mp->am_autofs_fh = NULL;
    417 #endif /* HAVE_FS_AUTOFS */
    418 
    419   mp->am_timeo = gopt.am_timeo;
    420   mp->am_attr.ns_status = NFS_OK;
    421   mp->am_fattr = gen_fattr;
    422   mp->am_fattr.na_fsid = 42;
    423   mp->am_fattr.na_fileid = mp->am_gen;
    424   clocktime(&mp->am_fattr.na_atime);
    425   /* next line copies a "struct nfstime" among several fields */
    426   mp->am_fattr.na_mtime = mp->am_fattr.na_ctime = mp->am_fattr.na_atime;
    427 
    428   new_ttl(mp);
    429   mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds;
    430   mp->am_dev = -1;
    431   mp->am_rdev = -1;
    432   mp->am_fd[0] = -1;
    433   mp->am_fd[1] = -1;
    434 }
    435 
    436 
    437 void
    438 notify_child(am_node *mp, au_etype au_etype, int au_errno, int au_signal)
    439 {
    440   amq_sync_umnt rv;
    441   int err;
    442 
    443   if (mp->am_fd[1] >= 0) {	/* we have a child process */
    444     rv.au_etype = au_etype;
    445     rv.au_signal = au_signal;
    446     rv.au_errno = au_errno;
    447 
    448     err = write(mp->am_fd[1], &rv, sizeof(rv));
    449     /* XXX: do something else on err? */
    450     if (err < sizeof(rv))
    451       plog(XLOG_INFO, "notify_child: write returned %d instead of %d.",
    452 	   err, (int) sizeof(rv));
    453     close(mp->am_fd[1]);
    454     mp->am_fd[1] = -1;
    455   }
    456 }
    457 
    458 
    459 /*
    460  * Free a mount node.
    461  * The node must be already unmounted.
    462  */
    463 void
    464 free_map(am_node *mp)
    465 {
    466   remove_am(mp);
    467 
    468   if (mp->am_fd[1] != -1)
    469     plog(XLOG_FATAL, "free_map: called prior to notifying the child for %s.",
    470 	mp->am_path);
    471 
    472   XFREE(mp->am_link);
    473   XFREE(mp->am_name);
    474   XFREE(mp->am_path);
    475   XFREE(mp->am_pref);
    476   XFREE(mp->am_transp);
    477 
    478   if (mp->am_al)
    479     free_loc(mp->am_al);
    480 
    481   if (mp->am_alarray) {
    482     am_loc **temp_al;
    483     for (temp_al = mp->am_alarray; *temp_al; temp_al++)
    484       free_loc(*temp_al);
    485     XFREE(mp->am_alarray);
    486   }
    487 
    488 #ifdef HAVE_FS_AUTOFS
    489   if (mp->am_autofs_fh)
    490     autofs_release_fh(mp);
    491 #endif /* HAVE_FS_AUTOFS */
    492 
    493   exported_ap_free(mp);
    494 }
    495 
    496 
    497 static am_node *
    498 find_ap_recursive(char *dir, am_node *mp)
    499 {
    500   if (mp) {
    501     am_node *mp2;
    502     if (STREQ(mp->am_path, dir))
    503       return mp;
    504 
    505     if ((mp->am_al->al_mnt->mf_flags & MFF_MOUNTED) &&
    506 	STREQ(mp->am_al->al_mnt->mf_mount, dir))
    507       return mp;
    508 
    509     mp2 = find_ap_recursive(dir, mp->am_osib);
    510     if (mp2)
    511       return mp2;
    512     return find_ap_recursive(dir, mp->am_child);
    513   }
    514 
    515   return 0;
    516 }
    517 
    518 
    519 /*
    520  * Find the mount node corresponding to dir.  dir can match either the
    521  * automount path or, if the node is mounted, the mount location.
    522  */
    523 am_node *
    524 find_ap(char *dir)
    525 {
    526   int i;
    527 
    528   for (i = last_used_map; i >= 0; --i) {
    529     am_node *mp = exported_ap[i];
    530     if (mp && (mp->am_flags & AMF_ROOT)) {
    531       mp = find_ap_recursive(dir, exported_ap[i]);
    532       if (mp) {
    533 	return mp;
    534       }
    535     }
    536   }
    537 
    538   return 0;
    539 }
    540 
    541 
    542 /*
    543  * Get the filehandle for a particular named directory.
    544  * This is used during the bootstrap to tell the kernel
    545  * the filehandles of the initial automount points.
    546  */
    547 am_nfs_handle_t *
    548 get_root_nfs_fh(char *dir, am_nfs_handle_t *nfh)
    549 {
    550   am_node *mp = get_root_ap(dir);
    551   if (mp) {
    552     if (nfs_dispatcher == nfs_program_2)
    553       mp_to_fh(mp, &nfh->v2);
    554     else
    555       mp_to_fh3(mp, &nfh->v3);
    556     return nfh;
    557   }
    558 
    559   /*
    560    * Should never get here...
    561    */
    562   plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
    563 
    564   return 0;
    565 }
    566 
    567 
    568 static am_node *
    569 get_root_ap(char *dir)
    570 {
    571   am_node *mp = find_ap(dir);
    572 
    573   if (mp && mp->am_parent == root_node)
    574     return mp;
    575 
    576   return 0;
    577 }
    578 
    579 
    580 /*
    581  * Timeout all nodes waiting on
    582  * a given Fserver.
    583  */
    584 void
    585 map_flush_srvr(fserver *fs)
    586 {
    587   int i;
    588   int done = 0;
    589 
    590   for (i = last_used_map; i >= 0; --i) {
    591     am_node *mp = exported_ap[i];
    592 
    593     if (mp && mp->am_al->al_mnt && mp->am_al->al_mnt->mf_server == fs) {
    594       plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host);
    595       mp->am_ttl = clocktime(NULL);
    596       done = 1;
    597     }
    598   }
    599   if (done)
    600     reschedule_timeout_mp();
    601 }
    602 
    603 
    604 /*
    605  * Mount a top level automount node
    606  * by calling lookup in the parent
    607  * (root) node which will cause the
    608  * automount node to be automounted.
    609  */
    610 int
    611 mount_auto_node(char *dir, opaque_t arg)
    612 {
    613   int error = 0;
    614   am_node *mp = (am_node *) arg;
    615   am_node *new_mp;
    616 
    617   new_mp = mp->am_al->al_mnt->mf_ops->lookup_child(mp, dir, &error, VLOOK_CREATE);
    618   if (new_mp && error < 0) {
    619     /*
    620      * We can't allow the fileid of the root node to change.
    621      * Should be ok to force it to 1, always.
    622      */
    623     new_mp->am_gen = new_mp->am_fattr.na_fileid = 1;
    624 
    625     (void) mp->am_al->al_mnt->mf_ops->mount_child(new_mp, &error);
    626   }
    627 
    628   if (error > 0) {
    629     errno = error;		/* XXX */
    630     plog(XLOG_ERROR, "Could not mount %s: %m", dir);
    631   }
    632   return error;
    633 }
    634 
    635 
    636 /*
    637  * Cause all the top-level mount nodes
    638  * to be automounted
    639  */
    640 int
    641 mount_exported(void)
    642 {
    643   /*
    644    * Iterate over all the nodes to be started
    645    */
    646   return root_keyiter(mount_auto_node, root_node);
    647 }
    648 
    649 
    650 /*
    651  * Construct top-level node
    652  */
    653 void
    654 make_root_node(void)
    655 {
    656   mntfs *root_mf;
    657   char *rootmap = ROOT_MAP;
    658   root_node = exported_ap_alloc();
    659 
    660   /*
    661    * Allocate a new map
    662    */
    663   init_map(root_node, "");
    664 
    665   /*
    666    * Allocate a new mounted filesystem
    667    */
    668   root_mf = find_mntfs(&amfs_root_ops, (am_opts *) NULL, "", rootmap, "", "", "");
    669 
    670   /*
    671    * Replace the initial null reference
    672    */
    673   free_mntfs(root_node->am_al->al_mnt);
    674   root_node->am_al->al_mnt = root_mf;
    675 
    676   /*
    677    * Initialize the root
    678    */
    679   if (root_mf->mf_ops->fs_init)
    680     (*root_mf->mf_ops->fs_init) (root_mf);
    681 
    682   /*
    683    * Mount the root
    684    */
    685   root_mf->mf_error = root_mf->mf_ops->mount_fs(root_node, root_mf);
    686 }
    687 
    688 
    689 /*
    690  * Cause all the nodes to be unmounted by timing
    691  * them out.
    692  */
    693 void
    694 umount_exported(void)
    695 {
    696   int i, work_done;
    697 
    698   do {
    699     work_done = 0;
    700 
    701     for (i = last_used_map; i >= 0; --i) {
    702       am_node *mp = exported_ap[i];
    703       mntfs *mf;
    704 
    705       if (!mp)
    706 	continue;
    707 
    708       /*
    709        * Wait for children to be removed first
    710        */
    711       if (mp->am_child)
    712 	continue;
    713 
    714       mf = mp->am_al->al_mnt;
    715       if (mf->mf_flags & MFF_UNMOUNTING) {
    716 	/*
    717 	 * If this node is being unmounted then just ignore it.  However,
    718 	 * this could prevent amd from finishing if the unmount gets blocked
    719 	 * since the am_node will never be free'd.  am_unmounted needs
    720 	 * telling about this possibility. - XXX
    721 	 */
    722 	continue;
    723       }
    724 
    725       if (!(mf->mf_fsflags & FS_DIRECTORY))
    726 	/*
    727 	 * When shutting down this had better
    728 	 * look like a directory, otherwise it
    729 	 * can't be unmounted!
    730 	 */
    731 	mk_fattr(&mp->am_fattr, NFDIR);
    732 
    733       if ((--immediate_abort < 0 &&
    734 	   !(mp->am_flags & AMF_ROOT) && mp->am_parent) ||
    735 	  (mf->mf_flags & MFF_RESTART)) {
    736 
    737 	work_done++;
    738 
    739 	/*
    740 	 * Just throw this node away without bothering to unmount it.  If
    741 	 * the server is not known to be up then don't discard the mounted
    742 	 * on directory or Amd might hang...
    743 	 */
    744 	if (mf->mf_server &&
    745 	    (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID)
    746 	  mf->mf_flags &= ~MFF_MKMNT;
    747 	if (gopt.flags & CFM_UNMOUNT_ON_EXIT || mp->am_flags & AMF_AUTOFS) {
    748 	  plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount);
    749 	  /*
    750 	   * use unmount_mp, not unmount_node, so that unmounts be
    751 	   * backgrounded as needed.
    752 	   */
    753 	  unmount_mp((opaque_t) mp);
    754 	} else {
    755 	  am_unmounted(mp);
    756 	}
    757 	if (!(mf->mf_flags & (MFF_UNMOUNTING|MFF_MOUNTED)))
    758 	  exported_ap[i] = NULL;
    759       } else {
    760 	/*
    761 	 * Any other node gets forcibly timed out.
    762 	 */
    763 	mp->am_flags &= ~AMF_NOTIMEOUT;
    764 	mp->am_al->al_mnt->mf_flags &= ~MFF_RSTKEEP;
    765 	mp->am_ttl = 0;
    766 	mp->am_timeo = 1;
    767 	mp->am_timeo_w = 0;
    768       }
    769     }
    770   } while (work_done);
    771 }
    772 
    773 
    774 /*
    775  * Try to mount a file system.  Can be called directly or in a sub-process by run_task.
    776  *
    777  * Warning: this function might be running in a child process context.
    778  * Don't expect any changes made here to survive in the parent amd process.
    779  */
    780 int
    781 mount_node(opaque_t arg)
    782 {
    783   am_node *mp = (am_node *) arg;
    784   mntfs *mf = mp->am_al->al_mnt;
    785   int error = 0;
    786 
    787 #ifdef HAVE_FS_AUTOFS
    788   if (mp->am_flags & AMF_AUTOFS)
    789     error = autofs_mount_fs(mp, mf);
    790   else
    791 #endif /* HAVE_FS_AUTOFS */
    792     if (!(mf->mf_flags & MFF_MOUNTED))
    793       error = mf->mf_ops->mount_fs(mp, mf);
    794 
    795   if (error > 0)
    796     dlog("mount_node: call to mf_ops->mount_fs(%s) failed: %s",
    797 	 mp->am_path, strerror(error));
    798   return error;
    799 }
    800 
    801 
    802 static int
    803 unmount_node(opaque_t arg)
    804 {
    805   am_node *mp = (am_node *) arg;
    806   mntfs *mf = mp->am_al->al_mnt;
    807   int error = 0;
    808 
    809   if (mf->mf_flags & MFF_ERROR) {
    810     /*
    811      * Just unlink
    812      */
    813     dlog("No-op unmount of error node %s", mf->mf_info);
    814   } else {
    815     dlog("Unmounting <%s> <%s> (%s) flags %x",
    816 	 mp->am_path, mf->mf_mount, mf->mf_info, mf->mf_flags);
    817 #ifdef HAVE_FS_AUTOFS
    818     if (mp->am_flags & AMF_AUTOFS)
    819       error = autofs_umount_fs(mp, mf);
    820     else
    821 #endif /* HAVE_FS_AUTOFS */
    822       if (mf->mf_refc == 1)
    823 	error = mf->mf_ops->umount_fs(mp, mf);
    824   }
    825 
    826   /* do this again, it might have changed */
    827   mf = mp->am_al->al_mnt;
    828   if (error) {
    829     errno = error;		/* XXX */
    830     dlog("%s: unmount: %m", mf->mf_mount);
    831   }
    832 
    833   return error;
    834 }
    835 
    836 
    837 static void
    838 free_map_if_success(int rc, int term, opaque_t arg)
    839 {
    840   am_node *mp = (am_node *) arg;
    841   mntfs *mf = mp->am_al->al_mnt;
    842   wchan_t wchan = get_mntfs_wchan(mf);
    843 
    844   /*
    845    * Not unmounting any more
    846    */
    847   mf->mf_flags &= ~MFF_UNMOUNTING;
    848 
    849   /*
    850    * If a timeout was deferred because the underlying filesystem
    851    * was busy then arrange for a timeout as soon as possible.
    852    */
    853   if (mf->mf_flags & MFF_WANTTIMO) {
    854     mf->mf_flags &= ~MFF_WANTTIMO;
    855     reschedule_timeout_mp();
    856   }
    857   if (term) {
    858     notify_child(mp, AMQ_UMNT_SIGNAL, 0, term);
    859     plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
    860 #if defined(DEBUG) && defined(SIGTRAP)
    861     /*
    862      * dbx likes to put a trap on exit().
    863      * Pretend it succeeded for now...
    864      */
    865     if (term == SIGTRAP) {
    866       am_unmounted(mp);
    867     }
    868 #endif /* DEBUG */
    869 #ifdef HAVE_FS_AUTOFS
    870     if (mp->am_flags & AMF_AUTOFS)
    871       autofs_umount_failed(mp);
    872 #endif /* HAVE_FS_AUTOFS */
    873     amd_stats.d_uerr++;
    874   } else if (rc) {
    875     notify_child(mp, AMQ_UMNT_FAILED, rc, 0);
    876     if (mf->mf_ops == &amfs_program_ops || rc == EBUSY)
    877       plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
    878     else
    879       plog(XLOG_ERROR, "%s: unmount: %s", mp->am_path, strerror(rc));
    880 #ifdef HAVE_FS_AUTOFS
    881     if (rc != ENOENT) {
    882       if (mf->mf_flags & MFF_IS_AUTOFS)
    883         autofs_get_mp(mp);
    884       if (mp->am_flags & AMF_AUTOFS)
    885         autofs_umount_failed(mp);
    886     }
    887 #endif /* HAVE_FS_AUTOFS */
    888     amd_stats.d_uerr++;
    889   } else {
    890     /*
    891      * am_unmounted() will call notify_child() appropriately.
    892      */
    893     am_unmounted(mp);
    894   }
    895 
    896   /*
    897    * Wakeup anything waiting for this unmount
    898    */
    899   wakeup(wchan);
    900 }
    901 
    902 
    903 int
    904 unmount_mp(am_node *mp)
    905 {
    906   int was_backgrounded = 0;
    907   mntfs *mf = mp->am_al->al_mnt;
    908 
    909 #ifdef notdef
    910   plog(XLOG_INFO, "\"%s\" on %s timed out (flags 0x%x)",
    911        mp->am_path, mf->mf_mount, (int) mf->mf_flags);
    912 #endif /* notdef */
    913 
    914 #ifndef MNT2_NFS_OPT_SYMTTL
    915     /*
    916      * This code is needed to defeat Solaris 2.4's (and newer) symlink
    917      * values cache.  It forces the last-modified time of the symlink to be
    918      * current.  It is not needed if the O/S has an nfs flag to turn off the
    919      * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez.
    920      *
    921      * Additionally, Linux currently ignores the nt_useconds field,
    922      * so we must update the nt_seconds field every time if clocktime(NULL)
    923      * didn't return a new number of seconds.
    924      */
    925   if (mp->am_parent) {
    926     time_t last = mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds;
    927     clocktime(&mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime);
    928     /* defensive programming... can't we assert the above condition? */
    929     if (last == (time_t) mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds)
    930       mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++;
    931   }
    932 #endif /* not MNT2_NFS_OPT_SYMTTL */
    933 
    934   if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) {
    935     /*
    936      * Don't try to unmount from a server that is known to be down
    937      */
    938     if (!(mf->mf_flags & MFF_LOGDOWN)) {
    939       /* Only log this once, otherwise gets a bit boring */
    940       plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
    941       mf->mf_flags |= MFF_LOGDOWN;
    942     }
    943     notify_child(mp, AMQ_UMNT_SERVER, 0, 0);
    944     return 0;
    945   }
    946 
    947   dlog("\"%s\" on %s timed out", mp->am_path, mf->mf_mount);
    948   mf->mf_flags |= MFF_UNMOUNTING;
    949 
    950 #ifdef HAVE_FS_AUTOFS
    951   if (mf->mf_flags & MFF_IS_AUTOFS)
    952     autofs_release_mp(mp);
    953 #endif /* HAVE_FS_AUTOFS */
    954 
    955   if ((mf->mf_fsflags & FS_UBACKGROUND) &&
    956       (mf->mf_flags & MFF_MOUNTED) &&
    957      !(mf->mf_flags & MFF_ON_AUTOFS)) {
    958     dlog("Trying unmount in background");
    959     run_task(unmount_node, (opaque_t) mp,
    960 	     free_map_if_success, (opaque_t) mp);
    961     was_backgrounded = 1;
    962   } else {
    963     dlog("Trying unmount in foreground");
    964     free_map_if_success(unmount_node((opaque_t) mp), 0, (opaque_t) mp);
    965     dlog("unmount attempt done");
    966   }
    967 
    968   return was_backgrounded;
    969 }
    970 
    971 
    972 void
    973 timeout_mp(opaque_t v)				/* argument not used?! */
    974 {
    975   int i;
    976   time_t t = NEVER;
    977   time_t now = clocktime(NULL);
    978   int backoff = NumChildren / 4;
    979 
    980   dlog("Timing out automount points...");
    981 
    982   for (i = last_used_map; i >= 0; --i) {
    983     am_node *mp = exported_ap[i];
    984     mntfs *mf;
    985 
    986     /*
    987      * Just continue if nothing mounted
    988      */
    989     if (!mp)
    990       continue;
    991 
    992     /*
    993      * Pick up mounted filesystem
    994      */
    995     mf = mp->am_al->al_mnt;
    996     if (!mf)
    997       continue;
    998 
    999 #ifdef HAVE_FS_AUTOFS
   1000     if (mf->mf_flags & MFF_IS_AUTOFS && mp->am_autofs_ttl != NEVER) {
   1001       if (now >= mp->am_autofs_ttl)
   1002 	autofs_timeout_mp(mp);
   1003       t = smallest_t(t, mp->am_autofs_ttl);
   1004     }
   1005 #endif /* HAVE_FS_AUTOFS */
   1006 
   1007     if (mp->am_flags & AMF_NOTIMEOUT)
   1008       continue;
   1009 
   1010     /*
   1011      * Don't delete last reference to a restarted filesystem.
   1012      */
   1013     if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1)
   1014       continue;
   1015 
   1016     /*
   1017      * If there is action on this filesystem then ignore it
   1018      */
   1019     if (!(mf->mf_flags & IGNORE_FLAGS)) {
   1020       int expired = 0;
   1021       mf->mf_flags &= ~MFF_WANTTIMO;
   1022       if (now >= mp->am_ttl) {
   1023 	if (!backoff) {
   1024 	  expired = 1;
   1025 
   1026 	  /*
   1027 	   * Move the ttl forward to avoid thrashing effects
   1028 	   * on the next call to timeout!
   1029 	   */
   1030 	  /* sun's -tw option */
   1031 	  if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
   1032 	    mp->am_timeo_w += gopt.am_timeo_w;
   1033 	  mp->am_ttl = now + mp->am_timeo_w;
   1034 
   1035 	} else {
   1036 	  /*
   1037 	   * Just backoff this unmount for
   1038 	   * a couple of seconds to avoid
   1039 	   * many multiple unmounts being
   1040 	   * started in parallel.
   1041 	   */
   1042 	  mp->am_ttl = now + backoff + 1;
   1043 	}
   1044       }
   1045 
   1046       /*
   1047        * If the next ttl is smallest, use that
   1048        */
   1049       t = smallest_t(t, mp->am_ttl);
   1050 
   1051       if (!mp->am_child && mf->mf_error >= 0 && expired) {
   1052 	/*
   1053 	 * If the unmount was backgrounded then
   1054 	 * bump the backoff counter.
   1055 	 */
   1056 	if (unmount_mp(mp)) {
   1057 	  backoff = 2;
   1058 	}
   1059       }
   1060     } else if (mf->mf_flags & MFF_UNMOUNTING) {
   1061       mf->mf_flags |= MFF_WANTTIMO;
   1062     }
   1063   }
   1064 
   1065   if (t == NEVER) {
   1066     dlog("No further timeouts");
   1067     t = now + ONE_HOUR;
   1068   }
   1069 
   1070   /*
   1071    * Sanity check to avoid runaways.
   1072    * Absolutely should never get this but
   1073    * if you do without this trap amd will thrash.
   1074    */
   1075   if (t <= now) {
   1076     t = now + 6;		/* XXX */
   1077     plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!");
   1078   }
   1079 
   1080   /*
   1081    * XXX - when shutting down, make things happen faster
   1082    */
   1083   if ((int) amd_state >= (int) Finishing)
   1084     t = now + 1;
   1085   dlog("Next mount timeout in %lds", (long) (t - now));
   1086 
   1087   timeout_mp_id = timeout(t - now, timeout_mp, NULL);
   1088 }
   1089 
   1090 
   1091 /*
   1092  * Cause timeout_mp to be called soonest
   1093  */
   1094 void
   1095 reschedule_timeout_mp(void)
   1096 {
   1097   if (timeout_mp_id)
   1098     untimeout(timeout_mp_id);
   1099   timeout_mp_id = timeout(0, timeout_mp, NULL);
   1100 }
   1101