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