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