xref: /netbsd-src/external/bsd/am-utils/dist/amd/map.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
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  */
70 static u_int am_gen = 2;	/* Initial generation number */
71 static int timeout_mp_id;	/* Id from last call to timeout */
72 
73 static am_node *root_node;	/* The root of the mount tree */
74 static am_node **exported_ap = (am_node **) NULL;
75 static int exported_ap_size = 0;
76 static int first_free_map = 0;	/* First available free slot */
77 static 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  */
87 static 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 */
106 static int unmount_node(opaque_t arg);
107 static void exported_ap_free(am_node *mp);
108 static void remove_am(am_node *mp);
109 static am_node *get_root_ap(char *dir);
110 
111 
112 /*
113  * Iterator functions for exported_ap[]
114  */
115 am_node *
116 get_first_exported_ap(int *index)
117 {
118   *index = -1;
119   return get_next_exported_ap(index);
120 }
121 
122 
123 am_node *
124 get_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  */
139 am_node *
140 get_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  */
151 am_node *
152 path_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  */
170 static int
171 exported_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 
191 am_node *
192 get_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  */
236 am_node *
237 exported_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  */
273 static void
274 exported_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  */
312 void
313 insert_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  */
339 static void
340 remove_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  */
368 void
369 new_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 
377 void
378 mk_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  */
406 void
407 init_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 
440 void
441 notify_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  */
461 void
462 free_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 
500 static am_node *
501 find_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  */
526 am_node *
527 find_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  */
549 am_node *
550 find_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  */
569 am_nfs_fh *
570 get_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 
588 static am_node *
589 get_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  */
604 void
605 map_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  */
629 int
630 mount_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  */
659 int
660 mount_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  */
672 void
673 make_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  */
712 void
713 umount_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  */
786 int
787 mount_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 
808 static int
809 unmount_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 
843 static void
844 free_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 
907 int
908 unmount_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 
975 void
976 timeout_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  */
1097 void
1098 reschedule_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