138494Sobrien/*
2310490Scy * Copyright (c) 1997-2014 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
2038494Sobrien *    may be used to endorse or promote products derived from this software
2138494Sobrien *    without specific prior written permission.
2238494Sobrien *
2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338494Sobrien * SUCH DAMAGE.
3438494Sobrien *
3538494Sobrien *
36174294Sobrien * File: am-utils/amd/map.c
3738494Sobrien *
3838494Sobrien */
3938494Sobrien
4038494Sobrien#ifdef HAVE_CONFIG_H
4138494Sobrien# include <config.h>
4238494Sobrien#endif /* HAVE_CONFIG_H */
4338494Sobrien#include <am_defs.h>
4438494Sobrien#include <amd.h>
4538494Sobrien
4638494Sobrien#define	smallest_t(t1, t2) (t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2)
4738494Sobrien#define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART)
4838494Sobrien#define new_gen() (am_gen++)
4938494Sobrien
5038494Sobrien/*
5138494Sobrien * Generation Numbers.
5238494Sobrien *
5338494Sobrien * Generation numbers are allocated to every node created
5438494Sobrien * by amd.  When a filehandle is computed and sent to the
5538494Sobrien * kernel, the generation number makes sure that it is safe
5638494Sobrien * to reallocate a node slot even when the kernel has a cached
5738494Sobrien * reference to its old incarnation.
5838494Sobrien * No garbage collection is done, since it is assumed that
5938494Sobrien * there is no way that 2^32 generation numbers could ever
6038494Sobrien * be allocated by a single run of amd - there is simply
6138494Sobrien * not enough cpu time available.
6282794Sobrien * Famous last words... -Ion
6338494Sobrien */
6438494Sobrienstatic u_int am_gen = 2;	/* Initial generation number */
6538494Sobrienstatic int timeout_mp_id;	/* Id from last call to timeout */
6638494Sobrien
67174294Sobrienstatic am_node *root_node;	/* The root of the mount tree */
68310490Scystatic am_node **exported_ap = (am_node **) NULL;
69174294Sobrienstatic int exported_ap_size = 0;
70174294Sobrienstatic int first_free_map = 0;	/* First available free slot */
71174294Sobrienstatic int last_used_map = -1;	/* Last unavailable used slot */
7238494Sobrien
7338494Sobrien
7438494Sobrien/*
7538494Sobrien * This is the default attributes field which
7638494Sobrien * is copied into every new node to be created.
7738494Sobrien * The individual filesystem fs_init() routines
7838494Sobrien * patch the copy to represent the particular
7938494Sobrien * details for the relevant filesystem type
8038494Sobrien */
8138494Sobrienstatic nfsfattr gen_fattr =
8238494Sobrien{
8338494Sobrien  NFLNK,			/* type */
8438494Sobrien  NFSMODE_LNK | 0777,		/* mode */
8538494Sobrien  1,				/* nlink */
8638494Sobrien  0,				/* uid */
8738494Sobrien  0,				/* gid */
8838494Sobrien  0,				/* size */
8938494Sobrien  4096,				/* blocksize */
9038494Sobrien  0,				/* rdev */
9138494Sobrien  1,				/* blocks */
9238494Sobrien  0,				/* fsid */
9338494Sobrien  0,				/* fileid */
9438494Sobrien  {0, 0},			/* atime */
9538494Sobrien  {0, 0},			/* mtime */
9638494Sobrien  {0, 0},			/* ctime */
9738494Sobrien};
9838494Sobrien
9938494Sobrien/* forward declarations */
100174294Sobrienstatic int unmount_node(opaque_t arg);
10138494Sobrienstatic void exported_ap_free(am_node *mp);
10238494Sobrienstatic void remove_am(am_node *mp);
103174294Sobrienstatic am_node *get_root_ap(char *dir);
10438494Sobrien
10538494Sobrien
10638494Sobrien/*
107174294Sobrien * Iterator functions for exported_ap[]
108174294Sobrien */
109174294Sobrienam_node *
110174294Sobrienget_first_exported_ap(int *index)
111174294Sobrien{
112174294Sobrien  *index = -1;
113174294Sobrien  return get_next_exported_ap(index);
114174294Sobrien}
115174294Sobrien
116174294Sobrien
117174294Sobrienam_node *
118174294Sobrienget_next_exported_ap(int *index)
119174294Sobrien{
120174294Sobrien  (*index)++;
121174294Sobrien  while (*index < exported_ap_size) {
122174294Sobrien    if (exported_ap[*index] != NULL)
123174294Sobrien      return exported_ap[*index];
124174294Sobrien    (*index)++;
125174294Sobrien  }
126174294Sobrien  return NULL;
127174294Sobrien}
128174294Sobrien
129174294Sobrien
130174294Sobrien/*
131174294Sobrien * Get exported_ap by index
132174294Sobrien */
133174294Sobrienam_node *
134174294Sobrienget_exported_ap(int index)
135174294Sobrien{
136174294Sobrien  if (index < 0 || index >= exported_ap_size)
137174294Sobrien    return 0;
138174294Sobrien  return exported_ap[index];
139174294Sobrien}
140174294Sobrien
141174294Sobrien
142174294Sobrien/*
143174294Sobrien * Get exported_ap by path
144174294Sobrien */
145174294Sobrienam_node *
146174294Sobrienpath_to_exported_ap(char *path)
147174294Sobrien{
148174294Sobrien  int index;
149174294Sobrien  am_node *mp;
150174294Sobrien
151174294Sobrien  mp = get_first_exported_ap(&index);
152174294Sobrien  while (mp != NULL) {
153174294Sobrien    if (STREQ(mp->am_path, path))
154174294Sobrien      break;
155174294Sobrien    mp = get_next_exported_ap(&index);
156174294Sobrien  }
157174294Sobrien  return mp;
158174294Sobrien}
159174294Sobrien
160174294Sobrien
161174294Sobrien/*
16238494Sobrien * Resize exported_ap map
16338494Sobrien */
16438494Sobrienstatic int
16538494Sobrienexported_ap_realloc_map(int nsize)
16638494Sobrien{
16738494Sobrien  /*
16838494Sobrien   * this shouldn't happen, but...
16938494Sobrien   */
17038494Sobrien  if (nsize < 0 || nsize == exported_ap_size)
17138494Sobrien    return 0;
17238494Sobrien
17338494Sobrien  exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node *));
17438494Sobrien
17538494Sobrien  if (nsize > exported_ap_size)
17638494Sobrien    memset((char *) (exported_ap + exported_ap_size), 0,
17738494Sobrien	  (nsize - exported_ap_size) * sizeof(am_node *));
17838494Sobrien  exported_ap_size = nsize;
17938494Sobrien
18038494Sobrien  return 1;
18138494Sobrien}
18238494Sobrien
18338494Sobrien
184174294Sobrien
185174294Sobrienam_node *
186174294Sobrienget_ap_child(am_node *mp, char *fname)
187174294Sobrien{
188174294Sobrien  am_node *new_mp;
189310490Scy  mntfs *mf = mp->am_al->al_mnt;
190174294Sobrien
191174294Sobrien  /*
192174294Sobrien   * Allocate a new map
193174294Sobrien   */
194174294Sobrien  new_mp = exported_ap_alloc();
195174294Sobrien  if (new_mp) {
196174294Sobrien    /*
197174294Sobrien     * Fill it in
198174294Sobrien     */
199174294Sobrien    init_map(new_mp, fname);
200174294Sobrien
201174294Sobrien    /*
202174294Sobrien     * Put it in the table
203174294Sobrien     */
204174294Sobrien    insert_am(new_mp, mp);
205174294Sobrien
206174294Sobrien    /*
207174294Sobrien     * Fill in some other fields,
208174294Sobrien     * path and mount point.
209174294Sobrien     *
210174294Sobrien     * bugfix: do not prepend old am_path if direct map
211174294Sobrien     *         <wls@astro.umd.edu> William Sebok
212174294Sobrien     */
213174294Sobrien    new_mp->am_path = str3cat(new_mp->am_path,
214174294Sobrien			      (mf->mf_fsflags & FS_DIRECT)
215174294Sobrien				     ? ""
216174294Sobrien				     : mp->am_path,
217174294Sobrien			      *fname == '/' ? "" : "/", fname);
218174294Sobrien    dlog("setting path to %s", new_mp->am_path);
219174294Sobrien  }
220174294Sobrien
221174294Sobrien  return new_mp;
222174294Sobrien}
223174294Sobrien
22438494Sobrien/*
22538494Sobrien * Allocate a new mount slot and create
22638494Sobrien * a new node.
22738494Sobrien * Fills in the map number of the node,
22842629Sobrien * but leaves everything else uninitialized.
22938494Sobrien */
23038494Sobrienam_node *
23138494Sobrienexported_ap_alloc(void)
23238494Sobrien{
23338494Sobrien  am_node *mp, **mpp;
23438494Sobrien
23538494Sobrien  /*
23638494Sobrien   * First check if there are any slots left, realloc if needed
23738494Sobrien   */
23838494Sobrien  if (first_free_map >= exported_ap_size)
23938494Sobrien    if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP))
24038494Sobrien      return 0;
24138494Sobrien
24238494Sobrien  /*
24338494Sobrien   * Grab the next free slot
24438494Sobrien   */
24538494Sobrien  mpp = exported_ap + first_free_map;
24638494Sobrien  mp = *mpp = ALLOC(struct am_node);
247174294Sobrien  memset((char *) mp, 0, sizeof(struct am_node));
24838494Sobrien
24938494Sobrien  mp->am_mapno = first_free_map++;
25038494Sobrien
25138494Sobrien  /*
25238494Sobrien   * Update free pointer
25338494Sobrien   */
25438494Sobrien  while (first_free_map < exported_ap_size && exported_ap[first_free_map])
25538494Sobrien    first_free_map++;
25638494Sobrien
25738494Sobrien  if (first_free_map > last_used_map)
25838494Sobrien    last_used_map = first_free_map - 1;
25938494Sobrien
26038494Sobrien  return mp;
26138494Sobrien}
26238494Sobrien
26338494Sobrien
26438494Sobrien/*
26538494Sobrien * Free a mount slot
26638494Sobrien */
26738494Sobrienstatic void
26838494Sobrienexported_ap_free(am_node *mp)
26938494Sobrien{
27038494Sobrien  /*
27138494Sobrien   * Sanity check
27238494Sobrien   */
27338494Sobrien  if (!mp)
27438494Sobrien    return;
27538494Sobrien
27638494Sobrien  /*
27738494Sobrien   * Zero the slot pointer to avoid double free's
27838494Sobrien   */
279310490Scy  exported_ap[mp->am_mapno] = NULL;
28038494Sobrien
28138494Sobrien  /*
28238494Sobrien   * Update the free and last_used indices
28338494Sobrien   */
28438494Sobrien  if (mp->am_mapno == last_used_map)
28538494Sobrien    while (last_used_map >= 0 && exported_ap[last_used_map] == 0)
28638494Sobrien      --last_used_map;
28738494Sobrien
28838494Sobrien  if (first_free_map > mp->am_mapno)
28938494Sobrien    first_free_map = mp->am_mapno;
29038494Sobrien
29138494Sobrien  /*
292174294Sobrien   * Free the mount node, and zero out it's internal struct data.
29338494Sobrien   */
294174294Sobrien  memset((char *) mp, 0, sizeof(am_node));
29538494Sobrien  XFREE(mp);
29638494Sobrien}
29738494Sobrien
29838494Sobrien
29938494Sobrien/*
30038494Sobrien * Insert mp into the correct place,
30138494Sobrien * where p_mp is its parent node.
30238494Sobrien * A new node gets placed as the youngest sibling
30338494Sobrien * of any other children, and the parent's child
30438494Sobrien * pointer is adjusted to point to the new child node.
30538494Sobrien */
30638494Sobrienvoid
30738494Sobrieninsert_am(am_node *mp, am_node *p_mp)
30838494Sobrien{
30938494Sobrien  /*
31038494Sobrien   * If this is going in at the root then flag it
31138494Sobrien   * so that it cannot be unmounted by amq.
31238494Sobrien   */
31338494Sobrien  if (p_mp == root_node)
31438494Sobrien    mp->am_flags |= AMF_ROOT;
31538494Sobrien  /*
31638494Sobrien   * Fill in n-way links
31738494Sobrien   */
31838494Sobrien  mp->am_parent = p_mp;
31938494Sobrien  mp->am_osib = p_mp->am_child;
32038494Sobrien  if (mp->am_osib)
32138494Sobrien    mp->am_osib->am_ysib = mp;
32238494Sobrien  p_mp->am_child = mp;
323174294Sobrien#ifdef HAVE_FS_AUTOFS
324310490Scy  if (p_mp->am_al->al_mnt->mf_flags & MFF_IS_AUTOFS)
325174294Sobrien    mp->am_flags |= AMF_AUTOFS;
326174294Sobrien#endif /* HAVE_FS_AUTOFS */
32738494Sobrien}
32838494Sobrien
32938494Sobrien
33038494Sobrien/*
33138494Sobrien * Remove am from its place in the mount tree
33238494Sobrien */
33338494Sobrienstatic void
33438494Sobrienremove_am(am_node *mp)
33538494Sobrien{
33638494Sobrien  /*
33738494Sobrien   * 1.  Consistency check
33838494Sobrien   */
33938494Sobrien  if (mp->am_child && mp->am_parent) {
34038494Sobrien    plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path);
34138494Sobrien  }
34238494Sobrien
34338494Sobrien  /*
34438494Sobrien   * 2.  Update parent's child pointer
34538494Sobrien   */
34638494Sobrien  if (mp->am_parent && mp->am_parent->am_child == mp)
34738494Sobrien    mp->am_parent->am_child = mp->am_osib;
34838494Sobrien
34938494Sobrien  /*
35038494Sobrien   * 3.  Unlink from sibling chain
35138494Sobrien   */
35238494Sobrien  if (mp->am_ysib)
35338494Sobrien    mp->am_ysib->am_osib = mp->am_osib;
35438494Sobrien  if (mp->am_osib)
35538494Sobrien    mp->am_osib->am_ysib = mp->am_ysib;
35638494Sobrien}
35738494Sobrien
35838494Sobrien
35938494Sobrien/*
36038494Sobrien * Compute a new time to live value for a node.
36138494Sobrien */
36238494Sobrienvoid
36338494Sobriennew_ttl(am_node *mp)
36438494Sobrien{
36538494Sobrien  mp->am_timeo_w = 0;
366174294Sobrien  mp->am_ttl = clocktime(&mp->am_fattr.na_atime);
36738494Sobrien  mp->am_ttl += mp->am_timeo;	/* sun's -tl option */
36838494Sobrien}
36938494Sobrien
37038494Sobrien
37138494Sobrienvoid
372174294Sobrienmk_fattr(nfsfattr *fattr, nfsftype vntype)
37338494Sobrien{
37438494Sobrien  switch (vntype) {
37538494Sobrien  case NFDIR:
376174294Sobrien    fattr->na_type = NFDIR;
377174294Sobrien    fattr->na_mode = NFSMODE_DIR | 0555;
378174294Sobrien    fattr->na_nlink = 2;
379174294Sobrien    fattr->na_size = 512;
38038494Sobrien    break;
38138494Sobrien  case NFLNK:
382174294Sobrien    fattr->na_type = NFLNK;
383174294Sobrien    fattr->na_mode = NFSMODE_LNK | 0777;
384174294Sobrien    fattr->na_nlink = 1;
385174294Sobrien    fattr->na_size = 0;
38638494Sobrien    break;
38738494Sobrien  default:
38838494Sobrien    plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype);
38938494Sobrien    break;
39038494Sobrien  }
39138494Sobrien}
39238494Sobrien
39338494Sobrien
39438494Sobrien/*
39542629Sobrien * Initialize an allocated mount node.
39638494Sobrien * It is assumed that the mount node was b-zero'd
39738494Sobrien * before getting here so anything that would
39838494Sobrien * be set to zero isn't done here.
39938494Sobrien */
40038494Sobrienvoid
40138494Sobrieninit_map(am_node *mp, char *dir)
40238494Sobrien{
40338494Sobrien  /*
40438494Sobrien   * mp->am_mapno is initialized by exported_ap_alloc
405174294Sobrien   * other fields don't need to be set to zero.
40638494Sobrien   */
407310490Scy
408310490Scy  mp->am_al = new_loc();
409310490Scy  mp->am_alarray = NULL;
410310490Scy  mp->am_name = xstrdup(dir);
411310490Scy  mp->am_path = xstrdup(dir);
41238494Sobrien  mp->am_gen = new_gen();
413174294Sobrien#ifdef HAVE_FS_AUTOFS
414310490Scy  mp->am_autofs_fh = NULL;
415174294Sobrien#endif /* HAVE_FS_AUTOFS */
41638494Sobrien
41738494Sobrien  mp->am_timeo = gopt.am_timeo;
41838494Sobrien  mp->am_attr.ns_status = NFS_OK;
41938494Sobrien  mp->am_fattr = gen_fattr;
42038494Sobrien  mp->am_fattr.na_fsid = 42;
42182794Sobrien  mp->am_fattr.na_fileid = mp->am_gen;
422174294Sobrien  clocktime(&mp->am_fattr.na_atime);
423174294Sobrien  /* next line copies a "struct nfstime" among several fields */
42438494Sobrien  mp->am_fattr.na_mtime = mp->am_fattr.na_ctime = mp->am_fattr.na_atime;
42538494Sobrien
42638494Sobrien  new_ttl(mp);
42738494Sobrien  mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds;
428174294Sobrien  mp->am_dev = -1;
429174294Sobrien  mp->am_rdev = -1;
430310490Scy  mp->am_fd[0] = -1;
431310490Scy  mp->am_fd[1] = -1;
43238494Sobrien}
43338494Sobrien
43438494Sobrien
435310490Scyvoid
436310490Scynotify_child(am_node *mp, au_etype au_etype, int au_errno, int au_signal)
437310490Scy{
438310490Scy  amq_sync_umnt rv;
439310490Scy  int err;
440310490Scy
441310490Scy  if (mp->am_fd[1] >= 0) {	/* we have a child process */
442310490Scy    rv.au_etype = au_etype;
443310490Scy    rv.au_signal = au_signal;
444310490Scy    rv.au_errno = au_errno;
445310490Scy
446310490Scy    err = write(mp->am_fd[1], &rv, sizeof(rv));
447310490Scy    /* XXX: do something else on err? */
448310490Scy    if (err < sizeof(rv))
449310490Scy      plog(XLOG_INFO, "notify_child: write returned %d instead of %d.",
450310490Scy	   err, (int) sizeof(rv));
451310490Scy    close(mp->am_fd[1]);
452310490Scy    mp->am_fd[1] = -1;
453310490Scy  }
454310490Scy}
455310490Scy
456310490Scy
45738494Sobrien/*
45838494Sobrien * Free a mount node.
45938494Sobrien * The node must be already unmounted.
46038494Sobrien */
46138494Sobrienvoid
46238494Sobrienfree_map(am_node *mp)
46338494Sobrien{
46438494Sobrien  remove_am(mp);
46538494Sobrien
466310490Scy  if (mp->am_fd[1] != -1)
467310490Scy    plog(XLOG_FATAL, "free_map: called prior to notifying the child for %s.",
468310490Scy	mp->am_path);
46938494Sobrien
470310490Scy  XFREE(mp->am_link);
471310490Scy  XFREE(mp->am_name);
472310490Scy  XFREE(mp->am_path);
473310490Scy  XFREE(mp->am_pref);
474310490Scy  XFREE(mp->am_transp);
47538494Sobrien
476310490Scy  if (mp->am_al)
477310490Scy    free_loc(mp->am_al);
478310490Scy
479310490Scy  if (mp->am_alarray) {
480310490Scy    am_loc **temp_al;
481310490Scy    for (temp_al = mp->am_alarray; *temp_al; temp_al++)
482310490Scy      free_loc(*temp_al);
483310490Scy    XFREE(mp->am_alarray);
48438494Sobrien  }
48538494Sobrien
486174294Sobrien#ifdef HAVE_FS_AUTOFS
487174294Sobrien  if (mp->am_autofs_fh)
488174294Sobrien    autofs_release_fh(mp);
489174294Sobrien#endif /* HAVE_FS_AUTOFS */
49038494Sobrien
491174294Sobrien  exported_ap_free(mp);
49238494Sobrien}
49338494Sobrien
49438494Sobrien
495174294Sobrienstatic am_node *
496174294Sobrienfind_ap_recursive(char *dir, am_node *mp)
49738494Sobrien{
49838494Sobrien  if (mp) {
49938494Sobrien    am_node *mp2;
50038494Sobrien    if (STREQ(mp->am_path, dir))
50138494Sobrien      return mp;
50238494Sobrien
503310490Scy    if ((mp->am_al->al_mnt->mf_flags & MFF_MOUNTED) &&
504310490Scy	STREQ(mp->am_al->al_mnt->mf_mount, dir))
50538494Sobrien      return mp;
50638494Sobrien
507174294Sobrien    mp2 = find_ap_recursive(dir, mp->am_osib);
50838494Sobrien    if (mp2)
50938494Sobrien      return mp2;
510174294Sobrien    return find_ap_recursive(dir, mp->am_child);
51138494Sobrien  }
51238494Sobrien
51338494Sobrien  return 0;
51438494Sobrien}
51538494Sobrien
51638494Sobrien
51738494Sobrien/*
51838494Sobrien * Find the mount node corresponding to dir.  dir can match either the
51938494Sobrien * automount path or, if the node is mounted, the mount location.
52038494Sobrien */
52138494Sobrienam_node *
52238494Sobrienfind_ap(char *dir)
52338494Sobrien{
52438494Sobrien  int i;
52538494Sobrien
52638494Sobrien  for (i = last_used_map; i >= 0; --i) {
52738494Sobrien    am_node *mp = exported_ap[i];
52838494Sobrien    if (mp && (mp->am_flags & AMF_ROOT)) {
529174294Sobrien      mp = find_ap_recursive(dir, exported_ap[i]);
53038494Sobrien      if (mp) {
53138494Sobrien	return mp;
53238494Sobrien      }
53338494Sobrien    }
53438494Sobrien  }
53538494Sobrien
53638494Sobrien  return 0;
53738494Sobrien}
53838494Sobrien
53938494Sobrien
54038494Sobrien/*
54138494Sobrien * Get the filehandle for a particular named directory.
54238494Sobrien * This is used during the bootstrap to tell the kernel
54338494Sobrien * the filehandles of the initial automount points.
54438494Sobrien */
545310490Scyam_nfs_handle_t *
546310490Scyget_root_nfs_fh(char *dir, am_nfs_handle_t *nfh)
54738494Sobrien{
548174294Sobrien  am_node *mp = get_root_ap(dir);
54938494Sobrien  if (mp) {
550310490Scy    if (nfs_dispatcher == nfs_program_2)
551310490Scy      mp_to_fh(mp, &nfh->v2);
552310490Scy    else
553310490Scy      mp_to_fh3(mp, &nfh->v3);
554310490Scy    return nfh;
55538494Sobrien  }
55638494Sobrien
55738494Sobrien  /*
55838494Sobrien   * Should never get here...
55938494Sobrien   */
56038494Sobrien  plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
56138494Sobrien
56238494Sobrien  return 0;
56338494Sobrien}
56438494Sobrien
56538494Sobrien
566174294Sobrienstatic am_node *
567174294Sobrienget_root_ap(char *dir)
56838494Sobrien{
56938494Sobrien  am_node *mp = find_ap(dir);
57038494Sobrien
57138494Sobrien  if (mp && mp->am_parent == root_node)
57238494Sobrien    return mp;
57338494Sobrien
57438494Sobrien  return 0;
57538494Sobrien}
57638494Sobrien
57738494Sobrien
57838494Sobrien/*
57938494Sobrien * Timeout all nodes waiting on
58038494Sobrien * a given Fserver.
58138494Sobrien */
58238494Sobrienvoid
58338494Sobrienmap_flush_srvr(fserver *fs)
58438494Sobrien{
58538494Sobrien  int i;
58638494Sobrien  int done = 0;
58738494Sobrien
58838494Sobrien  for (i = last_used_map; i >= 0; --i) {
58938494Sobrien    am_node *mp = exported_ap[i];
590310490Scy
591310490Scy    if (mp && mp->am_al->al_mnt && mp->am_al->al_mnt->mf_server == fs) {
59238494Sobrien      plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host);
593174294Sobrien      mp->am_ttl = clocktime(NULL);
59438494Sobrien      done = 1;
59538494Sobrien    }
59638494Sobrien  }
59738494Sobrien  if (done)
59838494Sobrien    reschedule_timeout_mp();
59938494Sobrien}
60038494Sobrien
60138494Sobrien
60238494Sobrien/*
60338494Sobrien * Mount a top level automount node
60438494Sobrien * by calling lookup in the parent
60538494Sobrien * (root) node which will cause the
60638494Sobrien * automount node to be automounted.
60738494Sobrien */
60838494Sobrienint
609174294Sobrienmount_auto_node(char *dir, opaque_t arg)
61038494Sobrien{
61138494Sobrien  int error = 0;
612174294Sobrien  am_node *mp = (am_node *) arg;
613174294Sobrien  am_node *new_mp;
61438494Sobrien
615310490Scy  new_mp = mp->am_al->al_mnt->mf_ops->lookup_child(mp, dir, &error, VLOOK_CREATE);
616174294Sobrien  if (new_mp && error < 0) {
617174294Sobrien    /*
618174294Sobrien     * We can't allow the fileid of the root node to change.
619174294Sobrien     * Should be ok to force it to 1, always.
620174294Sobrien     */
621174294Sobrien    new_mp->am_gen = new_mp->am_fattr.na_fileid = 1;
622174294Sobrien
623310490Scy    (void) mp->am_al->al_mnt->mf_ops->mount_child(new_mp, &error);
624174294Sobrien  }
625174294Sobrien
62638494Sobrien  if (error > 0) {
62738494Sobrien    errno = error;		/* XXX */
62838494Sobrien    plog(XLOG_ERROR, "Could not mount %s: %m", dir);
62938494Sobrien  }
63038494Sobrien  return error;
63138494Sobrien}
63238494Sobrien
63338494Sobrien
63438494Sobrien/*
63538494Sobrien * Cause all the top-level mount nodes
63638494Sobrien * to be automounted
63738494Sobrien */
63838494Sobrienint
63938494Sobrienmount_exported(void)
64038494Sobrien{
64138494Sobrien  /*
64238494Sobrien   * Iterate over all the nodes to be started
64338494Sobrien   */
644174294Sobrien  return root_keyiter(mount_auto_node, root_node);
64538494Sobrien}
64638494Sobrien
64738494Sobrien
64838494Sobrien/*
64938494Sobrien * Construct top-level node
65038494Sobrien */
65138494Sobrienvoid
65238494Sobrienmake_root_node(void)
65338494Sobrien{
654310490Scy  mntfs *root_mf;
65538494Sobrien  char *rootmap = ROOT_MAP;
65638494Sobrien  root_node = exported_ap_alloc();
65738494Sobrien
65838494Sobrien  /*
65938494Sobrien   * Allocate a new map
66038494Sobrien   */
66138494Sobrien  init_map(root_node, "");
66238494Sobrien
66338494Sobrien  /*
66438494Sobrien   * Allocate a new mounted filesystem
66538494Sobrien   */
666310490Scy  root_mf = find_mntfs(&amfs_root_ops, (am_opts *) NULL, "", rootmap, "", "", "");
66738494Sobrien
66838494Sobrien  /*
66938494Sobrien   * Replace the initial null reference
67038494Sobrien   */
671310490Scy  free_mntfs(root_node->am_al->al_mnt);
672310490Scy  root_node->am_al->al_mnt = root_mf;
67338494Sobrien
67438494Sobrien  /*
67542629Sobrien   * Initialize the root
67638494Sobrien   */
677310490Scy  if (root_mf->mf_ops->fs_init)
678310490Scy    (*root_mf->mf_ops->fs_init) (root_mf);
67938494Sobrien
68038494Sobrien  /*
68138494Sobrien   * Mount the root
68238494Sobrien   */
683310490Scy  root_mf->mf_error = root_mf->mf_ops->mount_fs(root_node, root_mf);
68438494Sobrien}
68538494Sobrien
68638494Sobrien
68738494Sobrien/*
68838494Sobrien * Cause all the nodes to be unmounted by timing
68938494Sobrien * them out.
69038494Sobrien */
69138494Sobrienvoid
69238494Sobrienumount_exported(void)
69338494Sobrien{
694310490Scy  int i, work_done;
69538494Sobrien
696310490Scy  do {
697310490Scy    work_done = 0;
69838494Sobrien
699310490Scy    for (i = last_used_map; i >= 0; --i) {
700310490Scy      am_node *mp = exported_ap[i];
701310490Scy      mntfs *mf;
70238494Sobrien
703310490Scy      if (!mp)
704310490Scy	continue;
70538494Sobrien
706174294Sobrien      /*
707310490Scy       * Wait for children to be removed first
708174294Sobrien       */
709310490Scy      if (mp->am_child)
710310490Scy	continue;
71138494Sobrien
712310490Scy      mf = mp->am_al->al_mnt;
713310490Scy      if (mf->mf_flags & MFF_UNMOUNTING) {
714310490Scy	/*
715310490Scy	 * If this node is being unmounted then just ignore it.  However,
716310490Scy	 * this could prevent amd from finishing if the unmount gets blocked
717310490Scy	 * since the am_node will never be free'd.  am_unmounted needs
718310490Scy	 * telling about this possibility. - XXX
719310490Scy	 */
720310490Scy	continue;
721310490Scy      }
722174294Sobrien
723310490Scy      if (!(mf->mf_fsflags & FS_DIRECTORY))
72438494Sobrien	/*
725310490Scy	 * When shutting down this had better
726310490Scy	 * look like a directory, otherwise it
727310490Scy	 * can't be unmounted!
72838494Sobrien	 */
729310490Scy	mk_fattr(&mp->am_fattr, NFDIR);
730310490Scy
731310490Scy      if ((--immediate_abort < 0 &&
732310490Scy	   !(mp->am_flags & AMF_ROOT) && mp->am_parent) ||
733310490Scy	  (mf->mf_flags & MFF_RESTART)) {
734310490Scy
735310490Scy	work_done++;
736310490Scy
737310490Scy	/*
738310490Scy	 * Just throw this node away without bothering to unmount it.  If
739310490Scy	 * the server is not known to be up then don't discard the mounted
740310490Scy	 * on directory or Amd might hang...
741310490Scy	 */
742310490Scy	if (mf->mf_server &&
743310490Scy	    (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID)
744310490Scy	  mf->mf_flags &= ~MFF_MKMNT;
745310490Scy	if (gopt.flags & CFM_UNMOUNT_ON_EXIT || mp->am_flags & AMF_AUTOFS) {
746310490Scy	  plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount);
747310490Scy	  /*
748310490Scy	   * use unmount_mp, not unmount_node, so that unmounts be
749310490Scy	   * backgrounded as needed.
750310490Scy	   */
751310490Scy	  unmount_mp((opaque_t) mp);
752310490Scy	} else {
753310490Scy	  am_unmounted(mp);
754310490Scy	}
755310490Scy	if (!(mf->mf_flags & (MFF_UNMOUNTING|MFF_MOUNTED)))
756310490Scy	  exported_ap[i] = NULL;
757174294Sobrien      } else {
758310490Scy	/*
759310490Scy	 * Any other node gets forcibly timed out.
760310490Scy	 */
761310490Scy	mp->am_flags &= ~AMF_NOTIMEOUT;
762310490Scy	mp->am_al->al_mnt->mf_flags &= ~MFF_RSTKEEP;
763310490Scy	mp->am_ttl = 0;
764310490Scy	mp->am_timeo = 1;
765310490Scy	mp->am_timeo_w = 0;
76638494Sobrien      }
76738494Sobrien    }
768310490Scy  } while (work_done);
76938494Sobrien}
77038494Sobrien
77138494Sobrien
772174294Sobrien/*
773174294Sobrien * Try to mount a file system.  Can be called directly or in a sub-process by run_task.
774174294Sobrien *
775174294Sobrien * Warning: this function might be running in a child process context.
776174294Sobrien * Don't expect any changes made here to survive in the parent amd process.
777174294Sobrien */
778174294Sobrienint
779174294Sobrienmount_node(opaque_t arg)
780174294Sobrien{
781174294Sobrien  am_node *mp = (am_node *) arg;
782310490Scy  mntfs *mf = mp->am_al->al_mnt;
783174294Sobrien  int error = 0;
784174294Sobrien
785174294Sobrien#ifdef HAVE_FS_AUTOFS
786174294Sobrien  if (mp->am_flags & AMF_AUTOFS)
787174294Sobrien    error = autofs_mount_fs(mp, mf);
788174294Sobrien  else
789174294Sobrien#endif /* HAVE_FS_AUTOFS */
790174294Sobrien    if (!(mf->mf_flags & MFF_MOUNTED))
791174294Sobrien      error = mf->mf_ops->mount_fs(mp, mf);
792174294Sobrien
793174294Sobrien  if (error > 0)
794174294Sobrien    dlog("mount_node: call to mf_ops->mount_fs(%s) failed: %s",
795174294Sobrien	 mp->am_path, strerror(error));
796174294Sobrien  return error;
797174294Sobrien}
798174294Sobrien
799174294Sobrien
80038494Sobrienstatic int
801174294Sobrienunmount_node(opaque_t arg)
80238494Sobrien{
803174294Sobrien  am_node *mp = (am_node *) arg;
804310490Scy  mntfs *mf = mp->am_al->al_mnt;
805174294Sobrien  int error = 0;
80638494Sobrien
807174294Sobrien  if (mf->mf_flags & MFF_ERROR) {
80838494Sobrien    /*
80938494Sobrien     * Just unlink
81038494Sobrien     */
811174294Sobrien    dlog("No-op unmount of error node %s", mf->mf_info);
81238494Sobrien  } else {
813174294Sobrien    dlog("Unmounting <%s> <%s> (%s) flags %x",
814174294Sobrien	 mp->am_path, mf->mf_mount, mf->mf_info, mf->mf_flags);
815174294Sobrien#ifdef HAVE_FS_AUTOFS
816174294Sobrien    if (mp->am_flags & AMF_AUTOFS)
817174294Sobrien      error = autofs_umount_fs(mp, mf);
818174294Sobrien    else
819174294Sobrien#endif /* HAVE_FS_AUTOFS */
820174294Sobrien      if (mf->mf_refc == 1)
821174294Sobrien	error = mf->mf_ops->umount_fs(mp, mf);
82238494Sobrien  }
82338494Sobrien
824174294Sobrien  /* do this again, it might have changed */
825310490Scy  mf = mp->am_al->al_mnt;
82638494Sobrien  if (error) {
82738494Sobrien    errno = error;		/* XXX */
82838494Sobrien    dlog("%s: unmount: %m", mf->mf_mount);
82938494Sobrien  }
83038494Sobrien
83138494Sobrien  return error;
83238494Sobrien}
83338494Sobrien
83438494Sobrien
83538494Sobrienstatic void
836174294Sobrienfree_map_if_success(int rc, int term, opaque_t arg)
83738494Sobrien{
838174294Sobrien  am_node *mp = (am_node *) arg;
839310490Scy  mntfs *mf = mp->am_al->al_mnt;
840174294Sobrien  wchan_t wchan = get_mntfs_wchan(mf);
84138494Sobrien
84238494Sobrien  /*
84338494Sobrien   * Not unmounting any more
84438494Sobrien   */
84538494Sobrien  mf->mf_flags &= ~MFF_UNMOUNTING;
84638494Sobrien
84738494Sobrien  /*
84842629Sobrien   * If a timeout was deferred because the underlying filesystem
84938494Sobrien   * was busy then arrange for a timeout as soon as possible.
85038494Sobrien   */
85138494Sobrien  if (mf->mf_flags & MFF_WANTTIMO) {
85238494Sobrien    mf->mf_flags &= ~MFF_WANTTIMO;
85338494Sobrien    reschedule_timeout_mp();
85438494Sobrien  }
85538494Sobrien  if (term) {
856310490Scy    notify_child(mp, AMQ_UMNT_SIGNAL, 0, term);
85738494Sobrien    plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
85838494Sobrien#if defined(DEBUG) && defined(SIGTRAP)
85938494Sobrien    /*
86038494Sobrien     * dbx likes to put a trap on exit().
86138494Sobrien     * Pretend it succeeded for now...
86238494Sobrien     */
86338494Sobrien    if (term == SIGTRAP) {
86438494Sobrien      am_unmounted(mp);
86538494Sobrien    }
86638494Sobrien#endif /* DEBUG */
867174294Sobrien#ifdef HAVE_FS_AUTOFS
868174294Sobrien    if (mp->am_flags & AMF_AUTOFS)
869174294Sobrien      autofs_umount_failed(mp);
870174294Sobrien#endif /* HAVE_FS_AUTOFS */
87138494Sobrien    amd_stats.d_uerr++;
87238494Sobrien  } else if (rc) {
873310490Scy    notify_child(mp, AMQ_UMNT_FAILED, rc, 0);
874174294Sobrien    if (mf->mf_ops == &amfs_program_ops || rc == EBUSY)
87538494Sobrien      plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
876174294Sobrien    else
877174294Sobrien      plog(XLOG_ERROR, "%s: unmount: %s", mp->am_path, strerror(rc));
878174294Sobrien#ifdef HAVE_FS_AUTOFS
879310490Scy    if (rc != ENOENT) {
880310490Scy      if (mf->mf_flags & MFF_IS_AUTOFS)
881310490Scy        autofs_get_mp(mp);
882310490Scy      if (mp->am_flags & AMF_AUTOFS)
883310490Scy        autofs_umount_failed(mp);
884310490Scy    }
885174294Sobrien#endif /* HAVE_FS_AUTOFS */
88638494Sobrien    amd_stats.d_uerr++;
88738494Sobrien  } else {
888310490Scy    /*
889310490Scy     * am_unmounted() will call notify_child() appropriately.
890310490Scy     */
89138494Sobrien    am_unmounted(mp);
89238494Sobrien  }
89338494Sobrien
89438494Sobrien  /*
895119679Smbr   * Wakeup anything waiting for this unmount
89638494Sobrien   */
897174294Sobrien  wakeup(wchan);
89838494Sobrien}
89938494Sobrien
90038494Sobrien
901174294Sobrienint
90238494Sobrienunmount_mp(am_node *mp)
90338494Sobrien{
90438494Sobrien  int was_backgrounded = 0;
905310490Scy  mntfs *mf = mp->am_al->al_mnt;
90638494Sobrien
907174294Sobrien#ifdef notdef
908174294Sobrien  plog(XLOG_INFO, "\"%s\" on %s timed out (flags 0x%x)",
909310490Scy       mp->am_path, mf->mf_mount, (int) mf->mf_flags);
910174294Sobrien#endif /* notdef */
911174294Sobrien
91282794Sobrien#ifndef MNT2_NFS_OPT_SYMTTL
91382794Sobrien    /*
91482794Sobrien     * This code is needed to defeat Solaris 2.4's (and newer) symlink
91582794Sobrien     * values cache.  It forces the last-modified time of the symlink to be
91682794Sobrien     * current.  It is not needed if the O/S has an nfs flag to turn off the
91782794Sobrien     * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez.
918119679Smbr     *
919119679Smbr     * Additionally, Linux currently ignores the nt_useconds field,
920174294Sobrien     * so we must update the nt_seconds field every time if clocktime(NULL)
921174294Sobrien     * didn't return a new number of seconds.
92282794Sobrien     */
923174294Sobrien  if (mp->am_parent) {
924174294Sobrien    time_t last = mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds;
925174294Sobrien    clocktime(&mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime);
92682794Sobrien    /* defensive programming... can't we assert the above condition? */
927174294Sobrien    if (last == (time_t) mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds)
928174294Sobrien      mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++;
929174294Sobrien  }
93082794Sobrien#endif /* not MNT2_NFS_OPT_SYMTTL */
93182794Sobrien
932174294Sobrien  if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) {
933174294Sobrien    /*
934174294Sobrien     * Don't try to unmount from a server that is known to be down
935174294Sobrien     */
936174294Sobrien    if (!(mf->mf_flags & MFF_LOGDOWN)) {
937174294Sobrien      /* Only log this once, otherwise gets a bit boring */
938174294Sobrien      plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
939174294Sobrien      mf->mf_flags |= MFF_LOGDOWN;
940174294Sobrien    }
941310490Scy    notify_child(mp, AMQ_UMNT_SERVER, 0, 0);
942174294Sobrien    return 0;
943174294Sobrien  }
94438494Sobrien
945310490Scy  dlog("\"%s\" on %s timed out", mp->am_path, mf->mf_mount);
946174294Sobrien  mf->mf_flags |= MFF_UNMOUNTING;
947174294Sobrien
948174294Sobrien#ifdef HAVE_FS_AUTOFS
949174294Sobrien  if (mf->mf_flags & MFF_IS_AUTOFS)
950174294Sobrien    autofs_release_mp(mp);
951174294Sobrien#endif /* HAVE_FS_AUTOFS */
952174294Sobrien
953174294Sobrien  if ((mf->mf_fsflags & FS_UBACKGROUND) &&
954310490Scy      (mf->mf_flags & MFF_MOUNTED) &&
955310490Scy     !(mf->mf_flags & MFF_ON_AUTOFS)) {
956174294Sobrien    dlog("Trying unmount in background");
957174294Sobrien    run_task(unmount_node, (opaque_t) mp,
958174294Sobrien	     free_map_if_success, (opaque_t) mp);
959174294Sobrien    was_backgrounded = 1;
96038494Sobrien  } else {
96138494Sobrien    dlog("Trying unmount in foreground");
962174294Sobrien    free_map_if_success(unmount_node((opaque_t) mp), 0, (opaque_t) mp);
96338494Sobrien    dlog("unmount attempt done");
96438494Sobrien  }
96538494Sobrien
96638494Sobrien  return was_backgrounded;
96738494Sobrien}
96838494Sobrien
96938494Sobrien
97038494Sobrienvoid
971174294Sobrientimeout_mp(opaque_t v)				/* argument not used?! */
97238494Sobrien{
97338494Sobrien  int i;
97438494Sobrien  time_t t = NEVER;
975174294Sobrien  time_t now = clocktime(NULL);
976174294Sobrien  int backoff = NumChildren / 4;
97738494Sobrien
97838494Sobrien  dlog("Timing out automount points...");
97938494Sobrien
98038494Sobrien  for (i = last_used_map; i >= 0; --i) {
98138494Sobrien    am_node *mp = exported_ap[i];
98238494Sobrien    mntfs *mf;
98338494Sobrien
98438494Sobrien    /*
985174294Sobrien     * Just continue if nothing mounted
98638494Sobrien     */
987174294Sobrien    if (!mp)
98838494Sobrien      continue;
98938494Sobrien
99038494Sobrien    /*
99138494Sobrien     * Pick up mounted filesystem
99238494Sobrien     */
993310490Scy    mf = mp->am_al->al_mnt;
99438494Sobrien    if (!mf)
99538494Sobrien      continue;
99638494Sobrien
997174294Sobrien#ifdef HAVE_FS_AUTOFS
998174294Sobrien    if (mf->mf_flags & MFF_IS_AUTOFS && mp->am_autofs_ttl != NEVER) {
999174294Sobrien      if (now >= mp->am_autofs_ttl)
1000174294Sobrien	autofs_timeout_mp(mp);
1001174294Sobrien      t = smallest_t(t, mp->am_autofs_ttl);
1002174294Sobrien    }
1003174294Sobrien#endif /* HAVE_FS_AUTOFS */
1004174294Sobrien
1005174294Sobrien    if (mp->am_flags & AMF_NOTIMEOUT)
1006174294Sobrien      continue;
1007174294Sobrien
100838494Sobrien    /*
100938494Sobrien     * Don't delete last reference to a restarted filesystem.
101038494Sobrien     */
101138494Sobrien    if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1)
101238494Sobrien      continue;
101338494Sobrien
101438494Sobrien    /*
101538494Sobrien     * If there is action on this filesystem then ignore it
101638494Sobrien     */
101738494Sobrien    if (!(mf->mf_flags & IGNORE_FLAGS)) {
101838494Sobrien      int expired = 0;
101938494Sobrien      mf->mf_flags &= ~MFF_WANTTIMO;
102038494Sobrien      if (now >= mp->am_ttl) {
102138494Sobrien	if (!backoff) {
102238494Sobrien	  expired = 1;
102338494Sobrien
102438494Sobrien	  /*
102538494Sobrien	   * Move the ttl forward to avoid thrashing effects
102638494Sobrien	   * on the next call to timeout!
102738494Sobrien	   */
102838494Sobrien	  /* sun's -tw option */
102938494Sobrien	  if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
103038494Sobrien	    mp->am_timeo_w += gopt.am_timeo_w;
103138494Sobrien	  mp->am_ttl = now + mp->am_timeo_w;
103238494Sobrien
103338494Sobrien	} else {
103438494Sobrien	  /*
103538494Sobrien	   * Just backoff this unmount for
103638494Sobrien	   * a couple of seconds to avoid
103738494Sobrien	   * many multiple unmounts being
103838494Sobrien	   * started in parallel.
103938494Sobrien	   */
104038494Sobrien	  mp->am_ttl = now + backoff + 1;
104138494Sobrien	}
104238494Sobrien      }
104338494Sobrien
104438494Sobrien      /*
104538494Sobrien       * If the next ttl is smallest, use that
104638494Sobrien       */
104738494Sobrien      t = smallest_t(t, mp->am_ttl);
104838494Sobrien
104938494Sobrien      if (!mp->am_child && mf->mf_error >= 0 && expired) {
105038494Sobrien	/*
105138494Sobrien	 * If the unmount was backgrounded then
105238494Sobrien	 * bump the backoff counter.
105338494Sobrien	 */
105438494Sobrien	if (unmount_mp(mp)) {
105538494Sobrien	  backoff = 2;
105638494Sobrien	}
105738494Sobrien      }
105838494Sobrien    } else if (mf->mf_flags & MFF_UNMOUNTING) {
105938494Sobrien      mf->mf_flags |= MFF_WANTTIMO;
106038494Sobrien    }
106138494Sobrien  }
106238494Sobrien
106338494Sobrien  if (t == NEVER) {
106438494Sobrien    dlog("No further timeouts");
106538494Sobrien    t = now + ONE_HOUR;
106638494Sobrien  }
106738494Sobrien
106838494Sobrien  /*
106938494Sobrien   * Sanity check to avoid runaways.
107038494Sobrien   * Absolutely should never get this but
107138494Sobrien   * if you do without this trap amd will thrash.
107238494Sobrien   */
107338494Sobrien  if (t <= now) {
107438494Sobrien    t = now + 6;		/* XXX */
107538494Sobrien    plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!");
107638494Sobrien  }
107738494Sobrien
107838494Sobrien  /*
107938494Sobrien   * XXX - when shutting down, make things happen faster
108038494Sobrien   */
108138494Sobrien  if ((int) amd_state >= (int) Finishing)
108238494Sobrien    t = now + 1;
108351292Sobrien  dlog("Next mount timeout in %lds", (long) (t - now));
108438494Sobrien
1085310490Scy  timeout_mp_id = timeout(t - now, timeout_mp, NULL);
108638494Sobrien}
108738494Sobrien
108838494Sobrien
108938494Sobrien/*
109038494Sobrien * Cause timeout_mp to be called soonest
109138494Sobrien */
109238494Sobrienvoid
109338494Sobrienreschedule_timeout_mp(void)
109438494Sobrien{
109538494Sobrien  if (timeout_mp_id)
109638494Sobrien    untimeout(timeout_mp_id);
1097310490Scy  timeout_mp_id = timeout(0, timeout_mp, NULL);
109838494Sobrien}
1099