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