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 *
get_first_exported_ap(int * index)114 get_first_exported_ap(int *index)
115 {
116 *index = -1;
117 return get_next_exported_ap(index);
118 }
119
120
121 am_node *
get_next_exported_ap(int * index)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 *
get_exported_ap(int index)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 *
path_to_exported_ap(char * path)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
exported_ap_realloc_map(int nsize)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 *
get_ap_child(am_node * mp,char * fname)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 *
exported_ap_alloc(void)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
exported_ap_free(am_node * mp)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
insert_am(am_node * mp,am_node * p_mp)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
remove_am(am_node * mp)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
new_ttl(am_node * mp)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
mk_fattr(nfsfattr * fattr,nfsftype vntype)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
init_map(am_node * mp,char * dir)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
free_map(am_node * mp)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 *
find_ap_recursive(char * dir,am_node * mp)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 *
find_ap(char * dir)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 *
find_mf(mntfs * mf)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 *
get_root_nfs_fh(char * dir)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 *
get_root_ap(char * dir)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
map_flush_srvr(fserver * fs)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
mount_auto_node(char * dir,opaque_t arg)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
mount_exported(void)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
make_root_node(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
umount_exported(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
mount_node(opaque_t arg)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
unmount_node(opaque_t arg)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
free_map_if_success(int rc,int term,opaque_t arg)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
unmount_mp(am_node * mp)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
timeout_mp(opaque_t v)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
reschedule_timeout_mp(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 }
1073