xref: /original-bsd/usr.sbin/amd/amd/map.c (revision 4092c5cc)
1 /*-
2  * Copyright (c) 1990 Jan-Simon Pendry
3  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Jan-Simon Pendry at Imperial College, London.
9  *
10  * %sccs.include.redist.c%
11  *
12  * $Id: map.c,v 5.2.2.1 1992/02/09 15:08:36 jsp beta $
13  */
14 
15 #ifndef lint
16 static char sccsid[] = "@(#)map.c	8.1 (Berkeley) 06/06/93";
17 #endif /* not lint */
18 
19 #include "am.h"
20 
21 /*
22  * Generation Numbers.
23  *
24  * Generation numbers are allocated to every node created
25  * by amd.  When a filehandle is computed and sent to the
26  * kernel, the generation number makes sure that it is safe
27  * to reallocate a node slot even when the kernel has a cached
28  * reference to its old incarnation.
29  * No garbage collection is done, since it is assumed that
30  * there is no way that 2^32 generation numbers could ever
31  * be allocated by a single run of amd - there is simply
32  * not enough cpu time available.
33  */
34 static unsigned int am_gen = 2;	/* Initial generation number */
35 #define new_gen() (am_gen++)
36 
37 am_node **exported_ap = (am_node **) 0;
38 int exported_ap_size = 0;
39 int first_free_map = 0;		/* First available free slot */
40 int last_used_map = -1;		/* Last unavailable used slot */
41 static int timeout_mp_id;	/* Id from last call to timeout */
42 
43 /*
44  * This is the default attributes field which
45  * is copied into every new node to be created.
46  * The individual filesystem fs_init() routines
47  * patch the copy to represent the particular
48  * details for the relevant filesystem type
49  */
50 static struct fattr gen_fattr = {
51 	NFLNK,				/* type */
52 	NFSMODE_LNK | 0777,		/* mode */
53 	1,				/* nlink */
54 	0,				/* uid */
55 	0,				/* gid */
56 	0,				/* size */
57 	4096,				/* blocksize */
58 	0,				/* rdev */
59 	1,				/* blocks */
60 	0,				/* fsid */
61 	0,				/* fileid */
62 	{ 0, 0 },			/* atime */
63 	{ 0, 0 },			/* mtime */
64 	{ 0, 0 },			/* ctime */
65 };
66 
67 /*
68  * Resize exported_ap map
69  */
70 static int exported_ap_realloc_map P((int nsize));
exported_ap_realloc_map(nsize)71 static int exported_ap_realloc_map(nsize)
72 int nsize;
73 {
74 #ifdef notdef
75 	/*
76 	 * If a second realloc occasionally causes Amd to die
77 	 * in then include this check.
78 	 */
79 	if (exported_ap_size != 0)	/* XXX */
80 		return 0;
81 #endif
82 
83 	/*
84 	 * this shouldn't happen, but...
85 	 */
86 	if (nsize < 0 || nsize == exported_ap_size)
87 		return 0;
88 
89 	exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node*));
90 
91 	if (nsize > exported_ap_size)
92 		bzero((char*) (exported_ap+exported_ap_size),
93 			(nsize - exported_ap_size) * sizeof(am_node*));
94 	exported_ap_size = nsize;
95 
96 	return 1;
97 }
98 
99 
100 /*
101  * The root of the mount tree.
102  */
103 am_node *root_node;
104 
105 /*
106  * Allocate a new mount slot and create
107  * a new node.
108  * Fills in the map number of the node,
109  * but leaves everything else uninitialised.
110  */
exported_ap_alloc(P_void)111 am_node *exported_ap_alloc(P_void)
112 {
113 	am_node *mp, **mpp;
114 
115 	/*
116 	 * First check if there are any slots left, realloc if needed
117 	 */
118 	if (first_free_map >= exported_ap_size)
119 		if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP))
120 			return 0;
121 
122 	/*
123 	 * Grab the next free slot
124 	 */
125 	mpp = exported_ap + first_free_map;
126 	mp = *mpp = ALLOC(am_node);
127 	bzero((char *) mp, sizeof(*mp));
128 
129 	mp->am_mapno = first_free_map++;
130 
131 	/*
132 	 * Update free pointer
133 	 */
134 	while (first_free_map < exported_ap_size && exported_ap[first_free_map])
135 		first_free_map++;
136 
137 	if (first_free_map > last_used_map)
138 		last_used_map = first_free_map - 1;
139 
140 	/*
141 	 * Shrink exported_ap if reasonable
142 	 */
143 	if (last_used_map < exported_ap_size - (NEXP_AP + NEXP_AP_MARGIN))
144 		exported_ap_realloc_map(exported_ap_size - NEXP_AP);
145 
146 #ifdef DEBUG
147 	/*dlog("alloc_exp: last_used_map = %d, first_free_map = %d\n",
148 		last_used_map, first_free_map);*/
149 #endif /* DEBUG */
150 
151 	return mp;
152 }
153 
154 /*
155  * Free a mount slot
156  */
157 void exported_ap_free P((am_node *mp));
exported_ap_free(mp)158 void exported_ap_free(mp)
159 am_node *mp;
160 {
161 	/*
162 	 * Sanity check
163 	 */
164 	if (!mp)
165 		return;
166 
167 	/*
168 	 * Zero the slot pointer to avoid double free's
169 	 */
170 	exported_ap[mp->am_mapno] = 0;
171 
172 	/*
173 	 * Update the free and last_used indices
174 	 */
175 	if (mp->am_mapno == last_used_map)
176 		while (last_used_map >= 0 && exported_ap[last_used_map] == 0)
177 			--last_used_map;
178 
179 	if (first_free_map > mp->am_mapno)
180 		first_free_map = mp->am_mapno;
181 
182 #ifdef DEBUG
183 	/*dlog("free_exp: last_used_map = %d, first_free_map = %d\n",
184 		last_used_map, first_free_map);*/
185 #endif /* DEBUG */
186 
187 	/*
188 	 * Free the mount node
189 	 */
190 	free((voidp) mp);
191 }
192 
193 /*
194  * Insert mp into the correct place,
195  * where p_mp is its parent node.
196  * A new node gets placed as the youngest sibling
197  * of any other children, and the parent's child
198  * pointer is adjusted to point to the new child node.
199  */
insert_am(mp,p_mp)200 void insert_am(mp, p_mp)
201 am_node *mp;
202 am_node *p_mp;
203 {
204 	/*
205 	 * If this is going in at the root then flag it
206 	 * so that it cannot be unmounted by amq.
207 	 */
208 	if (p_mp == root_node)
209 		mp->am_flags |= AMF_ROOT;
210 	/*
211 	 * Fill in n-way links
212 	 */
213 	mp->am_parent = p_mp;
214 	mp->am_osib = p_mp->am_child;
215 	if (mp->am_osib)
216 		mp->am_osib->am_ysib = mp;
217 	p_mp->am_child = mp;
218 }
219 
220 /*
221  * Remove am from its place in the mount tree
222  */
remove_am(mp)223 void remove_am(mp)
224 am_node *mp;
225 {
226 	/*
227 	 * 1.  Consistency check
228 	 */
229 	if (mp->am_child && mp->am_parent) {
230 		plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path);
231 	}
232 
233 	/*
234 	 * 2.  Update parent's child pointer
235 	 */
236 	if (mp->am_parent && mp->am_parent->am_child == mp)
237 		mp->am_parent->am_child = mp->am_osib;
238 
239 	/*
240 	 * 3.  Unlink from sibling chain
241 	 */
242 	if (mp->am_ysib)
243 		mp->am_ysib->am_osib = mp->am_osib;
244 	if (mp->am_osib)
245 		mp->am_osib->am_ysib = mp->am_ysib;
246 }
247 
248 /*
249  * Compute a new time to live value for a node.
250  */
new_ttl(mp)251 void new_ttl(mp)
252 am_node *mp;
253 {
254 	mp->am_timeo_w = 0;
255 
256 	mp->am_ttl = clocktime();
257 	mp->am_fattr.atime.seconds = mp->am_ttl;
258 	mp->am_ttl += mp->am_timeo;	/* sun's -tl option */
259 }
260 
261 void mk_fattr P((am_node *mp, ftype vntype));
mk_fattr(mp,vntype)262 void mk_fattr(mp, vntype)
263 am_node *mp;
264 ftype vntype;
265 {
266 	switch (vntype) {
267 	case NFDIR:
268 		mp->am_fattr.type = NFDIR;
269 		mp->am_fattr.mode = NFSMODE_DIR | 0555;
270 		mp->am_fattr.nlink = 2;
271 		mp->am_fattr.size = 512;
272 		break;
273 	case NFLNK:
274 		mp->am_fattr.type = NFLNK;
275 		mp->am_fattr.mode = NFSMODE_LNK | 0777;
276 		mp->am_fattr.nlink = 1;
277 		mp->am_fattr.size = 0;
278 		break;
279 	default:
280 		plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype);
281 		break;
282 	}
283 }
284 
285 /*
286  * Initialise an allocated mount node.
287  * It is assumed that the mount node was bzero'd
288  * before getting here so anything that would
289  * be set to zero isn't done here.
290  */
init_map(mp,dir)291 void init_map(mp, dir)
292 am_node *mp;
293 char *dir;
294 {
295 	/* mp->am_mapno initalised by exported_ap_alloc */
296 	mp->am_mnt = new_mntfs();
297 	mp->am_name = strdup(dir);
298 	mp->am_path = strdup(dir);
299 	/*mp->am_link = 0;*/
300 	/*mp->am_parent = 0;*/
301 	/*mp->am_ysib = 0;*/
302 	/*mp->am_osib = 0;*/
303 	/*mp->am_child = 0;*/
304 	/*mp->am_flags = 0;*/
305 	/*mp->am_error = 0;*/
306 	mp->am_gen = new_gen();
307 	/*mp->am_pref = 0;*/
308 
309 	mp->am_timeo = am_timeo;
310 	mp->am_attr.status = NFS_OK;
311 	mp->am_fattr = gen_fattr;
312 	mp->am_fattr.fsid = 42;
313 	mp->am_fattr.fileid = 0;
314 	mp->am_fattr.atime.seconds = clocktime();
315 	mp->am_fattr.atime.useconds = 0;
316 	mp->am_fattr.mtime = mp->am_fattr.ctime = mp->am_fattr.atime;
317 
318 	new_ttl(mp);
319 	mp->am_stats.s_mtime = mp->am_fattr.atime.seconds;
320 	/*mp->am_private = 0;*/
321 }
322 
323 /*
324  * Free a mount node.
325  * The node must be already unmounted.
326  */
free_map(mp)327 void free_map(mp)
328 am_node *mp;
329 {
330 	remove_am(mp);
331 
332 	if (mp->am_link)
333 		free(mp->am_link);
334 	if (mp->am_name)
335 		free(mp->am_name);
336 	if (mp->am_path)
337 		free(mp->am_path);
338 	if (mp->am_pref)
339 		free(mp->am_pref);
340 
341 	if (mp->am_mnt)
342 		free_mntfs(mp->am_mnt);
343 
344 	exported_ap_free(mp);
345 }
346 
347 /*
348  * Convert from file handle to
349  * automount node.
350  */
fh_to_mp3(fhp,rp,c_or_d)351 am_node *fh_to_mp3(fhp, rp, c_or_d)
352 nfs_fh *fhp;
353 int *rp;
354 int c_or_d;
355 {
356 	struct am_fh *fp = (struct am_fh *) fhp;
357 	am_node *ap = 0;
358 
359 	/*
360 	 * Check process id matches
361 	 * If it doesn't then it is probably
362 	 * from an old kernel cached filehandle
363 	 * which is now out of date.
364 	 */
365 	if (fp->fhh_pid != mypid)
366 		goto drop;
367 
368 	/*
369 	 * Make sure the index is valid before
370 	 * exported_ap is referenced.
371 	 */
372 	if (fp->fhh_id < 0 || fp->fhh_id >= exported_ap_size)
373 		goto drop;
374 
375 	/*
376 	 * Get hold of the supposed mount node
377 	 */
378 	ap = exported_ap[fp->fhh_id];
379 
380 	/*
381 	 * If it exists then maybe...
382 	 */
383 	if (ap) {
384 		/*
385 		 * Check the generation number in the node
386 		 * matches the one from the kernel.  If not
387 		 * then the old node has been timed out and
388 		 * a new one allocated.
389 		 */
390 		if (ap->am_gen != fp->fhh_gen) {
391 			ap = 0;
392 			goto drop;
393 		}
394 
395 		/*
396 		 * If the node is hung then locate a new node
397 		 * for it.  This implements the replicated filesystem
398 		 * retries.
399 		 */
400 		if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) {
401 			int error;
402 			am_node *orig_ap = ap;
403 #ifdef DEBUG
404 			dlog("fh_to_mp3: %s (%s) is hung:- call lookup",
405 					orig_ap->am_path, orig_ap->am_mnt->mf_info);
406 #endif /* DEBUG */
407 			/*
408 			 * Update modify time of parent node.
409 			 * With any luck the kernel will re-stat
410 			 * the child node and get new information.
411 			 */
412 			orig_ap->am_fattr.mtime.seconds = clocktime();
413 
414 			/*
415 			 * Call the parent's lookup routine for an object
416 			 * with the same name.  This may return -1 in error
417 			 * if a mount is in progress.  In any case, if no
418 			 * mount node is returned the error code is propagated
419 			 * to the caller.
420 			 */
421 			if (c_or_d == VLOOK_CREATE) {
422 				ap = (*orig_ap->am_parent->am_mnt->mf_ops->lookuppn)(orig_ap->am_parent,
423 						orig_ap->am_name, &error, c_or_d);
424 			} else {
425 				ap = 0;
426 				error = ESTALE;
427 			}
428 			if (ap == 0) {
429 				if (error < 0 && amd_state == Finishing)
430 					error = ENOENT;
431 				*rp = error;
432 				return 0;
433 			}
434 			/*
435 			 * Update last access to original node.  This
436 			 * avoids timing it out and so sending ESTALE
437 			 * back to the kernel.
438 			 * XXX - Not sure we need this anymore (jsp, 90/10/6).
439 			 */
440 			new_ttl(orig_ap);
441 
442 		}
443 		/*
444 		 * Disallow references to objects being unmounted, unless
445 		 * they are automount points.
446 		 */
447 		if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) &&
448 				!(ap->am_flags & AMF_ROOT)) {
449 			if (amd_state == Finishing)
450 				*rp = ENOENT;
451 			else
452 				*rp = -1;
453 			return 0;
454 		}
455 		new_ttl(ap);
456 	}
457 
458 drop:
459 	if (!ap || !ap->am_mnt) {
460 		/*
461 		 * If we are shutting down then it is likely
462 		 * that this node has disappeared because of
463 		 * a fast timeout.  To avoid things thrashing
464 		 * just pretend it doesn't exist at all.  If
465 		 * ESTALE is returned, some NFS clients just
466 		 * keep retrying (stupid or what - if it's
467 		 * stale now, what's it going to be in 5 minutes?)
468 		 */
469 		if (amd_state == Finishing)
470 			*rp = ENOENT;
471 		else
472 			*rp = ESTALE;
473 		amd_stats.d_stale++;
474 	}
475 
476 	return ap;
477 }
478 
fh_to_mp(fhp)479 am_node *fh_to_mp(fhp)
480 nfs_fh *fhp;
481 {
482 	int dummy;
483 	return fh_to_mp2(fhp, &dummy);
484 }
485 
486 /*
487  * Convert from automount node to
488  * file handle.
489  */
mp_to_fh(mp,fhp)490 void mp_to_fh(mp, fhp)
491 am_node *mp;
492 struct nfs_fh *fhp;
493 {
494 	struct am_fh *fp = (struct am_fh *) fhp;
495 
496 	/*
497 	 * Take the process id
498 	 */
499 	fp->fhh_pid = mypid;
500 	/*
501 	 * .. the map number
502 	 */
503 	fp->fhh_id = mp->am_mapno;
504 	/*
505 	 * .. and the generation number
506 	 */
507 	fp->fhh_gen = mp->am_gen;
508 	/*
509 	 * .. to make a "unique" triple that will never
510 	 * be reallocated except across reboots (which doesn't matter)
511 	 * or if we are unlucky enough to be given the same
512 	 * pid as a previous amd (very unlikely).
513 	 */
514 }
515 
516 static am_node *find_ap2 P((char *dir, am_node *mp));
find_ap2(dir,mp)517 static am_node *find_ap2(dir, mp)
518 char *dir;
519 am_node *mp;
520 {
521 	if (mp) {
522 		am_node *mp2;
523 		if (strcmp(mp->am_path, dir) == 0)
524 			return mp;
525 
526 		if ((mp->am_mnt->mf_flags & MFF_MOUNTED) &&
527 			strcmp(mp->am_mnt->mf_mount, dir) == 0)
528 			return mp;
529 
530 		mp2 = find_ap2(dir, mp->am_osib);
531 		if (mp2)
532 			return mp2;
533 		return find_ap2(dir, mp->am_child);
534 	}
535 
536 	return 0;
537 }
538 
539 /*
540  * Find the mount node corresponding
541  * to dir.  dir can match either the
542  * automount path or, if the node is
543  * mounted, the mount location.
544  */
545 am_node *find_ap P((char *dir));
find_ap(dir)546 am_node *find_ap(dir)
547 char *dir;
548 {
549 	int i;
550 
551 	for (i = last_used_map; i >= 0; --i) {
552 		am_node *mp = exported_ap[i];
553 		if (mp && (mp->am_flags & AMF_ROOT)) {
554 			mp = find_ap2(dir, exported_ap[i]);
555 			if (mp)
556 				return mp;
557 		}
558 	}
559 
560 	return 0;
561 }
562 
563 /*
564  * Find the mount node corresponding
565  * to the mntfs structure.
566  */
567 am_node *find_mf P((mntfs *mf));
find_mf(mf)568 am_node *find_mf(mf)
569 mntfs *mf;
570 {
571 	int i;
572 
573 	for (i = last_used_map; i >= 0; --i) {
574 		am_node *mp = exported_ap[i];
575 		if (mp && mp->am_mnt == mf)
576 			return mp;
577 	}
578 	return 0;
579 }
580 
581 /*
582  * Get the filehandle for a particular named directory.
583  * This is used during the bootstrap to tell the kernel
584  * the filehandles of the initial automount points.
585  */
root_fh(dir)586 nfs_fh *root_fh(dir)
587 char *dir;
588 {
589 	static nfs_fh nfh;
590 	am_node *mp = root_ap(dir, TRUE);
591 	if (mp) {
592 		mp_to_fh(mp, &nfh);
593 		/*
594 		 * Patch up PID to match main server...
595 		 */
596 		if (!foreground) {
597 			long pid = getppid();
598 			((struct am_fh *) &nfh)->fhh_pid = pid;
599 #ifdef DEBUG
600 			dlog("root_fh substitutes pid %d", pid);
601 #endif
602 		}
603 		return &nfh;
604 	}
605 
606 	/*
607 	 * Should never get here...
608 	 */
609 	plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
610 	return 0;
611 }
612 
root_ap(dir,path)613 am_node *root_ap(dir, path)
614 char *dir;
615 int path;
616 {
617 	am_node *mp = find_ap(dir);
618 	if (mp && mp->am_parent == root_node)
619 		return mp;
620 
621 	return 0;
622 }
623 
624 /*
625  * Timeout all nodes waiting on
626  * a given Fserver.
627  */
628 void map_flush_srvr P((fserver *fs));
map_flush_srvr(fs)629 void map_flush_srvr(fs)
630 fserver *fs;
631 {
632 	int i;
633 	int done = 0;
634 
635 	for (i = last_used_map; i >= 0; --i) {
636 		am_node *mp = exported_ap[i];
637 		if (mp && mp->am_mnt && mp->am_mnt->mf_server == fs) {
638 			plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host);
639 			mp->am_ttl = clocktime();
640 			done = 1;
641 		}
642 	}
643 	if (done)
644 		reschedule_timeout_mp();
645 }
646 
647 /*
648  * Mount a top level automount node
649  * by calling lookup in the parent
650  * (root) node which will cause the
651  * automount node to be automounted.
652  */
653 int mount_auto_node P((char *dir, voidp arg));
mount_auto_node(dir,arg)654 int mount_auto_node(dir, arg)
655 char *dir;
656 voidp arg;
657 {
658 	int error = 0;
659 	(void) afs_ops.lookuppn((am_node *) arg, dir, &error, VLOOK_CREATE);
660 	if (error > 0) {
661 		errno = error; /* XXX */
662 		plog(XLOG_ERROR, "Could not mount %s: %m", dir);
663 	}
664 	return error;
665 }
666 
667 /*
668  * Cause all the top-level mount nodes
669  * to be automounted
670  */
671 int mount_exported P((void));
mount_exported()672 int mount_exported()
673 {
674 	/*
675 	 * Iterate over all the nodes to be started
676 	 */
677 	return root_keyiter((void (*)P((char*,void*))) mount_auto_node, root_node);
678 }
679 
680 /*
681  * Construct top-level node
682  */
683 void make_root_node P((void));
make_root_node()684 void make_root_node()
685 {
686 	mntfs *root_mnt;
687 	char *rootmap = ROOT_MAP;
688 	root_node = exported_ap_alloc();
689 
690 	/*
691 	 * Allocate a new map
692 	 */
693 	init_map(root_node, "");
694 	/*
695 	 * Allocate a new mounted filesystem
696 	 */
697 	root_mnt = find_mntfs(&root_ops, (am_opts *) 0, "", rootmap, "", "", "");
698 	/*
699 	 * Replace the initial null reference
700 	 */
701 	free_mntfs(root_node->am_mnt);
702 	root_node->am_mnt = root_mnt;
703 
704 	/*
705 	 * Initialise the root
706 	 */
707 	if (root_mnt->mf_ops->fs_init)
708 		(*root_mnt->mf_ops->fs_init)(root_mnt);
709 
710 	/*
711 	 * Mount the root
712 	 */
713 	root_mnt->mf_error = (*root_mnt->mf_ops->mount_fs)(root_node);
714 }
715 
716 /*
717  * Cause all the nodes to be unmounted by timing
718  * them out.
719  */
umount_exported(P_void)720 void umount_exported(P_void)
721 {
722 	int i;
723 	for (i = last_used_map; i >= 0; --i) {
724 		am_node *mp = exported_ap[i];
725 		if (mp) {
726 			mntfs *mf = mp->am_mnt;
727 			if (mf->mf_flags & MFF_UNMOUNTING) {
728 				/*
729 				 * If this node is being unmounted then
730 				 * just ignore it.  However, this could
731 				 * prevent amd from finishing if the
732 				 * unmount gets blocked since the am_node
733 				 * will never be free'd.  am_unmounted needs
734 				 * telling about this possibility. - XXX
735 				 */
736 				continue;
737 			}
738 			if (mf && !(mf->mf_ops->fs_flags & FS_DIRECTORY)) {
739 				/*
740 				 * When shutting down this had better
741 				 * look like a directory, otherwise it
742 				 * can't be unmounted!
743 				 */
744 				mk_fattr(mp, NFDIR);
745 			}
746 			if ((--immediate_abort < 0 && !(mp->am_flags & AMF_ROOT) && mp->am_parent) ||
747 			    (mf->mf_flags & MFF_RESTART)) {
748 				/*
749 				 * Just throw this node away without
750 				 * bothering to unmount it.  If the
751 				 * server is not known to be up then
752 				 * don't discard the mounted on directory
753 				 * or Amd might hang...
754 				 */
755 				if (mf->mf_server &&
756 					(mf->mf_server->fs_flags & (FSF_DOWN|FSF_VALID)) != FSF_VALID)
757 					mf->mf_flags &= ~MFF_MKMNT;
758 				am_unmounted(mp);
759 			} else {
760 				/*
761 				 * Any other node gets forcibly
762 				 * timed out
763 				 */
764 				mp->am_flags &= ~AMF_NOTIMEOUT;
765 				mp->am_mnt->mf_flags &= ~MFF_RSTKEEP;
766 				mp->am_ttl = 0;
767 				mp->am_timeo = 1;
768 				mp->am_timeo_w = 0;
769 			}
770 		}
771 	}
772 }
773 
774 static int unmount_node P((am_node *mp));
unmount_node(mp)775 static int unmount_node(mp)
776 am_node *mp;
777 {
778 	mntfs *mf = mp->am_mnt;
779 	int error;
780 
781 	if ((mf->mf_flags & MFF_ERROR) || mf->mf_refc > 1) {
782 		/*
783 		 * Just unlink
784 		 */
785 #ifdef DEBUG
786 		if (mf->mf_flags & MFF_ERROR)
787 			dlog("No-op unmount of error node %s", mf->mf_info);
788 #endif /* DEBUG */
789 		error = 0;
790 	} else {
791 #ifdef DEBUG
792 		dlog("Unmounting %s (%s)", mf->mf_mount, mf->mf_info);
793 #endif /* DEBUG */
794 		error = (*mf->mf_ops->umount_fs)(mp);
795 	}
796 
797 	if (error) {
798 #ifdef DEBUG
799 		errno = error; /* XXX */
800 		dlog("%s: unmount: %m", mf->mf_mount);
801 #endif /* DEBUG */
802 	}
803 
804 	return error;
805 }
806 
807 #ifdef FLUSH_KERNEL_NAME_CACHE
808 static void flush_kernel_name_cache P((am_node*));
flush_kernel_name_cache(mp)809 static void flush_kernel_name_cache(mp)
810 am_node *mp;
811 {
812 	int islink = (mp->am_mnt->mf_fattr.type == NFLNK);
813 	int isdir = (mp->am_mnt->mf_fattr.type == NFDIR);
814 	int elog = 0;
815 	if (islink) {
816 		if (unlink(mp->am_path) < 0)
817 			elog = 1;
818 	} else if (isdir) {
819 		if (rmdir(mp->am_path) < 0)
820 			elog = 1;
821 	}
822 	if (elog)
823 		plog(XLOG_WARNING, "failed to clear \"%s\" from dnlc: %m", mp->am_path);
824 }
825 #endif /* FLUSH_KERNEL_NAME_CACHE */
826 
827 static int unmount_node_wrap P((voidp vp));
unmount_node_wrap(vp)828 static int unmount_node_wrap(vp)
829 voidp vp;
830 {
831 #ifndef FLUSH_KERNEL_NAME_CACHE
832 	return unmount_node((am_node*) vp);
833 #else /* FLUSH_KERNEL_NAME_CACHE */
834 	/*
835 	 * This code should just say:
836 	 * return unmount_node((am_node *) vp);
837 	 *
838 	 * However...
839 	 * The kernel keeps a cached copy of filehandles,
840 	 * and doesn't ever uncache them (apparently).  So
841 	 * when Amd times out a node the kernel will have a
842 	 * stale filehandle.  When the kernel next uses the
843 	 * filehandle it gets ESTALE.
844 	 *
845 	 * The workaround:
846 	 * Arrange that when a node is removed an unlink or
847 	 * rmdir is done on that path so that the kernel
848 	 * cache is done.  Yes - yuck.
849 	 *
850 	 * This can all be removed (and the background
851 	 * unmount flag in sfs_ops) if/when the kernel does
852 	 * something smarter.
853 	 *
854 	 * If the unlink or rmdir failed then just log a warning,
855 	 * don't fail the unmount.  This can occur if the kernel
856 	 * client code decides that the object is still referenced
857 	 * and should be renamed rather than discarded.
858 	 *
859 	 * There is still a race condition here...
860 	 * if another process is trying to access the same
861 	 * filesystem at the time we get here, then
862 	 * it will block, since the MF_UNMOUNTING flag will
863 	 * be set.  That may, or may not, cause the entire
864 	 * system to deadlock.  Hmmm...
865 	 */
866 	am_node *mp = (am_node *) vp;
867 	int isauto = mp->am_parent && (mp->am_parent->am_mnt->mf_fattr.type == NFDIR);
868 	int error = unmount_node(mp);
869 	if (error)
870 		return error;
871 	if (isauto && (int)amd_state < (int)Finishing)
872 		flush_kernel_name_cache(mp);
873 
874 	return 0;
875 #endif /* FLUSH_KERNEL_NAME_CACHE */
876 }
877 
free_map_if_success(rc,term,closure)878 static void free_map_if_success(rc, term, closure)
879 int rc;
880 int term;
881 voidp closure;
882 {
883 	am_node *mp = (am_node *) closure;
884 	mntfs *mf = mp->am_mnt;
885 
886 	/*
887 	 * Not unmounting any more
888 	 */
889 	mf->mf_flags &= ~MFF_UNMOUNTING;
890 
891 	/*
892 	 * If a timeout was defered because the underlying filesystem
893 	 * was busy then arrange for a timeout as soon as possible.
894 	 */
895 	if (mf->mf_flags & MFF_WANTTIMO) {
896 		mf->mf_flags &= ~MFF_WANTTIMO;
897 		reschedule_timeout_mp();
898 	}
899 
900 	if (term) {
901 		plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
902 #if defined(DEBUG) && defined(SIGTRAP)
903 		/*
904 		 * dbx likes to put a trap on exit().
905 		 * Pretend it succeeded for now...
906 		 */
907 		if (term == SIGTRAP) {
908 			am_unmounted(mp);
909 		}
910 #endif /* DEBUG */
911 		amd_stats.d_uerr++;
912 	} else if (rc) {
913 		if (rc == EBUSY) {
914 			plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
915 		} else {
916 			errno = rc;	/* XXX */
917 			plog(XLOG_ERROR, "%s: unmount: %m", mp->am_path);
918 		}
919 		amd_stats.d_uerr++;
920 	} else {
921 		am_unmounted(mp);
922 	}
923 
924 	/*
925 	 * Wakeup anything waiting for this mount
926 	 */
927 	wakeup((voidp) mf);
928 }
929 
unmount_mp(mp)930 static int unmount_mp(mp)
931 am_node *mp;
932 {
933 	int was_backgrounded = 0;
934 	mntfs *mf = mp->am_mnt;
935 
936 #ifdef notdef
937 	plog(XLOG_INFO, "\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
938 #endif /* notdef */
939 
940 	if ((mf->mf_ops->fs_flags & FS_UBACKGROUND) &&
941 			(mf->mf_flags & MFF_MOUNTED)) {
942 		if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) {
943 			/*
944 			 * Don't try to unmount from a server that is known to be down
945 			 */
946 			if (!(mf->mf_flags & MFF_LOGDOWN)) {
947 				/* Only log this once, otherwise gets a bit boring */
948 				plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
949 				mf->mf_flags |= MFF_LOGDOWN;
950 			}
951 		} else {
952 			/* Clear logdown flag - since the server must be up */
953 			mf->mf_flags &= ~MFF_LOGDOWN;
954 #ifdef DEBUG
955 			dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
956 			/*dlog("Will background the unmount attempt");*/
957 #endif /* DEBUG */
958 			/*
959 			 * Note that we are unmounting this node
960 			 */
961 			mf->mf_flags |= MFF_UNMOUNTING;
962 			run_task(unmount_node_wrap, (voidp) mp,
963 				 free_map_if_success, (voidp) mp);
964 			was_backgrounded = 1;
965 #ifdef DEBUG
966 			dlog("unmount attempt backgrounded");
967 #endif /* DEBUG */
968 		}
969 	} else {
970 #ifdef DEBUG
971 		dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
972 		dlog("Trying unmount in foreground");
973 #endif
974 		mf->mf_flags |= MFF_UNMOUNTING;
975 		free_map_if_success(unmount_node(mp), 0, (voidp) mp);
976 #ifdef DEBUG
977 		dlog("unmount attempt done");
978 #endif /* DEBUG */
979 	}
980 
981 	return was_backgrounded;
982 }
983 
timeout_mp()984 void timeout_mp()
985 {
986 #define NEVER (time_t) 0
987 #define	smallest_t(t1, t2) \
988 	(t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2)
989 #define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART)
990 
991 	int i;
992 	time_t t = NEVER;
993 	time_t now = clocktime();
994 	int backoff = 0;
995 
996 #ifdef DEBUG
997 	dlog("Timing out automount points...");
998 #endif /* DEBUG */
999 	for (i = last_used_map; i >= 0; --i) {
1000 		am_node *mp = exported_ap[i];
1001 		mntfs *mf;
1002 		/*
1003 		 * Just continue if nothing mounted, or can't be timed out.
1004 		 */
1005 		if (!mp || (mp->am_flags & AMF_NOTIMEOUT))
1006 			continue;
1007 		/*
1008 		 * Pick up mounted filesystem
1009 		 */
1010 		mf = mp->am_mnt;
1011 		if (!mf)
1012 			continue;
1013 		/*
1014 		 * Don't delete last reference to a restarted filesystem.
1015 		 */
1016 		if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1)
1017 			continue;
1018 		/*
1019 		 * If there is action on this filesystem then ignore it
1020 		 */
1021 		if (!(mf->mf_flags & IGNORE_FLAGS)) {
1022 			int expired = 0;
1023 			mf->mf_flags &= ~MFF_WANTTIMO;
1024 #ifdef DEBUG
1025 			/*dlog("t is initially @%d, zero in %d secs", t, t - now);*/
1026 #endif /* DEBUG */
1027 			if (now >= mp->am_ttl) {
1028 				if (!backoff) {
1029 					expired = 1;
1030 					/*
1031 					 * Move the ttl forward to avoid thrashing effects
1032 					 * on the next call to timeout!
1033 					 */
1034 					/* sun's -tw option */
1035 					if (mp->am_timeo_w < 4 * am_timeo_w)
1036 						mp->am_timeo_w += am_timeo_w;
1037 					mp->am_ttl = now + mp->am_timeo_w;
1038 				} else {
1039 					/*
1040 					 * Just backoff this unmount for
1041 					 * a couple of seconds to avoid
1042 					 * many multiple unmounts being
1043 					 * started in parallel.
1044 					 */
1045 					mp->am_ttl = now + backoff + 1;
1046 				}
1047 			}
1048 			/*
1049 			 * If the next ttl is smallest, use that
1050 			 */
1051 			t = smallest_t(t, mp->am_ttl);
1052 
1053 #ifdef DEBUG
1054 			/*dlog("after ttl t is @%d, zero in %d secs", t, t - now);*/
1055 #endif /* DEBUG */
1056 
1057 			if (!mp->am_child && mf->mf_error >= 0 && expired) {
1058 				/*
1059 				 * If the unmount was backgrounded then
1060 				 * bump the backoff counter.
1061 				 */
1062 				if (unmount_mp(mp)) {
1063 					backoff = 2;
1064 #ifdef DEBUG
1065 					/*dlog("backing off subsequent unmounts by at least %d seconds", backoff);*/
1066 #endif
1067 				}
1068 			}
1069 		} else if (mf->mf_flags & MFF_UNMOUNTING) {
1070 			mf->mf_flags |= MFF_WANTTIMO;
1071 		}
1072 	}
1073 
1074 	if (t == NEVER) {
1075 #ifdef DEBUG
1076 		dlog("No further timeouts");
1077 #endif /* DEBUG */
1078 		t = now + ONE_HOUR;
1079 	}
1080 
1081 	/*
1082 	 * Sanity check to avoid runaways.
1083 	 * Absolutely should never get this but
1084 	 * if you do without this trap amd will thrash.
1085 	 */
1086 	if (t <= now) {
1087 		t = now + 6;	/* XXX */
1088 		plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!");
1089 	}
1090 	/*
1091 	 * XXX - when shutting down, make things happen faster
1092 	 */
1093 	if ((int)amd_state >= (int)Finishing)
1094 		t = now + 1;
1095 #ifdef DEBUG
1096 	dlog("Next mount timeout in %ds", t - now);
1097 #endif /* DEBUG */
1098 
1099 	timeout_mp_id = timeout(t - now, timeout_mp, 0);
1100 
1101 #undef NEVER
1102 #undef smallest_t
1103 #undef IGNORE_FLAGS
1104 }
1105 
1106 /*
1107  * Cause timeout_mp to be called soonest
1108  */
reschedule_timeout_mp()1109 void reschedule_timeout_mp()
1110 {
1111 	if (timeout_mp_id)
1112 		untimeout(timeout_mp_id);
1113 	timeout_mp_id = timeout(0, timeout_mp, 0);
1114 }
1115