No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / am-utils / dist / amd / map.c
blob6bb588337fc29a2d13a57f868ecdcb85744dda31
1 /* $NetBSD$ */
3 /*
4 * Copyright (c) 1997-2009 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.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
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. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
42 * File: am-utils/amd/map.c
46 #ifdef HAVE_CONFIG_H
47 # include <config.h>
48 #endif /* HAVE_CONFIG_H */
49 #include <am_defs.h>
50 #include <amd.h>
52 #define smallest_t(t1, t2) (t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2)
53 #define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART)
54 #define new_gen() (am_gen++)
57 * Generation Numbers.
59 * Generation numbers are allocated to every node created
60 * by amd. When a filehandle is computed and sent to the
61 * kernel, the generation number makes sure that it is safe
62 * to reallocate a node slot even when the kernel has a cached
63 * reference to its old incarnation.
64 * No garbage collection is done, since it is assumed that
65 * there is no way that 2^32 generation numbers could ever
66 * be allocated by a single run of amd - there is simply
67 * not enough cpu time available.
68 * Famous last words... -Ion
70 static u_int am_gen = 2; /* Initial generation number */
71 static int timeout_mp_id; /* Id from last call to timeout */
73 static am_node *root_node; /* The root of the mount tree */
74 static am_node **exported_ap = (am_node **) NULL;
75 static int exported_ap_size = 0;
76 static int first_free_map = 0; /* First available free slot */
77 static int last_used_map = -1; /* Last unavailable used slot */
81 * This is the default attributes field which
82 * is copied into every new node to be created.
83 * The individual filesystem fs_init() routines
84 * patch the copy to represent the particular
85 * details for the relevant filesystem type
87 static nfsfattr gen_fattr =
89 NFLNK, /* type */
90 NFSMODE_LNK | 0777, /* mode */
91 1, /* nlink */
92 0, /* uid */
93 0, /* gid */
94 0, /* size */
95 4096, /* blocksize */
96 0, /* rdev */
97 1, /* blocks */
98 0, /* fsid */
99 0, /* fileid */
100 {0, 0}, /* atime */
101 {0, 0}, /* mtime */
102 {0, 0}, /* ctime */
105 /* forward declarations */
106 static int unmount_node(opaque_t arg);
107 static void exported_ap_free(am_node *mp);
108 static void remove_am(am_node *mp);
109 static am_node *get_root_ap(char *dir);
113 * Iterator functions for exported_ap[]
115 am_node *
116 get_first_exported_ap(int *index)
118 *index = -1;
119 return get_next_exported_ap(index);
123 am_node *
124 get_next_exported_ap(int *index)
126 (*index)++;
127 while (*index < exported_ap_size) {
128 if (exported_ap[*index] != NULL)
129 return exported_ap[*index];
130 (*index)++;
132 return NULL;
137 * Get exported_ap by index
139 am_node *
140 get_exported_ap(int index)
142 if (index < 0 || index >= exported_ap_size)
143 return 0;
144 return exported_ap[index];
149 * Get exported_ap by path
151 am_node *
152 path_to_exported_ap(char *path)
154 int index;
155 am_node *mp;
157 mp = get_first_exported_ap(&index);
158 while (mp != NULL) {
159 if (STREQ(mp->am_path, path))
160 break;
161 mp = get_next_exported_ap(&index);
163 return mp;
168 * Resize exported_ap map
170 static int
171 exported_ap_realloc_map(int nsize)
174 * this shouldn't happen, but...
176 if (nsize < 0 || nsize == exported_ap_size)
177 return 0;
179 exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node *));
181 if (nsize > exported_ap_size)
182 memset((char *) (exported_ap + exported_ap_size), 0,
183 (nsize - exported_ap_size) * sizeof(am_node *));
184 exported_ap_size = nsize;
186 return 1;
191 am_node *
192 get_ap_child(am_node *mp, char *fname)
194 am_node *new_mp;
195 mntfs *mf = mp->am_mnt;
198 * Allocate a new map
200 new_mp = exported_ap_alloc();
201 if (new_mp) {
203 * Fill it in
205 init_map(new_mp, fname);
208 * Put it in the table
210 insert_am(new_mp, mp);
213 * Fill in some other fields,
214 * path and mount point.
216 * bugfix: do not prepend old am_path if direct map
217 * <wls@astro.umd.edu> William Sebok
219 new_mp->am_path = str3cat(new_mp->am_path,
220 (mf->mf_fsflags & FS_DIRECT)
221 ? ""
222 : mp->am_path,
223 *fname == '/' ? "" : "/", fname);
224 dlog("setting path to %s", new_mp->am_path);
227 return new_mp;
231 * Allocate a new mount slot and create
232 * a new node.
233 * Fills in the map number of the node,
234 * but leaves everything else uninitialized.
236 am_node *
237 exported_ap_alloc(void)
239 am_node *mp, **mpp;
242 * First check if there are any slots left, realloc if needed
244 if (first_free_map >= exported_ap_size)
245 if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP))
246 return 0;
249 * Grab the next free slot
251 mpp = exported_ap + first_free_map;
252 mp = *mpp = ALLOC(struct am_node);
253 memset((char *) mp, 0, sizeof(struct am_node));
255 mp->am_mapno = first_free_map++;
258 * Update free pointer
260 while (first_free_map < exported_ap_size && exported_ap[first_free_map])
261 first_free_map++;
263 if (first_free_map > last_used_map)
264 last_used_map = first_free_map - 1;
266 return mp;
271 * Free a mount slot
273 static void
274 exported_ap_free(am_node *mp)
277 * Sanity check
279 if (!mp)
280 return;
283 * Zero the slot pointer to avoid double free's
285 exported_ap[mp->am_mapno] = NULL;
288 * Update the free and last_used indices
290 if (mp->am_mapno == last_used_map)
291 while (last_used_map >= 0 && exported_ap[last_used_map] == 0)
292 --last_used_map;
294 if (first_free_map > mp->am_mapno)
295 first_free_map = mp->am_mapno;
298 * Free the mount node, and zero out it's internal struct data.
300 memset((char *) mp, 0, sizeof(am_node));
301 XFREE(mp);
306 * Insert mp into the correct place,
307 * where p_mp is its parent node.
308 * A new node gets placed as the youngest sibling
309 * of any other children, and the parent's child
310 * pointer is adjusted to point to the new child node.
312 void
313 insert_am(am_node *mp, am_node *p_mp)
316 * If this is going in at the root then flag it
317 * so that it cannot be unmounted by amq.
319 if (p_mp == root_node)
320 mp->am_flags |= AMF_ROOT;
322 * Fill in n-way links
324 mp->am_parent = p_mp;
325 mp->am_osib = p_mp->am_child;
326 if (mp->am_osib)
327 mp->am_osib->am_ysib = mp;
328 p_mp->am_child = mp;
329 #ifdef HAVE_FS_AUTOFS
330 if (p_mp->am_mnt->mf_flags & MFF_IS_AUTOFS)
331 mp->am_flags |= AMF_AUTOFS;
332 #endif /* HAVE_FS_AUTOFS */
337 * Remove am from its place in the mount tree
339 static void
340 remove_am(am_node *mp)
343 * 1. Consistency check
345 if (mp->am_child && mp->am_parent) {
346 plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path);
350 * 2. Update parent's child pointer
352 if (mp->am_parent && mp->am_parent->am_child == mp)
353 mp->am_parent->am_child = mp->am_osib;
356 * 3. Unlink from sibling chain
358 if (mp->am_ysib)
359 mp->am_ysib->am_osib = mp->am_osib;
360 if (mp->am_osib)
361 mp->am_osib->am_ysib = mp->am_ysib;
366 * Compute a new time to live value for a node.
368 void
369 new_ttl(am_node *mp)
371 mp->am_timeo_w = 0;
372 mp->am_ttl = clocktime(&mp->am_fattr.na_atime);
373 mp->am_ttl += mp->am_timeo; /* sun's -tl option */
377 void
378 mk_fattr(nfsfattr *fattr, nfsftype vntype)
380 switch (vntype) {
381 case NFDIR:
382 fattr->na_type = NFDIR;
383 fattr->na_mode = NFSMODE_DIR | 0555;
384 fattr->na_nlink = 2;
385 fattr->na_size = 512;
386 break;
387 case NFLNK:
388 fattr->na_type = NFLNK;
389 fattr->na_mode = NFSMODE_LNK | 0777;
390 fattr->na_nlink = 1;
391 fattr->na_size = 0;
392 break;
393 default:
394 plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype);
395 break;
401 * Initialize an allocated mount node.
402 * It is assumed that the mount node was b-zero'd
403 * before getting here so anything that would
404 * be set to zero isn't done here.
406 void
407 init_map(am_node *mp, char *dir)
410 * mp->am_mapno is initialized by exported_ap_alloc
411 * other fields don't need to be set to zero.
413 mp->am_mnt = new_mntfs();
414 mp->am_mfarray = NULL;
415 mp->am_name = strdup(dir);
416 mp->am_path = strdup(dir);
417 mp->am_gen = new_gen();
418 #ifdef HAVE_FS_AUTOFS
419 mp->am_autofs_fh = NULL;
420 #endif /* HAVE_FS_AUTOFS */
422 mp->am_timeo = gopt.am_timeo;
423 mp->am_attr.ns_status = NFS_OK;
424 mp->am_fattr = gen_fattr;
425 mp->am_fattr.na_fsid = 42;
426 mp->am_fattr.na_fileid = mp->am_gen;
427 clocktime(&mp->am_fattr.na_atime);
428 /* next line copies a "struct nfstime" among several fields */
429 mp->am_fattr.na_mtime = mp->am_fattr.na_ctime = mp->am_fattr.na_atime;
431 new_ttl(mp);
432 mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds;
433 mp->am_dev = -1;
434 mp->am_rdev = -1;
435 mp->am_fd[0] = -1;
436 mp->am_fd[1] = -1;
440 void
441 notify_child(am_node *mp, au_etype au_etype, int au_errno, int au_signal)
443 amq_sync_umnt rv;
445 if (mp->am_fd[1] >= 0) { /* we have a child process */
446 rv.au_etype = au_etype;
447 rv.au_signal = au_signal;
448 rv.au_errno = au_errno;
450 write(mp->am_fd[1], &rv, sizeof(rv));
451 close(mp->am_fd[1]);
452 mp->am_fd[1] = -1;
458 * Free a mount node.
459 * The node must be already unmounted.
461 void
462 free_map(am_node *mp)
464 remove_am(mp);
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);
470 if (mp->am_link)
471 XFREE(mp->am_link);
472 if (mp->am_name)
473 XFREE(mp->am_name);
474 if (mp->am_path)
475 XFREE(mp->am_path);
476 if (mp->am_pref)
477 XFREE(mp->am_pref);
478 if (mp->am_transp)
479 XFREE(mp->am_transp);
481 if (mp->am_mnt)
482 free_mntfs(mp->am_mnt);
484 if (mp->am_mfarray) {
485 mntfs **temp_mf;
486 for (temp_mf = mp->am_mfarray; *temp_mf; temp_mf++)
487 free_mntfs(*temp_mf);
488 XFREE(mp->am_mfarray);
491 #ifdef HAVE_FS_AUTOFS
492 if (mp->am_autofs_fh)
493 autofs_release_fh(mp);
494 #endif /* HAVE_FS_AUTOFS */
496 exported_ap_free(mp);
500 static am_node *
501 find_ap_recursive(char *dir, am_node *mp)
503 if (mp) {
504 am_node *mp2;
505 if (STREQ(mp->am_path, dir))
506 return mp;
508 if ((mp->am_mnt->mf_flags & MFF_MOUNTED) &&
509 STREQ(mp->am_mnt->mf_mount, dir))
510 return mp;
512 mp2 = find_ap_recursive(dir, mp->am_osib);
513 if (mp2)
514 return mp2;
515 return find_ap_recursive(dir, mp->am_child);
518 return 0;
523 * Find the mount node corresponding to dir. dir can match either the
524 * automount path or, if the node is mounted, the mount location.
526 am_node *
527 find_ap(char *dir)
529 int i;
531 for (i = last_used_map; i >= 0; --i) {
532 am_node *mp = exported_ap[i];
533 if (mp && (mp->am_flags & AMF_ROOT)) {
534 mp = find_ap_recursive(dir, exported_ap[i]);
535 if (mp) {
536 return mp;
541 return 0;
546 * Find the mount node corresponding
547 * to the mntfs structure.
549 am_node *
550 find_mf(mntfs *mf)
552 int i;
554 for (i = last_used_map; i >= 0; --i) {
555 am_node *mp = exported_ap[i];
556 if (mp && mp->am_mnt == mf)
557 return mp;
560 return 0;
565 * Get the filehandle for a particular named directory.
566 * This is used during the bootstrap to tell the kernel
567 * the filehandles of the initial automount points.
569 am_nfs_fh *
570 get_root_nfs_fh(char *dir)
572 static am_nfs_fh nfh;
573 am_node *mp = get_root_ap(dir);
574 if (mp) {
575 mp_to_fh(mp, &nfh);
576 return &nfh;
580 * Should never get here...
582 plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
584 return 0;
588 static am_node *
589 get_root_ap(char *dir)
591 am_node *mp = find_ap(dir);
593 if (mp && mp->am_parent == root_node)
594 return mp;
596 return 0;
601 * Timeout all nodes waiting on
602 * a given Fserver.
604 void
605 map_flush_srvr(fserver *fs)
607 int i;
608 int done = 0;
610 for (i = last_used_map; i >= 0; --i) {
611 am_node *mp = exported_ap[i];
612 if (mp && mp->am_mnt && mp->am_mnt->mf_server == fs) {
613 plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host);
614 mp->am_ttl = clocktime(NULL);
615 done = 1;
618 if (done)
619 reschedule_timeout_mp();
624 * Mount a top level automount node
625 * by calling lookup in the parent
626 * (root) node which will cause the
627 * automount node to be automounted.
630 mount_auto_node(char *dir, opaque_t arg)
632 int error = 0;
633 am_node *mp = (am_node *) arg;
634 am_node *new_mp;
636 new_mp = mp->am_mnt->mf_ops->lookup_child(mp, dir, &error, VLOOK_CREATE);
637 if (new_mp && error < 0) {
639 * We can't allow the fileid of the root node to change.
640 * Should be ok to force it to 1, always.
642 new_mp->am_gen = new_mp->am_fattr.na_fileid = 1;
644 new_mp = mp->am_mnt->mf_ops->mount_child(new_mp, &error);
647 if (error > 0) {
648 errno = error; /* XXX */
649 plog(XLOG_ERROR, "Could not mount %s: %m", dir);
651 return error;
656 * Cause all the top-level mount nodes
657 * to be automounted
660 mount_exported(void)
663 * Iterate over all the nodes to be started
665 return root_keyiter(mount_auto_node, root_node);
670 * Construct top-level node
672 void
673 make_root_node(void)
675 mntfs *root_mnt;
676 char *rootmap = ROOT_MAP;
677 root_node = exported_ap_alloc();
680 * Allocate a new map
682 init_map(root_node, "");
685 * Allocate a new mounted filesystem
687 root_mnt = find_mntfs(&amfs_root_ops, (am_opts *) NULL, "", rootmap, "", "", "");
690 * Replace the initial null reference
692 free_mntfs(root_node->am_mnt);
693 root_node->am_mnt = root_mnt;
696 * Initialize the root
698 if (root_mnt->mf_ops->fs_init)
699 (*root_mnt->mf_ops->fs_init) (root_mnt);
702 * Mount the root
704 root_mnt->mf_error = root_mnt->mf_ops->mount_fs(root_node, root_mnt);
709 * Cause all the nodes to be unmounted by timing
710 * them out.
712 void
713 umount_exported(void)
715 int i;
717 for (i = last_used_map; i >= 0; --i) {
718 am_node *mp = exported_ap[i];
719 mntfs *mf;
721 if (!mp)
722 continue;
724 mf = mp->am_mnt;
725 if (mf->mf_flags & MFF_UNMOUNTING) {
727 * If this node is being unmounted then just ignore it. However,
728 * this could prevent amd from finishing if the unmount gets blocked
729 * since the am_node will never be free'd. am_unmounted needs
730 * telling about this possibility. - XXX
732 continue;
735 if (!(mf->mf_fsflags & FS_DIRECTORY))
737 * When shutting down this had better
738 * look like a directory, otherwise it
739 * can't be unmounted!
741 mk_fattr(&mp->am_fattr, NFDIR);
743 if ((--immediate_abort < 0 &&
744 !(mp->am_flags & AMF_ROOT) && mp->am_parent) ||
745 (mf->mf_flags & MFF_RESTART)) {
748 * Just throw this node away without bothering to unmount it. If
749 * the server is not known to be up then don't discard the mounted
750 * on directory or Amd might hang...
752 if (mf->mf_server &&
753 (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID)
754 mf->mf_flags &= ~MFF_MKMNT;
755 if (gopt.flags & CFM_UNMOUNT_ON_EXIT || mp->am_flags & AMF_AUTOFS) {
756 plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount);
758 * use unmount_mp, not unmount_node, so that unmounts be
759 * backgrounded as needed.
761 unmount_mp((opaque_t) mp);
762 } else {
763 am_unmounted(mp);
765 exported_ap[i] = NULL;
766 } else {
768 * Any other node gets forcibly timed out.
770 mp->am_flags &= ~AMF_NOTIMEOUT;
771 mp->am_mnt->mf_flags &= ~MFF_RSTKEEP;
772 mp->am_ttl = 0;
773 mp->am_timeo = 1;
774 mp->am_timeo_w = 0;
781 * Try to mount a file system. Can be called directly or in a sub-process by run_task.
783 * Warning: this function might be running in a child process context.
784 * Don't expect any changes made here to survive in the parent amd process.
787 mount_node(opaque_t arg)
789 am_node *mp = (am_node *) arg;
790 mntfs *mf = mp->am_mnt;
791 int error = 0;
793 #ifdef HAVE_FS_AUTOFS
794 if (mp->am_flags & AMF_AUTOFS)
795 error = autofs_mount_fs(mp, mf);
796 else
797 #endif /* HAVE_FS_AUTOFS */
798 if (!(mf->mf_flags & MFF_MOUNTED))
799 error = mf->mf_ops->mount_fs(mp, mf);
801 if (error > 0)
802 dlog("mount_node: call to mf_ops->mount_fs(%s) failed: %s",
803 mp->am_path, strerror(error));
804 return error;
808 static int
809 unmount_node(opaque_t arg)
811 am_node *mp = (am_node *) arg;
812 mntfs *mf = mp->am_mnt;
813 int error = 0;
815 if (mf->mf_flags & MFF_ERROR) {
817 * Just unlink
819 dlog("No-op unmount of error node %s", mf->mf_info);
820 } else {
821 dlog("Unmounting <%s> <%s> (%s) flags %x",
822 mp->am_path, mf->mf_mount, mf->mf_info, mf->mf_flags);
823 #ifdef HAVE_FS_AUTOFS
824 if (mp->am_flags & AMF_AUTOFS)
825 error = autofs_umount_fs(mp, mf);
826 else
827 #endif /* HAVE_FS_AUTOFS */
828 if (mf->mf_refc == 1)
829 error = mf->mf_ops->umount_fs(mp, mf);
832 /* do this again, it might have changed */
833 mf = mp->am_mnt;
834 if (error) {
835 errno = error; /* XXX */
836 dlog("%s: unmount: %m", mf->mf_mount);
839 return error;
843 static void
844 free_map_if_success(int rc, int term, opaque_t arg)
846 am_node *mp = (am_node *) arg;
847 mntfs *mf = mp->am_mnt;
848 wchan_t wchan = get_mntfs_wchan(mf);
851 * Not unmounting any more
853 mf->mf_flags &= ~MFF_UNMOUNTING;
856 * If a timeout was deferred because the underlying filesystem
857 * was busy then arrange for a timeout as soon as possible.
859 if (mf->mf_flags & MFF_WANTTIMO) {
860 mf->mf_flags &= ~MFF_WANTTIMO;
861 reschedule_timeout_mp();
863 if (term) {
864 notify_child(mp, AMQ_UMNT_SIGNAL, 0, term);
865 plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
866 #if defined(DEBUG) && defined(SIGTRAP)
868 * dbx likes to put a trap on exit().
869 * Pretend it succeeded for now...
871 if (term == SIGTRAP) {
872 am_unmounted(mp);
874 #endif /* DEBUG */
875 #ifdef HAVE_FS_AUTOFS
876 if (mp->am_flags & AMF_AUTOFS)
877 autofs_umount_failed(mp);
878 #endif /* HAVE_FS_AUTOFS */
879 amd_stats.d_uerr++;
880 } else if (rc) {
881 notify_child(mp, AMQ_UMNT_FAILED, rc, 0);
882 if (mf->mf_ops == &amfs_program_ops || rc == EBUSY)
883 plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
884 else
885 plog(XLOG_ERROR, "%s: unmount: %s", mp->am_path, strerror(rc));
886 #ifdef HAVE_FS_AUTOFS
887 if (mf->mf_flags & MFF_IS_AUTOFS)
888 autofs_get_mp(mp);
889 if (mp->am_flags & AMF_AUTOFS)
890 autofs_umount_failed(mp);
891 #endif /* HAVE_FS_AUTOFS */
892 amd_stats.d_uerr++;
893 } else {
895 * am_unmounted() will call notify_child() appropriately.
897 am_unmounted(mp);
901 * Wakeup anything waiting for this unmount
903 wakeup(wchan);
908 unmount_mp(am_node *mp)
910 int was_backgrounded = 0;
911 mntfs *mf = mp->am_mnt;
913 #ifdef notdef
914 plog(XLOG_INFO, "\"%s\" on %s timed out (flags 0x%x)",
915 mp->am_path, mp->am_mnt->mf_mount, (int) mf->mf_flags);
916 #endif /* notdef */
918 #ifndef MNT2_NFS_OPT_SYMTTL
920 * This code is needed to defeat Solaris 2.4's (and newer) symlink
921 * values cache. It forces the last-modified time of the symlink to be
922 * current. It is not needed if the O/S has an nfs flag to turn off the
923 * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez.
925 * Additionally, Linux currently ignores the nt_useconds field,
926 * so we must update the nt_seconds field every time if clocktime(NULL)
927 * didn't return a new number of seconds.
929 if (mp->am_parent) {
930 time_t last = mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds;
931 clocktime(&mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime);
932 /* defensive programming... can't we assert the above condition? */
933 if (last == (time_t) mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds)
934 mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++;
936 #endif /* not MNT2_NFS_OPT_SYMTTL */
938 if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) {
940 * Don't try to unmount from a server that is known to be down
942 if (!(mf->mf_flags & MFF_LOGDOWN)) {
943 /* Only log this once, otherwise gets a bit boring */
944 plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
945 mf->mf_flags |= MFF_LOGDOWN;
947 notify_child(mp, AMQ_UMNT_SERVER, 0, 0);
948 return 0;
951 dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
952 mf->mf_flags |= MFF_UNMOUNTING;
954 #ifdef HAVE_FS_AUTOFS
955 if (mf->mf_flags & MFF_IS_AUTOFS)
956 autofs_release_mp(mp);
957 #endif /* HAVE_FS_AUTOFS */
959 if ((mf->mf_fsflags & FS_UBACKGROUND) &&
960 (mf->mf_flags & MFF_MOUNTED)) {
961 dlog("Trying unmount in background");
962 run_task(unmount_node, (opaque_t) mp,
963 free_map_if_success, (opaque_t) mp);
964 was_backgrounded = 1;
965 } else {
966 dlog("Trying unmount in foreground");
967 free_map_if_success(unmount_node((opaque_t) mp), 0, (opaque_t) mp);
968 dlog("unmount attempt done");
971 return was_backgrounded;
975 void
976 timeout_mp(opaque_t v) /* argument not used?! */
978 int i;
979 time_t t = NEVER;
980 time_t now = clocktime(NULL);
981 int backoff = NumChildren / 4;
983 dlog("Timing out automount points...");
985 for (i = last_used_map; i >= 0; --i) {
986 am_node *mp = exported_ap[i];
987 mntfs *mf;
990 * Just continue if nothing mounted
992 if (!mp)
993 continue;
996 * Pick up mounted filesystem
998 mf = mp->am_mnt;
999 if (!mf)
1000 continue;
1002 #ifdef HAVE_FS_AUTOFS
1003 if (mf->mf_flags & MFF_IS_AUTOFS && mp->am_autofs_ttl != NEVER) {
1004 if (now >= mp->am_autofs_ttl)
1005 autofs_timeout_mp(mp);
1006 t = smallest_t(t, mp->am_autofs_ttl);
1008 #endif /* HAVE_FS_AUTOFS */
1010 if (mp->am_flags & AMF_NOTIMEOUT)
1011 continue;
1014 * Don't delete last reference to a restarted filesystem.
1016 if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1)
1017 continue;
1020 * If there is action on this filesystem then ignore it
1022 if (!(mf->mf_flags & IGNORE_FLAGS)) {
1023 int expired = 0;
1024 mf->mf_flags &= ~MFF_WANTTIMO;
1025 if (now >= mp->am_ttl) {
1026 if (!backoff) {
1027 expired = 1;
1030 * Move the ttl forward to avoid thrashing effects
1031 * on the next call to timeout!
1033 /* sun's -tw option */
1034 if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
1035 mp->am_timeo_w += gopt.am_timeo_w;
1036 mp->am_ttl = now + mp->am_timeo_w;
1038 } else {
1040 * Just backoff this unmount for
1041 * a couple of seconds to avoid
1042 * many multiple unmounts being
1043 * started in parallel.
1045 mp->am_ttl = now + backoff + 1;
1050 * If the next ttl is smallest, use that
1052 t = smallest_t(t, mp->am_ttl);
1054 if (!mp->am_child && mf->mf_error >= 0 && expired) {
1056 * If the unmount was backgrounded then
1057 * bump the backoff counter.
1059 if (unmount_mp(mp)) {
1060 backoff = 2;
1063 } else if (mf->mf_flags & MFF_UNMOUNTING) {
1064 mf->mf_flags |= MFF_WANTTIMO;
1068 if (t == NEVER) {
1069 dlog("No further timeouts");
1070 t = now + ONE_HOUR;
1074 * Sanity check to avoid runaways.
1075 * Absolutely should never get this but
1076 * if you do without this trap amd will thrash.
1078 if (t <= now) {
1079 t = now + 6; /* XXX */
1080 plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!");
1084 * XXX - when shutting down, make things happen faster
1086 if ((int) amd_state >= (int) Finishing)
1087 t = now + 1;
1088 dlog("Next mount timeout in %lds", (long) (t - now));
1090 timeout_mp_id = timeout(t - now, timeout_mp, NULL);
1095 * Cause timeout_mp to be called soonest
1097 void
1098 reschedule_timeout_mp(void)
1100 if (timeout_mp_id)
1101 untimeout(timeout_mp_id);
1102 timeout_mp_id = timeout(0, timeout_mp, NULL);