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