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