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