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