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