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