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