xref: /original-bsd/usr.sbin/amd/amd/nfs_ops.c (revision d364528e)
1 /*
2  * Copyright (c) 1990 Jan-Simon Pendry
3  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1990 The Regents of the University of California.
5  * 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  *	@(#)nfs_ops.c	1.3 (Berkeley) 6/26/91
13  *
14  * $Id: nfs_ops.c,v 5.2.2.2 1992/05/31 16:35:05 jsp Exp $
15  *
16  */
17 
18 #include "am.h"
19 #include <sys/stat.h>
20 
21 #ifdef HAS_NFS
22 
23 #define NFS
24 #define NFSCLIENT
25 #ifdef NFS_3
26 typedef nfs_fh fhandle_t;
27 #endif /* NFS_3 */
28 #ifdef NFS_HDR
29 #include NFS_HDR
30 #endif /* NFS_HDR */
31 #include <sys/mount.h>
32 #include "mount.h"
33 
34 /*
35  * Network file system
36  */
37 
38 /*
39  * Convert from nfsstat to UN*X error code
40  */
41 #define unx_error(e)	((int)(e))
42 
43 /*
44  * The NFS layer maintains a cache of file handles.
45  * This is *fundamental* to the implementation and
46  * also allows quick remounting when a filesystem
47  * is accessed soon after timing out.
48  *
49  * The NFS server layer knows to flush this cache
50  * when a server goes down so avoiding stale handles.
51  *
52  * Each cache entry keeps a hard reference to
53  * the corresponding server.  This ensures that
54  * the server keepalive information is maintained.
55  *
56  * The copy of the sockaddr_in here is taken so
57  * that the port can be twiddled to talk to mountd
58  * instead of portmap or the NFS server as used
59  * elsewhere.
60  * The port# is flushed if a server goes down.
61  * The IP address is never flushed - we assume
62  * that the address of a mounted machine never
63  * changes.  If it does, then you have other
64  * problems...
65  */
66 typedef struct fh_cache fh_cache;
67 struct fh_cache {
68 	qelem	fh_q;			/* List header */
69 	voidp	fh_wchan;		/* Wait channel */
70 	int	fh_error;		/* Valid data? */
71 	int	fh_id;			/* Unique id */
72 	int	fh_cid;			/* Callout id */
73 	struct fhstatus fh_handle;	/* Handle on filesystem */
74 	struct sockaddr_in fh_sin;	/* Address of mountd */
75 	fserver *fh_fs;			/* Server holding filesystem */
76 	char	*fh_path;		/* Filesystem on host */
77 };
78 
79 /*
80  * FH_TTL is the time a file handle will remain in the cache since
81  * last being used.  If the file handle becomes invalid, then it
82  * will be flushed anyway.
83  */
84 #define	FH_TTL		(5 * 60)		/* five minutes */
85 #define	FH_TTL_ERROR	(30)			/* 30 seconds */
86 
87 static int fh_id = 0;
88 #define	FHID_ALLOC()	(++fh_id)
89 extern qelem fh_head;
90 qelem fh_head = { &fh_head, &fh_head };
91 
92 static int call_mountd P((fh_cache*, unsigned long, fwd_fun, voidp));
93 
94 AUTH *nfs_auth;
95 
96 static fh_cache *find_nfs_fhandle_cache P((voidp idv, int done));
97 static fh_cache *find_nfs_fhandle_cache(idv, done)
98 voidp idv;
99 int done;
100 {
101 	fh_cache *fp, *fp2 = 0;
102 	int id = (int) idv;
103 
104 	ITER(fp, fh_cache, &fh_head) {
105 		if (fp->fh_id == id) {
106 			fp2 = fp;
107 			break;
108 		}
109 	}
110 
111 #ifdef DEBUG
112 	if (fp2) {
113 		dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path);
114 	} else {
115 		dlog("fh cache search failed");
116 	}
117 #endif /* DEBUG */
118 
119 	if (fp2 && !done) {
120 		fp2->fh_error = ETIMEDOUT;
121 		return 0;
122 	}
123 
124 	return fp2;
125 }
126 
127 /*
128  * Called when a filehandle appears
129  */
130 static void got_nfs_fh P((voidp pkt, int len, struct sockaddr_in *sa,
131 				struct sockaddr_in *ia, voidp idv, int done));
132 static void got_nfs_fh(pkt, len, sa, ia, idv, done)
133 voidp pkt;
134 int len;
135 struct sockaddr_in *sa, *ia;
136 voidp idv;
137 int done;
138 {
139 	fh_cache *fp = find_nfs_fhandle_cache(idv, done);
140 	if (fp) {
141 		fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_handle, xdr_fhstatus);
142 		if (!fp->fh_error) {
143 #ifdef DEBUG
144 			dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
145 #endif /* DEBUG */
146 			/*
147 			 * Wakeup anything sleeping on this filehandle
148 			 */
149 			if (fp->fh_wchan) {
150 #ifdef DEBUG
151 				dlog("Calling wakeup on %#x", fp->fh_wchan);
152 #endif /* DEBUG */
153 				wakeup(fp->fh_wchan);
154 			}
155 		}
156 	}
157 }
158 
159 void flush_nfs_fhandle_cache P((fserver *fs));
160 void flush_nfs_fhandle_cache(fs)
161 fserver *fs;
162 {
163 	fh_cache *fp;
164 	ITER(fp, fh_cache, &fh_head) {
165 		if (fp->fh_fs == fs || fs == 0) {
166 			fp->fh_sin.sin_port = (u_short) 0;
167 			fp->fh_error = -1;
168 		}
169 	}
170 }
171 
172 static void discard_fh P((fh_cache *fp));
173 static void discard_fh(fp)
174 fh_cache *fp;
175 {
176 	rem_que(&fp->fh_q);
177 #ifdef DEBUG
178 	dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
179 #endif /* DEBUG */
180 	free_srvr(fp->fh_fs);
181 	free((voidp) fp->fh_path);
182 	free((voidp) fp);
183 }
184 
185 /*
186  * Determine the file handle for a node
187  */
188 static int prime_nfs_fhandle_cache P((char *path, fserver *fs, struct fhstatus *fhbuf, voidp wchan));
189 static int prime_nfs_fhandle_cache(path, fs, fhbuf, wchan)
190 char *path;
191 fserver *fs;
192 struct fhstatus *fhbuf;
193 voidp wchan;
194 {
195 	fh_cache *fp, *fp_save = 0;
196 	int error;
197 	int reuse_id = FALSE;
198 
199 #ifdef DEBUG
200 	dlog("Searching cache for %s:%s", fs->fs_host, path);
201 #endif /* DEBUG */
202 
203 	/*
204 	 * First search the cache
205 	 */
206 	ITER(fp, fh_cache, &fh_head) {
207 		if (fs == fp->fh_fs && strcmp(path, fp->fh_path) == 0) {
208 			switch (fp->fh_error) {
209 			case 0:
210 				error = fp->fh_error = unx_error(fp->fh_handle.fhs_status);
211 				if (error == 0) {
212 					if (fhbuf)
213 						bcopy((voidp) &fp->fh_handle, (voidp) fhbuf,
214 							sizeof(fp->fh_handle));
215 					if (fp->fh_cid)
216 						untimeout(fp->fh_cid);
217 					fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
218 				} else if (error == EACCES) {
219 					/*
220 					 * Now decode the file handle return code.
221 					 */
222 					plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
223 						fs->fs_host, path);
224 				} else {
225 					errno = error;	/* XXX */
226 					plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
227 						fs->fs_host, path);
228 				}
229 
230 				/*
231 				 * The error was returned from the remote mount daemon.
232 				 * Policy: this error will be cached for now...
233 				 */
234 				return error;
235 
236 			case -1:
237 				/*
238 				 * Still thinking about it, but we can re-use.
239 				 */
240 				fp_save = fp;
241 				reuse_id = TRUE;
242 				break;
243 
244 			default:
245 				/*
246 				 * Return the error.
247 				 * Policy: make sure we recompute if required again
248 				 * in case this was caused by a network failure.
249 				 * This can thrash mountd's though...  If you find
250 				 * your mountd going slowly then:
251 				 * 1.  Add a fork() loop to main.
252 				 * 2.  Remove the call to innetgr() and don't use
253 				 *     netgroups, especially if you don't use YP.
254 				 */
255 				error = fp->fh_error;
256 				fp->fh_error = -1;
257 				return error;
258 			}
259 			break;
260 		}
261 	}
262 
263 	/*
264 	 * Not in cache
265 	 */
266 	if (fp_save) {
267 		fp = fp_save;
268 		/*
269 		 * Re-use existing slot
270 		 */
271 		untimeout(fp->fh_cid);
272 		free_srvr(fp->fh_fs);
273 		free(fp->fh_path);
274 	} else {
275 		fp = ALLOC(fh_cache);
276 		bzero((voidp) fp, sizeof(*fp));
277 		ins_que(&fp->fh_q, &fh_head);
278 	}
279 	if (!reuse_id)
280 		fp->fh_id = FHID_ALLOC();
281 	fp->fh_wchan = wchan;
282 	fp->fh_error = -1;
283 	fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
284 
285 	/*
286 	 * If the address has changed then don't try to re-use the
287 	 * port information
288 	 */
289 	if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
290 		fp->fh_sin = *fs->fs_ip;
291 		fp->fh_sin.sin_port = 0;
292 	}
293 	fp->fh_fs = dup_srvr(fs);
294 	fp->fh_path = strdup(path);
295 
296 	error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan);
297 	if (error) {
298 		/*
299 		 * Local error - cache for a short period
300 		 * just to prevent thrashing.
301 		 */
302 		untimeout(fp->fh_cid);
303 		fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
304 						discard_fh, (voidp) fp);
305 		fp->fh_error = error;
306 	} else {
307 		error = fp->fh_error;
308 	}
309 	return error;
310 }
311 
312 int make_nfs_auth P((void))
313 {
314 #ifdef HAS_NFS_QUALIFIED_NAMES
315 	/*
316 	 * From: Chris Metcalf <metcalf@masala.lcs.mit.edu>
317 	 * Use hostd, not just hostname.  Note that uids
318 	 * and gids and the gidlist are type *int* and not the
319 	 * system uid_t and gid_t types.
320 	 */
321 	static int group_wheel = 0;
322 	nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
323 #else
324 	nfs_auth = authunix_create_default();
325 #endif
326 	if (!nfs_auth)
327 		return ENOBUFS;
328 	return 0;
329 }
330 
331 static int call_mountd P((fh_cache *fp, u_long proc, fwd_fun f, voidp wchan));
332 static int call_mountd(fp, proc, f, wchan)
333 fh_cache *fp;
334 u_long proc;
335 fwd_fun f;
336 voidp wchan;
337 {
338 	struct rpc_msg mnt_msg;
339 	int len;
340 	char iobuf[8192];
341 	int error;
342 
343 	if (!nfs_auth) {
344 		error = make_nfs_auth();
345 		if (error)
346 			return error;
347 	}
348 
349 	if (fp->fh_sin.sin_port == 0) {
350 		u_short port;
351 		error = nfs_srvr_port(fp->fh_fs, &port, wchan);
352 		if (error)
353 			return error;
354 		fp->fh_sin.sin_port = port;
355 	}
356 
357 	rpc_msg_init(&mnt_msg, MOUNTPROG, MOUNTVERS, (unsigned long) 0);
358 	len = make_rpc_packet(iobuf, sizeof(iobuf), proc,
359 			&mnt_msg, (voidp) &fp->fh_path, xdr_nfspath,  nfs_auth);
360 
361 	if (len > 0) {
362 		error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
363 			(voidp) iobuf, len, &fp->fh_sin, &fp->fh_sin, (voidp) fp->fh_id, f);
364 	} else {
365 		error = -len;
366 	}
367 /*
368  * It may be the case that we're sending to the wrong MOUNTD port.  This
369  * occurs if mountd is restarted on the server after the port has been
370  * looked up and stored in the filehandle cache somewhere.  The correct
371  * solution, if we're going to cache port numbers is to catch the ICMP
372  * port unreachable reply from the server and cause the portmap request
373  * to be redone.  The quick solution here is to invalidate the MOUNTD
374  * port.
375  */
376       fp->fh_sin.sin_port = 0;
377 
378 	return error;
379 }
380 
381 /*-------------------------------------------------------------------------*/
382 
383 /*
384  * NFS needs the local filesystem, remote filesystem
385  * remote hostname.
386  * Local filesystem defaults to remote and vice-versa.
387  */
388 static char *nfs_match(fo)
389 am_opts *fo;
390 {
391 	char *xmtab;
392 	if (fo->opt_fs && !fo->opt_rfs)
393 		fo->opt_rfs = fo->opt_fs;
394 	if (!fo->opt_rfs) {
395 		plog(XLOG_USER, "nfs: no remote filesystem specified");
396 		return FALSE;
397 	}
398 	if (!fo->opt_rhost) {
399 		plog(XLOG_USER, "nfs: no remote host specified");
400 		return FALSE;
401 	}
402 	/*
403 	 * Determine magic cookie to put in mtab
404 	 */
405 	xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
406 	sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs);
407 #ifdef DEBUG
408 	dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
409 		fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
410 #endif /* DEBUG */
411 
412 	return xmtab;
413 }
414 
415 /*
416  * Initialise am structure for nfs
417  */
418 static int nfs_init(mf)
419 mntfs *mf;
420 {
421 	if (!mf->mf_private) {
422 		int error;
423 		struct fhstatus fhs;
424 
425 		char *colon = strchr(mf->mf_info, ':');
426 		if (colon == 0)
427 			return ENOENT;
428 
429 		error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, (voidp) mf);
430 		if (!error) {
431 			mf->mf_private = (voidp) ALLOC(fhstatus);
432 			mf->mf_prfree = (void (*)()) free;
433 			bcopy((voidp) &fhs, mf->mf_private, sizeof(fhs));
434 		}
435 		return error;
436 	}
437 
438 	return 0;
439 }
440 
441 int mount_nfs_fh P((struct fhstatus *fhp, char *dir, char *fs_name, char *opts, mntfs *mf));
442 int mount_nfs_fh(fhp, dir, fs_name, opts, mf)
443 struct fhstatus *fhp;
444 char *dir;
445 char *fs_name;
446 char *opts;
447 mntfs *mf;
448 {
449 	struct nfs_args nfs_args;
450 	struct mntent mnt;
451 	int retry;
452 	char *colon;
453 	/*char *path;*/
454 	char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
455 	fserver *fs = mf->mf_server;
456 	int flags;
457 	char *xopts;
458 	int error;
459 #ifdef notdef
460 	unsigned short port;
461 #endif /* notdef */
462 
463 	MTYPE_TYPE type = MOUNT_TYPE_NFS;
464 
465 	bzero((voidp) &nfs_args, sizeof(nfs_args));	/* Paranoid */
466 
467 	/*
468 	 * Extract host name to give to kernel
469 	 */
470 	if (!(colon = strchr(fs_name, ':')))
471 		return ENOENT;
472 #ifndef NFS_ARGS_NEEDS_PATH
473 	*colon = '\0';
474 #endif
475 	strncpy(host, fs_name, sizeof(host));
476 #ifndef NFS_ARGS_NEEDS_PATH
477 	*colon = ':';
478 #endif /* NFS_ARGS_NEEDS_PATH */
479 	/*path = colon + 1;*/
480 
481 	if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr))
482 		xopts = strdup(mf->mf_remopts);
483 	else
484 		xopts = strdup(opts);
485 
486 	bzero((voidp) &nfs_args, sizeof(nfs_args));
487 
488 	mnt.mnt_dir = dir;
489 	mnt.mnt_fsname = fs_name;
490 	mnt.mnt_type = MTAB_TYPE_NFS;
491 	mnt.mnt_opts = xopts;
492 	mnt.mnt_freq = 0;
493 	mnt.mnt_passno = 0;
494 
495 	retry = hasmntval(&mnt, "retry");
496 	if (retry <= 0)
497 		retry = 1;	/* XXX */
498 
499 /*again:*/
500 
501 	/*
502 	 * set mount args
503 	 */
504 	NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) fhp->fhstatus_u.fhs_fhandle);
505 
506 #ifdef ULTRIX_HACK
507 	nfs_args.optstr = mnt.mnt_opts;
508 #endif /* ULTRIX_HACK */
509 
510 	nfs_args.hostname = host;
511 	nfs_args.flags |= NFSMNT_HOSTNAME;
512 #ifdef HOSTNAMESZ
513 	/*
514 	 * Most kernels have a name length restriction.
515 	 */
516 	if (strlen(host) >= HOSTNAMESZ)
517 		strcpy(host + HOSTNAMESZ - 3, "..");
518 #endif /* HOSTNAMESZ */
519 
520 	if (nfs_args.rsize = hasmntval(&mnt, "rsize"))
521 		nfs_args.flags |= NFSMNT_RSIZE;
522 
523 	if (nfs_args.wsize = hasmntval(&mnt, "wsize"))
524 		nfs_args.flags |= NFSMNT_WSIZE;
525 
526 	if (nfs_args.timeo = hasmntval(&mnt, "timeo"))
527 		nfs_args.flags |= NFSMNT_TIMEO;
528 
529 	if (nfs_args.retrans = hasmntval(&mnt, "retrans"))
530 		nfs_args.flags |= NFSMNT_RETRANS;
531 
532 #ifdef NFSMNT_BIODS
533 	if (nfs_args.biods = hasmntval(&mnt, "biods"))
534 		nfs_args.flags |= NFSMNT_BIODS;
535 
536 #endif /* NFSMNT_BIODS */
537 
538 #ifdef notdef
539 /*
540  * This isn't supported by the ping algorithm yet.
541  * In any case, it is all done in nfs_init().
542  */
543  	if (port = hasmntval(&mnt, "port"))
544 		sin.sin_port = htons(port);
545 	else
546 		sin.sin_port = htons(NFS_PORT);	/* XXX should use portmapper */
547 #endif /* notdef */
548 
549 	if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL)
550 		nfs_args.flags |= NFSMNT_SOFT;
551 
552 #ifdef NFSMNT_SPONGY
553 	if (hasmntopt(&mnt, "spongy") != NULL) {
554 		nfs_args.flags |= NFSMNT_SPONGY;
555 		if (nfs_args.flags & NFSMNT_SOFT) {
556 			plog(XLOG_USER, "Mount opts soft and spongy are incompatible - soft ignored");
557 			nfs_args.flags &= ~NFSMNT_SOFT;
558 		}
559 	}
560 #endif /* MNTOPT_SPONGY */
561 
562 #ifdef MNTOPT_INTR
563 	if (hasmntopt(&mnt, MNTOPT_INTR) != NULL)
564 		nfs_args.flags |= NFSMNT_INT;
565 #endif /* MNTOPT_INTR */
566 
567 #ifdef MNTOPT_NODEVS
568 	if (hasmntopt(&mnt, MNTOPT_NODEVS) != NULL)
569 		nfs_args.flags |= NFSMNT_NODEVS;
570 #endif /* MNTOPT_NODEVS */
571 
572 #ifdef MNTOPT_COMPRESS
573 	if (hasmntopt(&mnt, "compress") != NULL)
574 		nfs_args.flags |= NFSMNT_COMPRESS;
575 #endif /* MNTOPT_COMPRESS */
576 
577 #ifdef MNTOPT_NOCONN
578 	if (hasmntopt(&mnt, "noconn") != NULL)
579 		nfs_args.flags |= NFSMNT_NOCONN;
580 #endif /* MNTOPT_NOCONN */
581 
582 #ifdef NFSMNT_PGTHRESH
583 	if (nfs_args.pg_thresh = hasmntval(&mnt, "pgthresh"))
584 		nfs_args.flags |= NFSMNT_PGTHRESH;
585 #endif /* NFSMNT_PGTHRESH */
586 
587 	NFS_SA_DREF(nfs_args, fs->fs_ip);
588 
589 	flags = compute_mount_flags(&mnt);
590 
591 #ifdef NFSMNT_NOCTO
592 	if (hasmntopt(&mnt, "nocto") != NULL)
593 		nfs_args.flags |= NFSMNT_NOCTO;
594 #endif /* NFSMNT_NOCTO */
595 
596 #ifdef HAS_TCP_NFS
597 	if (hasmntopt(&mnt, "tcp") != NULL)
598 		nfs_args.sotype = SOCK_STREAM;
599 #endif /* HAS_TCP_NFS */
600 
601 
602 #ifdef ULTRIX_HACK
603 	/*
604 	 * Ultrix passes the flags argument as part of the
605 	 * mount data structure, rather than using the
606 	 * flags argument to the system call.  This is
607 	 * confusing...
608 	 */
609 	if (!(nfs_args.flags & NFSMNT_PGTHRESH)) {
610 		nfs_args.pg_thresh = 64; /* 64k - XXX */
611 		nfs_args.flags |= NFSMNT_PGTHRESH;
612 	}
613 	nfs_args.gfs_flags = flags;
614 	flags &= M_RDONLY;
615 	if (flags & M_RDONLY)
616 		nfs_args.flags |= NFSMNT_RONLY;
617 #endif /* ULTRIX_HACK */
618 
619 	error = mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
620 	free(xopts);
621 	return error;
622 }
623 
624 static int mount_nfs(dir, fs_name, opts, mf)
625 char *dir;
626 char *fs_name;
627 char *opts;
628 mntfs *mf;
629 {
630 #ifdef notdef
631 	int error;
632 	struct fhstatus fhs;
633 	char *colon;
634 
635 	if (!(colon = strchr(fs_name, ':')))
636 		return ENOENT;
637 
638 #ifdef DEBUG
639 	dlog("locating fhandle for %s", fs_name);
640 #endif /* DEBUG */
641 	error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, (voidp) 0);
642 
643 	if (error)
644 		return error;
645 
646 	return mount_nfs_fh(&fhs, dir, fs_name, opts, mf);
647 #endif
648 	if (!mf->mf_private) {
649 		plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
650 		return EINVAL;
651 	}
652 
653 	return mount_nfs_fh((struct fhstatus *) mf->mf_private, dir, fs_name, opts, mf);
654 }
655 
656 static int nfs_fmount(mf)
657 mntfs *mf;
658 {
659 	int error;
660 
661 	error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
662 
663 #ifdef DEBUG
664 	if (error) {
665 		errno = error;
666 		dlog("mount_nfs: %m");
667 	}
668 #endif /* DEBUG */
669 	return error;
670 }
671 
672 static int nfs_fumount(mf)
673 mntfs *mf;
674 {
675 	int error = UMOUNT_FS(mf->mf_mount);
676 	if (error)
677 		return error;
678 
679 	return 0;
680 }
681 
682 static void nfs_umounted(mp)
683 am_node *mp;
684 {
685 #ifdef INFORM_MOUNTD
686 	/*
687 	 * Don't bother to inform remote mountd
688 	 * that we are finished.  Until a full
689 	 * track of filehandles is maintained
690 	 * the mountd unmount callback cannot
691 	 * be done correctly anyway...
692 	 */
693 
694 	mntfs *mf = mp->am_mnt;
695 	fserver *fs;
696 	char *colon, *path;
697 
698 	if (mf->mf_error || mf->mf_refc > 1)
699 		return;
700 
701 	fs = mf->mf_server;
702 
703 	/*
704 	 * Call the mount daemon on the server to
705 	 * announce that we are not using the fs any more.
706 	 *
707 	 * This is *wrong*.  The mountd should be called
708 	 * when the fhandle is flushed from the cache, and
709 	 * a reference held to the cached entry while the
710 	 * fs is mounted...
711 	 */
712 	colon = path = strchr(mf->mf_info, ':');
713 	if (fs && colon) {
714 		fh_cache f;
715 #ifdef DEBUG
716 		dlog("calling mountd for %s", mf->mf_info);
717 #endif /* DEBUG */
718 		*path++ = '\0';
719 		f.fh_path = path;
720 		f.fh_sin = *fs->fs_ip;
721 		f.fh_sin.sin_port = (u_short) 0;
722 		f.fh_fs = fs;
723 		f.fh_id = 0;
724 		f.fh_error = 0;
725 		(void) prime_nfs_fhandle_cache(colon+1, mf->mf_server, (struct fhstatus *) 0, (voidp) mf);
726 		(void) call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (voidp) 0);
727 		*colon = ':';
728 	}
729 #endif /* INFORM_MOUNTD */
730 
731 #ifdef KICK_KERNEL
732 	/* This should go into the mainline code, not in nfs_ops... */
733 
734 	/*
735 	 * Run lstat over the underlying directory in
736 	 * case this was a direct mount.  This will
737 	 * get the kernel back in sync with reality.
738 	 */
739 	if (mp->am_parent && mp->am_parent->am_path &&
740 	    STREQ(mp->am_parent->am_mnt->mf_ops->fs_type, "direct")) {
741 		struct stat stb;
742 		int pid;
743 		if ((pid = background()) == 0) {
744 			if (lstat(mp->am_parent->am_path, &stb) < 0) {
745 				plog(XLOG_ERROR, "lstat(%s) after unmount: %m", mp->am_parent->am_path);
746 #ifdef DEBUG
747 			} else {
748 				dlog("hack lstat(%s): ok", mp->am_parent->am_path);
749 #endif /* DEBUG */
750 			}
751 			_exit(0);
752 		}
753 	}
754 #endif /* KICK_KERNEL */
755 }
756 
757 /*
758  * Network file system
759  */
760 am_ops nfs_ops = {
761 	"nfs",
762 	nfs_match,
763 	nfs_init,
764 	auto_fmount,
765 	nfs_fmount,
766 	auto_fumount,
767 	nfs_fumount,
768 	efs_lookuppn,
769 	efs_readdir,
770 	0, /* nfs_readlink */
771 	0, /* nfs_mounted */
772 	nfs_umounted,
773 	find_nfs_srvr,
774 	FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
775 };
776 
777 #endif /* HAS_NFS */
778