]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/amd/amd/map.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / amd / amd / map.c
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  */
68 static u_int am_gen = 2;        /* Initial generation number */
69 static int timeout_mp_id;       /* Id from last call to timeout */
70
71 static am_node *root_node;      /* The root of the mount tree */
72 static am_node **exported_ap = (am_node **) 0;
73 static int exported_ap_size = 0;
74 static int first_free_map = 0;  /* First available free slot */
75 static 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  */
85 static 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 */
104 static int unmount_node(opaque_t arg);
105 static void exported_ap_free(am_node *mp);
106 static void remove_am(am_node *mp);
107 static am_node *get_root_ap(char *dir);
108
109
110 /*
111  * Iterator functions for exported_ap[]
112  */
113 am_node *
114 get_first_exported_ap(int *index)
115 {
116   *index = -1;
117   return get_next_exported_ap(index);
118 }
119
120
121 am_node *
122 get_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  */
137 am_node *
138 get_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  */
149 am_node *
150 path_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  */
168 static int
169 exported_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
189 am_node *
190 get_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  */
234 am_node *
235 exported_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  */
271 static void
272 exported_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  */
310 void
311 insert_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  */
337 static void
338 remove_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  */
366 void
367 new_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
375 void
376 mk_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  */
404 void
405 init_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  */
440 void
441 free_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
475 static am_node *
476 find_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  */
501 am_node *
502 find_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  */
524 am_node *
525 find_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  */
544 am_nfs_fh *
545 get_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
563 static am_node *
564 get_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  */
579 void
580 map_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  */
604 int
605 mount_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  */
634 int
635 mount_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  */
647 void
648 make_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  */
687 void
688 umount_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  */
761 int
762 mount_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
783 static int
784 unmount_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
818 static void
819 free_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
877 int
878 unmount_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
944 void
945 timeout_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  */
1066 void
1067 reschedule_timeout_mp(void)
1068 {
1069   if (timeout_mp_id)
1070     untimeout(timeout_mp_id);
1071   timeout_mp_id = timeout(0, timeout_mp, 0);
1072 }