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