xref: /original-bsd/usr.sbin/amd/amd/afs_ops.c (revision 50dd0bba)
1 /*
2  * $Id: afs_ops.c,v 5.2.1.6 91/03/17 17:49:10 jsp Alpha $
3  *
4  * Copyright (c) 1990 Jan-Simon Pendry
5  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
6  * Copyright (c) 1990 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Jan-Simon Pendry at Imperial College, London.
11  *
12  * %sccs.include.redist.c%
13  *
14  *	@(#)afs_ops.c	5.2 (Berkeley) 03/17/91
15  */
16 
17 #include "am.h"
18 
19 #define NFS
20 #define NFSCLIENT
21 
22 #include <sys/stat.h>
23 #ifdef NFS_3
24 typedef nfs_fh fhandle_t;
25 #endif /* NFS_3 */
26 #ifdef NFS_HDR
27 #include NFS_HDR
28 #endif /* NFS_HDR */
29 #include <sys/mount.h>
30 #include "mount.h"
31 
32 /*
33  * Automount file system
34  * Direct file system
35  * Root file system
36  * Top-level file system
37  */
38 
39 /*
40  * Interval between forced retries of a mount.
41  */
42 #define RETRY_INTERVAL	2
43 
44 /*
45  * AFS needs nothing in particular.
46  */
47 static char *afs_match P((am_opts *fo));
48 static char *afs_match(fo)
49 am_opts *fo;
50 {
51 	char *p = fo->opt_rfs;
52 	if (!fo->opt_rfs) {
53 		plog(XLOG_USER, "auto: no mount point named (rfs:=)");
54 		return 0;
55 	}
56 	if (!fo->opt_fs) {
57 		plog(XLOG_USER, "auto: no map named (fs:=)");
58 		return 0;
59 	}
60 	/*
61 	 * Swap round fs:= and rfs:= options
62 	 * ... historical (jsp)
63 	 */
64 	fo->opt_rfs = fo->opt_fs;
65 	fo->opt_fs = p;
66 	/*
67 	 * mtab entry turns out to be the name of the mount map
68 	 */
69 	return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
70 }
71 
72 /*
73  * Mount an automounter directory.
74  * The automounter is connected into the system
75  * as a user-level NFS server.  mount_toplvl constructs
76  * the necessary NFS parameters to be given to the
77  * kernel so that it will talk back to us.
78  */
79 static int mount_toplvl P((char *dir, char *opts));
80 static int mount_toplvl(dir, opts)
81 char *dir;
82 char *opts;
83 {
84 	struct nfs_args nfs_args;
85 	struct mntent mnt;
86 	int retry;
87 	struct sockaddr_in sin;
88 	unsigned short port;
89 	int flags;
90 	extern nfs_fh *root_fh();
91 	nfs_fh *fhp;
92 	char fs_hostname[MAXHOSTNAMELEN+MAXPATHLEN+1];
93 
94 	MTYPE_TYPE type = MOUNT_TYPE_NFS;
95 
96 	bzero((voidp) &nfs_args, sizeof(nfs_args));	/* Paranoid */
97 
98 	mnt.mnt_dir = dir;
99 	mnt.mnt_fsname = pid_fsname;
100 	mnt.mnt_type = MNTTYPE_AUTO;
101 	mnt.mnt_opts = opts;
102 	mnt.mnt_freq = 0;
103 	mnt.mnt_passno = 0;
104 
105 	retry = hasmntval(&mnt, "retry");
106 	if (retry <= 0)
107 		retry = 2;	/* XXX */
108 
109 	/*
110 	 * get fhandle of remote path for automount point
111 	 */
112 
113 	fhp = root_fh(dir);
114 	if (!fhp) {
115 		plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
116 		return EINVAL;
117 	}
118 
119 	NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) fhp);
120 
121 	/*
122 	 * Create sockaddr to point to the local machine.  127.0.0.1
123 	 * is not used since that will not work in HP-UX clusters and
124 	 * this is no more expensive.
125 	 */
126 	bzero((voidp) &sin, sizeof(sin));
127 	sin.sin_family = AF_INET;
128 	sin.sin_addr = myipaddr;
129 	if (port = hasmntval(&mnt, "port")) {
130 		sin.sin_port = htons(port);
131 	} else {
132 		plog(XLOG_ERROR, "no port number specified for %s", dir);
133 		return EINVAL;
134 	}
135 
136 	/*
137 	 * set mount args
138 	 */
139 	NFS_SA_DREF(nfs_args, &sin);
140 
141 	/*
142 	 * Make a ``hostname'' string for the kernel
143 	 */
144 #ifndef HOSTNAMESZ
145 #define	SHORT_MOUNT_NAME
146 #endif /* HOSTNAMESZ */
147 #ifdef SHORT_MOUNT_NAME
148 	sprintf(fs_hostname, "amd:%d", foreground ? mypid : getppid());
149 #else
150 	sprintf(fs_hostname, "pid%d@%s:%s", foreground ? mypid : getppid(), hostname, dir);
151 #endif /* SHORT_MOUNT_NAME */
152 	nfs_args.hostname = fs_hostname;
153 	nfs_args.flags |= NFSMNT_HOSTNAME;
154 #ifdef HOSTNAMESZ
155 	/*
156 	 * Most kernels have a name length restriction.
157 	 */
158 	if (strlen(fs_hostname) >= HOSTNAMESZ)
159 		strcpy(fs_hostname + HOSTNAMESZ - 3, "..");
160 #endif /* HOSTNAMESZ */
161 
162 	/*
163 	 * Parse a subset of the standard nfs options.  The
164 	 * others are probably irrelevant for this application
165 	 */
166 	if (nfs_args.timeo = hasmntval(&mnt, "timeo"))
167 		nfs_args.flags |= NFSMNT_TIMEO;
168 
169 	if (nfs_args.retrans = hasmntval(&mnt, "retrans"))
170 		nfs_args.flags |= NFSMNT_RETRANS;
171 
172 #ifdef NFSMNT_BIODS
173 	if (nfs_args.biods = hasmntval(&mnt, "biods"))
174 		nfs_args.flags |= NFSMNT_BIODS;
175 
176 #endif /* NFSMNT_BIODS */
177 
178 #if defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX)
179 	/*
180 	 * Don't cache attributes - they are changing under
181 	 * the kernel's feet...
182 	 */
183 	nfs_args.acregmin = nfs_args.acregmax = 1;
184 	nfs_args.flags |= NFSMNT_ACREGMIN|NFSMNT_ACREGMAX;
185 #endif /* defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX) */
186 	/*
187 	 * These two are constructed internally by the calling routine
188 	 */
189 	if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL)
190 		nfs_args.flags |= NFSMNT_SOFT;
191 
192 #ifdef MNTOPT_INTR
193 	if (hasmntopt(&mnt, MNTOPT_INTR) != NULL)
194 		nfs_args.flags |= NFSMNT_INT;
195 #endif /* MNTOPT_INTR */
196 
197 	flags = compute_mount_flags(&mnt);
198 #ifdef ULTRIX_HACK
199 	nfs_args.gfs_flags = flags;
200 	flags &= M_RDONLY;
201 	if (flags & M_RDONLY)
202 		nfs_args.flags |= NFSMNT_RONLY;
203 #endif /* ULTRIX_HACK */
204 	return mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
205 }
206 
207 static void afs_mkcacheref P((mntfs *mf));
208 static void afs_mkcacheref(mf)
209 mntfs *mf;
210 {
211 	/*
212 	 * Build a new map cache for this node, or re-use
213 	 * an existing cache for the same map.
214 	 */
215 	char *cache;
216 	if (mf->mf_fo && mf->mf_fo->opt_cache)
217 	  	cache = mf->mf_fo->opt_cache;
218 	else
219 	  	cache = "none";
220 	mf->mf_private = (voidp) mapc_find(mf->mf_info, cache);
221 	mf->mf_prfree = mapc_free;
222 }
223 
224 /*
225  * Mount the root...
226  */
227 static int root_mount P((am_node *mp));
228 static int root_mount(mp)
229 am_node *mp;
230 {
231 	mntfs *mf = mp->am_mnt;
232 #ifdef notdef
233 	/*
234 	 * Make sure fattr is set up correctly
235 	 */
236 	mk_fattr(mp, NFDIR);
237 #endif
238 
239 	mf->mf_mount = strealloc(mf->mf_mount, pid_fsname);
240 	mf->mf_private = (voidp) mapc_find(mf->mf_info, "");
241 	mf->mf_prfree = mapc_free;
242 
243 	return 0;
244 }
245 
246 /*
247  * Mount a sub-mount
248  */
249 static int afs_mount P((am_node *mp));
250 static int afs_mount(mp)
251 am_node *mp;
252 {
253 	mntfs *mf = mp->am_mnt;
254 
255 #ifdef notdef
256 	/*
257 	 * Make sure fattr is set up correctly
258 	 */
259 	mk_fattr(mp, NFDIR);
260 #endif
261 
262 	/*
263 	 * Pseudo-directories are used to provide some structure
264 	 * to the automounted directories instead
265 	 * of putting them all in the top-level automount directory.
266 	 *
267 	 * Here, just increment the parent's link count.
268 	 */
269 	mp->am_parent->am_fattr.nlink++;
270 	/*
271 	 * Info field of . means use parent's info field.
272 	 * Historical - not documented.
273 	 */
274 	if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
275 		mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
276 	/*
277 	 * Compute prefix:
278 	 *
279 	 * If there is an option prefix then use that else
280 	 * If the parent had a prefix then use that with name
281 	 *	of this node appended else
282 	 * Use the name of this node.
283 	 *
284 	 * That means if you want no prefix you must say so
285 	 * in the map.
286 	 */
287 	if (mf->mf_fo->opt_pref) {
288 		/*
289 		 * the prefix specified as an option
290 		 */
291 		mp->am_pref = strdup(mf->mf_fo->opt_pref);
292 	} else {
293 		/*
294 		 * else the parent's prefix
295 		 * followed by the name
296 		 * followed by /
297 		 */
298 		char *ppref = mp->am_parent->am_pref;
299 		if (ppref == 0)
300 			ppref = "";
301 		mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
302 	}
303 
304 	/*
305 	 * Attach a map cache
306 	 */
307 	afs_mkcacheref(mf);
308 
309 	return 0;
310 }
311 
312 /*
313  * Mount the top-level
314  */
315 static int toplvl_mount P((am_node *mp));
316 static int toplvl_mount(mp)
317 am_node *mp;
318 {
319 	mntfs *mf = mp->am_mnt;
320 	struct stat stb;
321 	char opts[256];
322 	int error;
323 	char *mnttype;
324 
325 	/*
326 	 * Mounting the automounter.
327 	 * Make sure the mount directory exists, construct
328 	 * the mount options and call the mount_toplvl routine.
329 	 */
330 
331 	if (stat(mp->am_path, &stb) < 0) {
332 		return errno;
333 	} else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
334 		plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
335 		return ENOTDIR;
336 	}
337 
338 	if (mf->mf_ops == &toplvl_ops) mnttype = "indirect";
339 	else if (mf->mf_ops == &dfs_ops) mnttype = "direct";
340 #ifdef HAS_UNION_FS
341 	else if (mf->mf_ops == &union_ops) mnttype = "union";
342 #endif
343 	else mnttype = "auto";
344 
345 	/*
346 	 * Construct some mount options
347 	 */
348 	sprintf(opts,
349 #ifdef MNTOPT_INTR
350 		"%s,%s,%s=%d,%s=%d,%s=%d,%s",
351 		MNTOPT_INTR,
352 #else
353 		"%s,%s=%d,%s=%d,%s=%d,%s",
354 #endif /* MNTOPT_INTR */
355 #ifdef AUTOMOUNT_RO
356 		MNTOPT_RO,	/* You don't really want this... */
357 #else
358 		"rw",
359 #endif /* AUTOMOUNT_RO */
360 		"port", nfs_port,
361 		"timeo", afs_timeo,
362 		"retrans", afs_retrans,
363 		mnttype);
364 
365 	error = mount_toplvl(mf->mf_mount, opts);
366 	if (error) {
367 		errno = error;
368 		plog(XLOG_FATAL, "mount_toplvl: %m");
369 		return error;
370 	}
371 
372 	return 0;
373 }
374 
375 static void toplvl_mounted P((mntfs *mf));
376 static void toplvl_mounted(mf)
377 mntfs *mf;
378 {
379 	afs_mkcacheref(mf);
380 }
381 
382 #ifdef HAS_UNION_FS
383 /*
384  * Create a reference to a union'ed entry
385  */
386 static int create_union_node P((char *dir, voidp arg));
387 static int create_union_node(dir, arg)
388 char *dir;
389 voidp arg;
390 {
391 	if (strcmp(dir, "/defaults") != 0) {
392 		int error = 0;
393 		(void) toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE);
394 		if (error > 0) {
395 			errno = error; /* XXX */
396 			plog(XLOG_ERROR, "Could not mount %s: %m", dir);
397 		}
398 		return error;
399 	}
400 	return 0;
401 }
402 
403 static void union_mounted P((mntfs *mf));
404 static void union_mounted(mf)
405 mntfs *mf;
406 {
407 	int i;
408 
409 	afs_mkcacheref(mf);
410 
411 	/*
412 	 * Having made the union mount point,
413 	 * populate all the entries...
414 	 */
415 	for (i = 0; i <= last_used_map; i++) {
416 		am_node *mp = exported_ap[i];
417 		if (mp && mp->am_mnt == mf) {
418 			/* return value from create_union_node is ignored by mapc_keyiter */
419 			(void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private,
420 				(void (*)P((char*,void*))) create_union_node, mp);
421 			break;
422 		}
423 	}
424 
425 #ifdef notdef
426 	/*
427 	 * would be nice to flush most of the cache, but we need to
428 	 * keep the wildcard and /defaults entries...
429 	 */
430 	mapc_free(mf->mf_private);
431 	mf->mf_private = (voidp) mapc_find(mf->mf_info, "inc");
432 /*	mapc_add_kv(mf->mf_private, strdup("/defaults"),
433 		strdup("type:=link;opts:=nounmount;sublink:=${key}")); */
434 #endif
435 }
436 #endif /* HAS_UNION_FS */
437 
438 /*
439  * Unmount an automount sub-node
440  */
441 static int afs_umount P((am_node *mp));
442 static int afs_umount(mp)
443 am_node *mp;
444 {
445 	return 0;
446 }
447 
448 /*
449  * Unmount a top-level automount node
450  */
451 static int toplvl_umount P((am_node *mp));
452 static int toplvl_umount(mp)
453 am_node *mp;
454 {
455 	int error;
456 
457 	struct stat stb;
458 again:
459 	/*
460 	 * The lstat is needed if this mount is type=direct.
461 	 * When that happens, the kernel cache gets confused
462 	 * between the underlying type (dir) and the mounted
463 	 * type (link) and so needs to be re-synced before
464 	 * the unmount.  This is all because the unmount system
465 	 * call follows links and so can't actually unmount
466 	 * a link (stupid!).  It was noted that doing an ls -ld
467 	 * of the mount point to see why things were not working
468 	 * actually fixed the problem - so simulate an ls -ld here.
469 	 */
470 	if (lstat(mp->am_path, &stb) < 0) {
471 #ifdef DEBUG
472 		dlog("lstat(%s): %m", mp->am_path);
473 #endif /* DEBUG */
474 	}
475 	error = UMOUNT_FS(mp->am_path);
476 	if (error == EBUSY) {
477 		plog(XLOG_WARNING, "afs_unmount retrying %s in 1s", mp->am_path);
478 		sleep(1);	/* XXX */
479 		goto again;
480 	}
481 
482 	return error;
483 }
484 
485 /*
486  * Unmount an automount node
487  */
488 static void afs_umounted P((am_node *mp));
489 static void afs_umounted(mp)
490 am_node *mp;
491 {
492 	/*
493 	 * If this is a pseudo-directory then just adjust the link count
494 	 * in the parent, otherwise call the generic unmount routine
495 	 */
496 	if (mp->am_parent && mp->am_parent->am_parent)
497 		--mp->am_parent->am_fattr.nlink;
498 }
499 
500 /*
501  * Mounting a file system may take a significant period of time.  The
502  * problem is that if this is done in the main process thread then
503  * the entire automounter could be blocked, possibly hanging lots of
504  * processes on the system.  Instead we use a continuation scheme to
505  * allow mounts to be attempted in a sub-process.  When the sub-process
506  * exits we pick up the exit status (by convention a UN*X error number)
507  * and continue in a notifier.  The notifier gets handed a data structure
508  * and can then determine whether the mount was successful or not.  If
509  * not, it updates the data structure and tries again until there are no
510  * more ways to try the mount, or some other permanent error occurs.
511  * In the mean time no RPC reply is sent, even after the mount is succesful.
512  * We rely on the RPC retry mechanism to resend the lookup request which
513  * can then be handled.
514  */
515 
516 
517 struct continuation {
518 	char **ivec;		/* Current mount info */
519 	am_node *mp;		/* Node we are trying to mount */
520 	char *key;		/* Map key */
521 	char *info;		/* Info string */
522 	char **xivec;		/* Saved strsplit vector */
523 	char *auto_opts;	/* Automount options */
524 	am_opts fs_opts;	/* Filesystem options */
525 	char *def_opts;		/* Default automount options */
526 	int retry;		/* Try again? */
527 	int tried;		/* Have we tried any yet? */
528 	time_t start;		/* Time we started this mount */
529 	int callout;		/* Callout identifier */
530 };
531 
532 /*
533  * Discard an old continuation
534  */
535 static void free_continuation P((struct continuation *cp));
536 static void free_continuation(cp)
537 struct continuation *cp;
538 {
539 	if (cp->callout)
540 		untimeout(cp->callout);
541 	free((voidp) cp->key);
542 	free((voidp) cp->xivec);
543 	free((voidp) cp->info);
544 	free((voidp) cp->auto_opts);
545 	free((voidp) cp->def_opts);
546 	free_opts(&cp->fs_opts);
547 	free((voidp) cp);
548 }
549 
550 static int afs_bgmount P((struct continuation*, int));
551 
552 /*
553  * Discard the underlying mount point and replace
554  * with a reference to an error filesystem.
555  */
556 static void assign_error_mntfs P((am_node *mp));
557 static void assign_error_mntfs(mp)
558 am_node *mp;
559 {
560 	if (mp->am_error > 0) {
561 		/*
562 		 * Save the old error code
563 		 */
564 		int error = mp->am_error;
565 		if (error <= 0)
566 			error = mp->am_mnt->mf_error;
567 		/*
568 		 * Discard the old filesystem
569 		 */
570 		free_mntfs(mp->am_mnt);
571 		/*
572 		 * Allocate a new error reference
573 		 */
574 		mp->am_mnt = new_mntfs();
575 		/*
576 		 * Put back the error code
577 		 */
578 		mp->am_mnt->mf_error = error;
579 		mp->am_mnt->mf_flags |= MFF_ERROR;
580 		/*
581 		 * Zero the error in the mount point
582 		 */
583 		mp->am_error = 0;
584 	}
585 }
586 
587 /*
588  * The continuation function.  This is called by
589  * the task notifier when a background mount attempt
590  * completes.
591  */
592 static void afs_cont P((int rc, int term, voidp closure));
593 static void afs_cont(rc, term, closure)
594 int rc;
595 int term;
596 voidp closure;
597 {
598 	struct continuation *cp = (struct continuation *) closure;
599 	mntfs *mf = cp->mp->am_mnt;
600 
601 	/*
602 	 * Definitely not trying to mount at the moment
603 	 */
604 	mf->mf_flags &= ~MFF_MOUNTING;
605 	/*
606 	 * While we are mounting - try to avoid race conditions
607 	 */
608 	new_ttl(cp->mp);
609 
610 	/*
611 	 * Wakeup anything waiting for this mount
612 	 */
613 	wakeup((voidp) mf);
614 
615 	/*
616 	 * Check for termination signal or exit status...
617 	 */
618 	if (rc || term) {
619 		if (term) {
620 			/*
621 			 * Not sure what to do for an error code.
622 			 */
623 			mf->mf_error = EIO;	/* XXX ? */
624 			mf->mf_flags |= MFF_ERROR;
625 			plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
626 		} else {
627 			/*
628 			 * Check for exit status...
629 			 */
630 			mf->mf_error = rc;
631 			mf->mf_flags |= MFF_ERROR;
632 			errno = rc;	/* XXX */
633 			plog(XLOG_ERROR, "%s: mount (afs_cont): %m", cp->mp->am_path);
634 		}
635 
636 		/*
637 		 * If we get here then that attempt didn't work, so
638 		 * move the info vector pointer along by one and
639 		 * call the background mount routine again
640 		 */
641 		amd_stats.d_merr++;
642 		cp->ivec++;
643 		(void) afs_bgmount(cp, 0);
644 		assign_error_mntfs(cp->mp);
645 	} else {
646 		/*
647 		 * The mount worked.
648 		 */
649 		am_mounted(cp->mp);
650 		free_continuation(cp);
651 	}
652 
653 	reschedule_timeout_mp();
654 }
655 
656 /*
657  * Retry a mount
658  */
659 /*ARGSUSED*/
660 static void afs_retry P((int rc, int term, voidp closure));
661 static void afs_retry(rc, term, closure)
662 int rc;
663 int term;
664 voidp closure;
665 {
666 	struct continuation *cp = (struct continuation *) closure;
667 	int error = 0;
668 
669 #ifdef DEBUG
670 	dlog("Commencing retry for mount of %s", cp->mp->am_path);
671 #endif /* DEBUG */
672 
673 	if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
674 		/*
675 		 * The entire mount has timed out.
676 		 * Set the error code and skip past
677 		 * all the info vectors so that
678 		 * afs_bgmount will not have any more
679 		 * ways to try the mount, so causing
680 		 * an error.
681 		 */
682 		plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
683 		error = ETIMEDOUT;
684 		new_ttl(cp->mp);
685 		while (*cp->ivec)
686 			cp->ivec++;
687 	}
688 
689 	(void) afs_bgmount(cp, error);
690 	reschedule_timeout_mp();
691 }
692 
693 /*
694  * Try to mount a file system.  Can be called
695  * directly or in a sub-process by run_task
696  */
697 static int try_mount P((voidp mvp));
698 static int try_mount(mvp)
699 voidp mvp;
700 {
701 	/*
702 	 * Mount it!
703 	 */
704 	int error;
705 	am_node *mp = (am_node *) mvp;
706 	mntfs *mf = mp->am_mnt;
707 
708 	/*
709 	 * If the directory is not yet made and
710 	 * it needs to be made, then make it!
711 	 * This may be run in a backgroun process
712 	 * in which case the flag setting won't be
713 	 * noticed later - but it is set anyway
714 	 * just after run_task is called.  It
715 	 * should probably go away totally...
716 	 */
717 	if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
718 		error = mkdirs(mf->mf_mount, 0555);
719 		if (!error)
720 			mf->mf_flags |= MFF_MKMNT;
721 	}
722 
723 	error = mount_node(mp);
724 #ifdef DEBUG
725 	if (error > 0) {
726 		errno = error;
727 		dlog("afs call to mount_node failed: %m");
728 	}
729 #endif /* DEBUG */
730 	return error;
731 }
732 
733 /*
734  * Pick a file system to try mounting and
735  * do that in the background if necessary
736  *
737 For each location:
738 	if it is new -defaults then
739 		extract and process
740 		continue;
741 	fi
742 	if it is a cut then
743 		if a location has been tried then
744 			break;
745 		fi
746 		continue;
747 	fi
748 	parse mount location
749 	discard previous mount location if required
750 	find matching mounted filesystem
751 	if not applicable then
752 		this_error = No such file or directory
753 		continue
754 	fi
755 	if the filesystem failed to be mounted then
756 		this_error = error from filesystem
757 	elif the filesystem is mounting or unmounting then
758 		this_error = -1
759 	elif the fileserver is down then
760 		this_error = -1
761 	elif the filesystem is already mounted
762 		this_error = 0
763 		break
764 	fi
765 	if no error on this mount then
766 		this_error = initialise mount point
767 	fi
768 	if no error on this mount and mount is delayed then
769 		this_error = -1
770 	fi
771 	if this_error < 0 then
772 		retry = true
773 	fi
774 	if no error on this mount then
775 		make mount point if required
776 	fi
777 	if no error on this mount then
778 		if mount in background then
779 			run mount in background
780 			return -1
781 		else
782 			this_error = mount in foreground
783 		fi
784 	fi
785 	if an error occured on this mount then
786 		update stats
787 		save error in mount point
788 	fi
789 endfor
790  */
791 
792 static int afs_bgmount P((struct continuation *cp, int mpe));
793 static int afs_bgmount(cp, mpe)
794 struct continuation *cp;
795 int mpe;
796 {
797 	mntfs *mf = cp->mp->am_mnt;	/* Current mntfs */
798 	mntfs *mf_retry = 0;		/* First mntfs which needed retrying */
799 	int this_error = -1;		/* Per-mount error */
800 	int hard_error = -1;
801 	int mp_error = mpe;
802 
803 	/*
804 	 * Try to mount each location.
805 	 * At the end:
806 	 * hard_error == 0 indicates something was mounted.
807 	 * hard_error > 0 indicates everything failed with a hard error
808 	 * hard_error < 0 indicates nothing could be mounted now
809 	 */
810 	for (; this_error && *cp->ivec; cp->ivec++) {
811 		am_ops *p;
812 		am_node *mp = cp->mp;
813 		char *link_dir;
814 		int dont_retry;
815 
816 		if (hard_error < 0)
817 			hard_error = this_error;
818 
819 		this_error = -1;
820 
821 		if (**cp->ivec == '-') {
822 			/*
823 			 * Pick up new defaults
824 			 */
825 			if (cp->auto_opts && *cp->auto_opts)
826 				cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec+1);
827 			else
828 				cp->def_opts = strealloc(cp->def_opts, *cp->ivec+1);
829 #ifdef DEBUG
830 			dlog("Setting def_opts to \"%s\"", cp->def_opts);
831 #endif /* DEBUG */
832 			continue;
833 		}
834 
835 		/*
836 		 * If a mount has been attempted, and we find
837 		 * a cut then don't try any more locations.
838 		 */
839 		if (strcmp(*cp->ivec, "/") == 0 || strcmp(*cp->ivec, "||") == 0) {
840 			if (cp->tried) {
841 #ifdef DEBUG
842 				dlog("Cut: not trying any more locations for %s",
843 					mp->am_path);
844 #endif /* DEBUG */
845 				break;
846 			}
847 			continue;
848 		}
849 
850 #ifdef SUNOS4_COMPAT
851 		/*
852 		 * By default, you only get this bit on SunOS4.
853 		 * If you want this anyway, then define SUNOS4_COMPAT
854 		 * in the relevant "os-blah.h" file.
855 		 *
856 		 * We make the observation that if the local key line contains
857 		 * no '=' signs then either it is sick, or it is a SunOS4-style
858 		 * "host:fs[:link]" line.  In the latter case the am_opts field
859 		 * is also assumed to be in old-style, so you can't mix & match.
860 		 * You can use ${} expansions for the fs and link bits though...
861 		 *
862 		 * Actually, this doesn't really cover all the possibilities for
863 		 * the latest SunOS automounter and it is debatable whether there
864 		 * is any point bothering.
865 		 */
866 		if (strchr(*cp->ivec, '=') == 0)
867 			p = sunos4_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
868 		else
869 #endif /* SUNOS4_COMPAT */
870 			p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
871 
872 		/*
873 		 * Find a mounted filesystem for this node.
874 		 */
875 		mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, cp->fs_opts.opt_fs,
876 						cp->fs_opts.fs_mtab, cp->auto_opts, cp->fs_opts.opt_opts);
877 
878 		p = mf->mf_ops;
879 #ifdef DEBUG
880 		dlog("Got a hit with %s", p->fs_type);
881 #endif /* DEBUG */
882 		/*
883 		 * Note whether this is a real mount attempt
884 		 */
885 		if (p == &efs_ops) {
886 			plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
887 			if (this_error <= 0)
888 				this_error = ENOENT;
889 			continue;
890 		} else {
891 			if (cp->fs_opts.fs_mtab) {
892 				plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
893 					cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
894 			}
895 			cp->tried = TRUE;
896 		}
897 
898 		this_error = 0;
899 		dont_retry = FALSE;
900 
901 		if (mp->am_link) {
902 			free(mp->am_link);
903 			mp->am_link = 0;
904 		}
905 
906 		link_dir = mf->mf_fo->opt_sublink;
907 
908 		if (link_dir && *link_dir) {
909 			if (*link_dir == '/') {
910 				mp->am_link = strdup(link_dir);
911 			} else {
912 				mp->am_link = str3cat((char *) 0,
913 					mf->mf_fo->opt_fs, "/", link_dir);
914 			}
915 		}
916 
917 		if (mf->mf_error > 0) {
918 			this_error = mf->mf_error;
919 		} else if (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)) {
920 			/*
921 			 * Still mounting - retry later
922 			 */
923 #ifdef DEBUG
924 			dlog("Duplicate pending mount fstype %s", p->fs_type);
925 #endif /* DEBUG */
926 			this_error = -1;
927 		} else if (FSRV_ISDOWN(mf->mf_server)) {
928 			/*
929 			 * Would just mount from the same place
930 			 * as a hung mount - so give up
931 			 */
932 #ifdef DEBUG
933 			dlog("%s is already hung - giving up", mf->mf_mount);
934 #endif /* DEBUG */
935 			mp_error = EWOULDBLOCK;
936 			dont_retry = TRUE;
937 			this_error = -1;
938 		} else if (mf->mf_flags & MFF_MOUNTED) {
939 #ifdef DEBUG
940 			dlog("duplicate mount of \"%s\" ...", mf->mf_info);
941 #endif /* DEBUG */
942 			/*
943 			 * Just call mounted()
944 			 */
945 			am_mounted(mp);
946 
947 			this_error = 0;
948 			break;
949 		}
950 
951 		/*
952 		 * Will usually need to play around with the mount nodes
953 		 * file attribute structure.  This must be done here.
954 		 * Try and get things initialised, even if the fileserver
955 		 * is not known to be up.  In the common case this will
956 		 * progress things faster.
957 		 */
958 		if (!this_error) {
959 			/*
960 			 * Fill in attribute fields.
961 			 */
962 			if (mf->mf_ops->fs_flags & FS_DIRECTORY)
963 				mk_fattr(mp, NFDIR);
964 			else
965 				mk_fattr(mp, NFLNK);
966 
967 			mp->am_fattr.fileid = mp->am_gen;
968 
969 			if (p->fs_init)
970 				this_error = (*p->fs_init)(mf);
971 		}
972 
973 		/*
974 		 * Make sure the fileserver is UP before doing any more work
975 		 */
976 		if (!FSRV_ISUP(mf->mf_server)) {
977 #ifdef DEBUG
978 			dlog("waiting for server %s to become available", mf->mf_server->fs_host);
979 #endif
980 			this_error =  -1;
981 		}
982 
983 		if (!this_error && mf->mf_fo->opt_delay) {
984 			/*
985 			 * If there is a delay timer on the mount
986 			 * then don't try to mount if the timer
987 			 * has not expired.
988 			 */
989 			int i = atoi(mf->mf_fo->opt_delay);
990 			if (i > 0 && clocktime() < (cp->start + i)) {
991 #ifdef DEBUG
992 				dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
993 #endif /* DEBUG */
994 				this_error = -1;
995 			}
996 		}
997 
998 		if (this_error < 0 && !dont_retry) {
999 			if (!mf_retry)
1000 				mf_retry = dup_mntfs(mf);
1001 			cp->retry = TRUE;
1002 		}
1003 
1004 		if (!this_error)
1005 		if (p->fs_flags & FS_MBACKGROUND) {
1006 			mf->mf_flags |= MFF_MOUNTING;	/*XXX*/
1007 #ifdef DEBUG
1008 			dlog("backgrounding mount of \"%s\"", mf->mf_mount);
1009 #endif /* DEBUG */
1010 			if (cp->callout) {
1011 				untimeout(cp->callout);
1012 				cp->callout = 0;
1013 			}
1014 			run_task(try_mount, (voidp) mp, afs_cont, (voidp) cp);
1015 			mf->mf_flags |= MFF_MKMNT;	/* XXX */
1016 			if (mf_retry) free_mntfs(mf_retry);
1017 			return -1;
1018 		} else {
1019 #ifdef DEBUG
1020 			dlog("foreground mount of \"%s\" ...", mf->mf_info);
1021 #endif /* DEBUG */
1022 			this_error = try_mount((voidp) mp);
1023 		}
1024 
1025 		if (this_error >= 0) {
1026 			if (this_error > 0) {
1027 				amd_stats.d_merr++;
1028 				if (mf != mf_retry) {
1029 					mf->mf_error = this_error;
1030 					mf->mf_flags |= MFF_ERROR;
1031 				}
1032 			}
1033 			/*
1034 			 * Wakeup anything waiting for this mount
1035 			 */
1036 			wakeup((voidp) mf);
1037 		}
1038 	}
1039 
1040 	if (this_error && cp->retry) {
1041 		free_mntfs(mf);
1042 		mf = cp->mp->am_mnt = mf_retry;
1043 		/*
1044 		 * Not retrying again (so far)
1045 		 */
1046 		cp->retry = FALSE;
1047 		cp->tried = FALSE;
1048 		/*
1049 		 * Start at the beginning.
1050 		 * Rewind the location vector and
1051 		 * reset the default options.
1052 		 */
1053 		cp->ivec = cp->xivec;
1054 		cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
1055 		/*
1056 		 * Arrange that afs_bgmount is called
1057 		 * after anything else happens.
1058 		 */
1059 #ifdef DEBUG
1060 		dlog("Arranging to retry mount of %s", cp->mp->am_path);
1061 #endif /* DEBUG */
1062 		sched_task(afs_retry, (voidp) cp, (voidp) mf);
1063 		if (cp->callout)
1064 			untimeout(cp->callout);
1065 		cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
1066 
1067 		cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
1068 
1069 		/*
1070 		 * Not done yet - so don't return anything
1071 		 */
1072 		return -1;
1073 	}
1074 
1075 	if (hard_error < 0 || this_error != 0)
1076 		hard_error = this_error;
1077 
1078 	/*
1079 	 * Discard handle on duff filesystem.
1080 	 * This should never happen since it
1081 	 * should be caught by the case above.
1082 	 */
1083 	if (mf_retry) {
1084 		if (hard_error)
1085 			plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
1086 		free_mntfs(mf_retry);
1087 	}
1088 
1089 	/*
1090 	 * If we get here, then either the mount succeeded or
1091 	 * there is no more mount information available.
1092 	 */
1093 	if (hard_error < 0 && mp_error)
1094 		hard_error = cp->mp->am_error = mp_error;
1095 	if (hard_error > 0) {
1096 		/*
1097 		 * Set a small(ish) timeout on an error node if
1098 		 * the error was not a time out.
1099 		 */
1100 		switch (hard_error) {
1101 		case ETIMEDOUT:
1102 		case EWOULDBLOCK:
1103 			cp->mp->am_timeo = 5;
1104 			break;
1105 		default:
1106 			cp->mp->am_timeo = 17;
1107 			break;
1108 		}
1109 		cp->mp->am_timeo_w = 0;
1110 	}
1111 
1112 	/*
1113 	 * Make sure that the error value in the mntfs has a
1114 	 * reasonable value.
1115 	 */
1116 	if (mf->mf_error < 0) {
1117 		mf->mf_error = hard_error;
1118 		if (hard_error)
1119 			mf->mf_flags |= MFF_ERROR;
1120 	}
1121 
1122 	/*
1123 	 * In any case we don't need the continuation any more
1124 	 */
1125 	free_continuation(cp);
1126 
1127 	return hard_error;
1128 }
1129 
1130 /*
1131  * Automount interface to RPC lookup routine
1132  */
1133 static am_node *afs_lookuppn P((am_node *mp, char *fname, int *error_return, int op));
1134 static am_node *afs_lookuppn(mp, fname, error_return, op)
1135 am_node *mp;
1136 char *fname;
1137 int *error_return;
1138 int op;
1139 {
1140 #define ereturn(x) { *error_return = x; return 0; }
1141 
1142 	/*
1143 	 * Find the corresponding entry and return
1144 	 * the file handle for it.
1145 	 */
1146 	am_node *ap, *new_mp, *ap_hung;
1147 	char *info;			/* Mount info - where to get the file system */
1148 	char **ivec, **xivec;		/* Split version of info */
1149 	char *auto_opts;		/* Automount options */
1150 	int error = 0;			/* Error so far */
1151 	char path_name[MAXPATHLEN];	/* General path name buffer */
1152 	char *pfname;			/* Path for database lookup */
1153 	struct continuation *cp;	/* Continuation structure if we need to mount */
1154 	int in_progress = 0;		/* # of (un)mount in progress */
1155 	char *dflts;
1156 	mntfs *mf;
1157 
1158 #ifdef DEBUG
1159 	dlog("in afs_lookuppn");
1160 #endif /* DEBUG */
1161 
1162 	/*
1163 	 * If the server is shutting down
1164 	 * then don't return information
1165 	 * about the mount point.
1166 	 */
1167 	if (amd_state == Finishing) {
1168 #ifdef DEBUG
1169 		if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &dfs_ops)
1170 			dlog("%s mount ignored - going down", fname);
1171 		else
1172 			dlog("%s/%s mount ignored - going down", mp->am_path, fname);
1173 #endif /* DEBUG */
1174 		ereturn(ENOENT);
1175 	}
1176 
1177 	/*
1178 	 * Handle special case of "." and ".."
1179 	 */
1180 	if (fname[0] == '.') {
1181 		if (fname[1] == '\0')
1182 			return mp;	/* "." is the current node */
1183 		if (fname[1] == '.' && fname[2] == '\0') {
1184 			if (mp->am_parent) {
1185 #ifdef DEBUG
1186 				dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
1187 #endif /* DEBUG */
1188 				return mp->am_parent;	/* ".." is the parent node */
1189 			}
1190 			ereturn(ESTALE);
1191 		}
1192 	}
1193 
1194 	/*
1195 	 * Check for valid key name.
1196 	 * If it is invalid then pretend it doesn't exist.
1197 	 */
1198 	if (!valid_key(fname)) {
1199 		plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
1200 		ereturn(ENOENT);
1201 	}
1202 
1203 	/*
1204 	 * Expand key name.
1205 	 * fname is now a private copy.
1206 	 */
1207 	fname = expand_key(fname);
1208 
1209 	for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
1210 		/*
1211 		 * Otherwise search children of this node
1212 		 */
1213 		if (FSTREQ(ap->am_name, fname)) {
1214 			mf = ap->am_mnt;
1215 			if (ap->am_error) {
1216 				error = ap->am_error;
1217 				continue;
1218 			}
1219 
1220 			/*
1221 			 * If the error code is undefined then it must be
1222 			 * in progress.
1223 			 */
1224 			if (mf->mf_error < 0)
1225 				goto in_progrss;
1226 
1227 			/*
1228 			 * Check for a hung node
1229 			 */
1230 			if (FSRV_ISDOWN(mf->mf_server)) {
1231 				ap_hung = ap;
1232 				continue;
1233 			}
1234 
1235 			/*
1236 			 * If there was a previous error with this node
1237 			 * then return that error code.
1238 			 */
1239 			if (mf->mf_flags & MFF_ERROR) {
1240 				error = mf->mf_error;
1241 				continue;
1242 			}
1243 
1244 			if (!(mf->mf_flags & MFF_MOUNTED) /*|| (mf->mf_flags & MFF_UNMOUNTING)*/) {
1245 in_progrss:
1246 				/*
1247 				 * If the fs is not mounted or it is unmounting then there
1248 				 * is a background (un)mount in progress.  In this case
1249 				 * we just drop the RPC request (return nil) and
1250 				 * wait for a retry, by which time the (un)mount may
1251 				 * have completed.
1252 				 */
1253 #ifdef DEBUG
1254 				dlog("ignoring mount of %s in %s -- in progress",
1255 					fname, mf->mf_mount);
1256 #endif /* DEBUG */
1257 				in_progress++;
1258 				continue;
1259 			}
1260 
1261 			/*
1262 			 * Otherwise we have a hit: return the current mount point.
1263 			 */
1264 #ifdef DEBUG
1265 			dlog("matched %s in %s", fname, ap->am_path);
1266 #endif /* DEBUG */
1267 			free(fname);
1268 			return ap;
1269 		}
1270 	}
1271 
1272 	if (in_progress) {
1273 #ifdef DEBUG
1274 		dlog("Waiting while %d mount(s) in progress", in_progress);
1275 #endif /* DEBUG */
1276 		free(fname);
1277 		ereturn(-1);
1278 	}
1279 
1280 	/*
1281 	 * If an error occured then return it.
1282 	 */
1283 	if (error) {
1284 #ifdef DEBUG
1285 		errno = error; /* XXX */
1286 		dlog("Returning error: %m", error);
1287 #endif /* DEBUG */
1288 		free(fname);
1289 		ereturn(error);
1290 	}
1291 
1292 	/*
1293 	 * If doing a delete then don't create again!
1294 	 */
1295 	switch (op) {
1296 	case VLOOK_DELETE:
1297 		ereturn(ENOENT);
1298 		break;
1299 
1300 	case VLOOK_CREATE:
1301 		break;
1302 
1303 	default:
1304 		plog(XLOG_FATAL, "Unknown op to afs_lookuppn: 0x%x", op);
1305 		ereturn(EINVAL);
1306 		break;
1307 	}
1308 
1309 	/*
1310 	 * If the server is going down then just return,
1311 	 * don't try to mount any more file systems
1312 	 */
1313 	if ((int)amd_state >= (int)Finishing) {
1314 #ifdef DEBUG
1315 		dlog("not found - server going down anyway");
1316 #endif /* DEBUG */
1317 		free(fname);
1318 		ereturn(ENOENT);
1319 	}
1320 
1321 	/*
1322 	 * If we get there then this is a reference to an,
1323 	 * as yet, unknown name so we need to search the mount
1324 	 * map for it.
1325 	 */
1326 	if (mp->am_pref) {
1327 		sprintf(path_name, "%s%s", mp->am_pref, fname);
1328 		pfname = path_name;
1329 	} else {
1330 		pfname = fname;
1331 	}
1332 
1333 	mf = mp->am_mnt;
1334 
1335 #ifdef DEBUG
1336 	dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1337 #endif /* DEBUG */
1338 	/*
1339 	 * Consult the oracle for some mount information.
1340 	 * info is malloc'ed and belongs to this routine.
1341 	 * It ends up being free'd in free_continuation().
1342 	 *
1343 	 * Note that this may return -1 indicating that information
1344 	 * is not yet available.
1345 	 */
1346 	error = mapc_search((mnt_map*) mf->mf_private, pfname, &info);
1347 	if (error) {
1348 		if (error > 0)
1349 			plog(XLOG_MAP, "No map entry for %s", pfname);
1350 		else
1351 			plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
1352 		free(fname);
1353 		ereturn(error);
1354 	}
1355 
1356 #ifdef DEBUG
1357 	dlog("mount info is %s", info);
1358 #endif /* DEBUG */
1359 
1360 	/*
1361 	 * Split info into an argument vector.
1362 	 * The vector is malloc'ed and belongs to
1363 	 * this routine.  It is free'd in free_continuation()
1364 	 */
1365 	xivec = ivec = strsplit(info, ' ', '\"');
1366 
1367 	/*
1368 	 * Default error code...
1369 	 */
1370 	if (ap_hung)
1371 		error = EWOULDBLOCK;
1372 	else
1373 		error = ENOENT;
1374 
1375 	/*
1376 	 * Allocate a new map
1377 	 */
1378 	new_mp = exported_ap_alloc();
1379 	if (new_mp == 0) {
1380 		free((voidp) xivec);
1381 		free((voidp) info);
1382 		free((voidp) fname);
1383 		ereturn(ENOSPC);
1384 	}
1385 
1386 	if (mf->mf_auto)
1387 		auto_opts = mf->mf_auto;
1388 	else
1389 		auto_opts = "";
1390 
1391 	auto_opts = strdup(auto_opts);
1392 
1393 #ifdef DEBUG
1394 	dlog("searching for /defaults entry");
1395 #endif /* DEBUG */
1396 	if (mapc_search((mnt_map*) mf->mf_private, "/defaults", &dflts) == 0) {
1397 	  	char *dfl;
1398 		char **rvec;
1399 #ifdef DEBUG
1400 		dlog("/defaults gave %s", dflts);
1401 #endif /* DEBUG */
1402 		if (*dflts == '-')
1403 			dfl = dflts+1;
1404 		else
1405 			dfl = dflts;
1406 
1407 		/*
1408 		 * Chop the defaults up
1409 		 */
1410 		rvec = strsplit(dfl, ' ', '\"');
1411 		/*
1412 		 * Extract first value
1413 		 */
1414 		dfl = rvec[0];
1415 
1416 		/*
1417 		 * Log error if there were other values
1418 		 */
1419 		if (rvec[1]) {
1420 #ifdef DEBUG
1421 			dlog("/defaults chopped into %s", dfl);
1422 #endif /* DEBUG */
1423 			plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1424 		}
1425 
1426 		/*
1427 		 * Don't need info vector any more
1428 		 */
1429 		free((voidp) rvec);
1430 
1431 		/*
1432 		 * If there were any values at all...
1433 		 */
1434 		if (dfl) {
1435 			/*
1436 			 * Prepend to existing defaults if they exist,
1437 			 * otherwise just use these defaults.
1438 			 */
1439 			if (*auto_opts && *dfl) {
1440 				char *nopts = (char *) xmalloc(strlen(auto_opts)+strlen(dfl)+2);
1441 				sprintf(nopts, "%s;%s", dfl, auto_opts);
1442 				free(auto_opts);
1443 				auto_opts = nopts;
1444 			} else if (*dfl) {
1445 				auto_opts = strealloc(auto_opts, dfl);
1446 			}
1447 		}
1448 		free(dflts);
1449 	}
1450 
1451 	/*
1452 	 * Fill it in
1453 	 */
1454 	init_map(new_mp, fname);
1455 
1456 	/*
1457 	 * Put it in the table
1458 	 */
1459 	insert_am(new_mp, mp);
1460 
1461 	/*
1462 	 * Fill in some other fields,
1463 	 * path and mount point.
1464 	 *
1465 	 * bugfix: do not prepend old am_path if direct map
1466 	 *         <wls@astro.umd.edu> William Sebok
1467 	 */
1468 	new_mp->am_path = str3cat(new_mp->am_path,
1469 		mf->mf_ops == &dfs_ops ? "" : mp->am_path,
1470 		*fname == '/' ? "" : "/", fname);
1471 
1472 #ifdef DEBUG
1473 	dlog("setting path to %s", new_mp->am_path);
1474 #endif /* DEBUG */
1475 
1476 	/*
1477 	 * Take private copy of pfname
1478 	 */
1479 	pfname = strdup(pfname);
1480 
1481 	/*
1482 	 * Construct a continuation
1483 	 */
1484 	cp = ALLOC(continuation);
1485 	cp->mp = new_mp;
1486 	cp->xivec = xivec;
1487 	cp->ivec = ivec;
1488 	cp->info = info;
1489 	cp->key = pfname;
1490 	cp->auto_opts = auto_opts;
1491 	cp->retry = FALSE;
1492 	cp->tried = FALSE;
1493 	cp->start = clocktime();
1494 	cp->def_opts = strdup(auto_opts);
1495 	bzero((voidp) &cp->fs_opts, sizeof(cp->fs_opts));
1496 
1497 	/*
1498 	 * Try and mount the file system
1499 	 * If this succeeds immediately (possible
1500 	 * for a ufs file system) then return
1501 	 * the attributes, otherwise just
1502 	 * return an error.
1503 	 */
1504 	error = afs_bgmount(cp, error);
1505 	reschedule_timeout_mp();
1506 	if (!error) {
1507 		free(fname);
1508 		return new_mp;
1509 	}
1510 
1511 	assign_error_mntfs(cp->mp);
1512 
1513 	free(fname);
1514 
1515 	ereturn(error);
1516 #undef ereturn
1517 }
1518 
1519 /*
1520  * Locate next node in sibling list which is mounted
1521  * and is not an error node.
1522  */
1523 static am_node *next_nonerror_node P((am_node *xp));
1524 static am_node *next_nonerror_node(xp)
1525 am_node *xp;
1526 {
1527 	mntfs *mf;
1528 
1529 	/*
1530 	 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
1531 	 * Fixes a race condition when mounting direct automounts.
1532 	 * Also fixes a problem when doing a readdir on a directory
1533 	 * containing hung automounts.
1534 	 */
1535 	while (xp &&
1536 	       (!(mf = xp->am_mnt) ||			/* No mounted filesystem */
1537 	        mf->mf_error != 0 ||			/* There was a mntfs error */
1538 	        xp->am_error != 0 ||			/* There was a mount error */
1539 	        !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
1540 	        (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
1541 		)
1542 		xp = xp->am_osib;
1543 
1544 	return xp;
1545 }
1546 
1547 static int afs_readdir P((am_node *mp, nfscookie cookie, struct dirlist *dp, struct entry *ep, int count));
1548 static int afs_readdir(mp, cookie, dp, ep, count)
1549 am_node *mp;
1550 nfscookie cookie;
1551 struct dirlist *dp;
1552 struct entry *ep;
1553 int count;
1554 {
1555 	unsigned int gen = *(unsigned int*) cookie;
1556 	am_node *xp;
1557 
1558 	dp->eof = FALSE;
1559 
1560 	if (gen == 0) {
1561 		/*
1562 		 * In the default instance (which is used to
1563 		 * start a search) we return "." and "..".
1564 		 *
1565 		 * This assumes that the count is big enough
1566 		 * to allow both "." and ".." to be returned in
1567 		 * a single packet.  If it isn't (which would
1568 		 * be fairly unbelievable) then tough.
1569 		 */
1570 #ifdef DEBUG
1571 		dlog("default search");
1572 #endif /* DEBUG */
1573 		xp = next_nonerror_node(mp->am_child);
1574 		dp->entries = ep;
1575 
1576 		/* construct "." */
1577 		ep[0].fileid = mp->am_gen;
1578 		ep[0].name = ".";
1579 		ep[0].nextentry = &ep[1];
1580 		*(unsigned int *) ep[0].cookie = 0;
1581 
1582 		/* construct ".." */
1583 		if (mp->am_parent)
1584 			ep[1].fileid = mp->am_parent->am_gen;
1585 		else
1586 			ep[1].fileid = mp->am_gen;
1587 		ep[1].name = "..";
1588 		ep[1].nextentry = 0;
1589 		*(unsigned int *) ep[1].cookie =
1590 			xp ? xp->am_gen : ~(unsigned int)0;
1591 
1592 		if (!xp) dp->eof = TRUE;
1593 		return 0;
1594 	}
1595 
1596 #ifdef DEBUG
1597 	dlog("real child");
1598 #endif /* DEBUG */
1599 
1600 	if (gen == ~(unsigned int)0) {
1601 #ifdef DEBUG
1602 		dlog("End of readdir in %s", mp->am_path);
1603 #endif /* DEBUG */
1604 		dp->eof = TRUE;
1605 		dp->entries = 0;
1606 		return 0;
1607 	}
1608 
1609 	xp = mp->am_child;
1610 	while (xp && xp->am_gen != gen)
1611 		xp = xp->am_osib;
1612 
1613 	if (xp) {
1614 		int nbytes = count / 2;		/* conservative */
1615 		int todo = MAX_READDIR_ENTRIES;
1616 		dp->entries = ep;
1617 		do {
1618 			am_node *xp_next = next_nonerror_node(xp->am_osib);
1619 
1620 			if (xp_next) {
1621 				*(unsigned int *) ep->cookie = xp_next->am_gen;
1622 			} else {
1623 				*(unsigned int *) ep->cookie = ~(unsigned int)0;
1624 				dp->eof = TRUE;
1625 			}
1626 
1627 			ep->fileid = xp->am_gen;
1628 			ep->name = xp->am_name;
1629 			nbytes -= sizeof(*ep) + strlen(xp->am_name) + 1;
1630 
1631 			xp = xp_next;
1632 
1633 			if (nbytes > 0 && !dp->eof && todo > 1) {
1634 				ep->nextentry = ep + 1;
1635 				ep++;
1636 				--todo;
1637 			} else {
1638 				todo = 0;
1639 			}
1640 		} while (todo > 0);
1641 
1642 		ep->nextentry = 0;
1643 
1644 		return 0;
1645 	}
1646 
1647 	return ESTALE;
1648 
1649 }
1650 
1651 static am_node *dfs_readlink P((am_node *mp, int *error_return));
1652 static am_node *dfs_readlink(mp, error_return)
1653 am_node *mp;
1654 int *error_return;
1655 {
1656 	am_node *xp;
1657 	int rc = 0;
1658 
1659 	xp = next_nonerror_node(mp->am_child);
1660 	if (!xp) {
1661 		if (!mp->am_mnt->mf_private)
1662 			afs_mkcacheref(mp->am_mnt);	/* XXX */
1663 		xp = afs_lookuppn(mp, mp->am_path+1, &rc, VLOOK_CREATE);
1664 	}
1665 
1666 	if (xp) {
1667 		new_ttl(xp);	/* (7/12/89) from Rein Tollevik */
1668 		return xp;
1669 	}
1670 	if (amd_state == Finishing)
1671 		rc = ENOENT;
1672 	*error_return = rc;
1673 	return 0;
1674 }
1675 
1676 /*
1677  * Ops structure
1678  */
1679 am_ops root_ops = {
1680 	"root",
1681 	0, /* root_match */
1682 	0, /* root_init */
1683 	root_mount,
1684 	0,
1685 	afs_umount,
1686 	0,
1687 	afs_lookuppn,
1688 	afs_readdir,
1689 	0, /* root_readlink */
1690 	0, /* root_mounted */
1691 	0, /* root_umounted */
1692 	find_afs_srvr,
1693 	FS_NOTIMEOUT|FS_AMQINFO|FS_DIRECTORY
1694 };
1695 
1696 am_ops afs_ops = {
1697 	"auto",
1698 	afs_match,
1699 	0, /* afs_init */
1700 	afs_mount,
1701 	0,
1702 	afs_umount,
1703 	0,
1704 	afs_lookuppn,
1705 	afs_readdir,
1706 	0, /* afs_readlink */
1707 	0, /* afs_mounted */
1708 	afs_umounted,
1709 	find_afs_srvr,
1710 	FS_AMQINFO|FS_DIRECTORY
1711 };
1712 
1713 am_ops toplvl_ops = {
1714 	"toplvl",
1715 	afs_match,
1716 	0, /* afs_init */
1717 	toplvl_mount,
1718 	0,
1719 	toplvl_umount,
1720 	0,
1721 	afs_lookuppn,
1722 	afs_readdir,
1723 	0, /* toplvl_readlink */
1724 	toplvl_mounted,
1725 	0, /* toplvl_umounted */
1726 	find_afs_srvr,
1727 	FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY
1728 };
1729 
1730 am_ops dfs_ops = {
1731 	"direct",
1732 	afs_match,
1733 	0, /* dfs_init */
1734 	toplvl_mount,
1735 	0,
1736 	toplvl_umount,
1737 	0,
1738 	efs_lookuppn,
1739 	efs_readdir,
1740 	dfs_readlink,
1741 	toplvl_mounted,
1742 	0, /* afs_umounted */
1743 	find_afs_srvr,
1744 	FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO
1745 };
1746 
1747 #ifdef HAS_UNION_FS
1748 am_ops union_ops = {
1749 	"union",
1750 	afs_match,
1751 	0, /* afs_init */
1752 	toplvl_mount,
1753 	0,
1754 	toplvl_umount,
1755 	0,
1756 	afs_lookuppn,
1757 	afs_readdir,
1758 	0, /* toplvl_readlink */
1759 	union_mounted,
1760 	0, /* toplvl_umounted */
1761 	find_afs_srvr,
1762 	FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY
1763 };
1764 #endif /* HAS_UNION_FS */
1765