xref: /csrg-svn/sys/nfs/nfs_vfsops.c (revision 69346)
138414Smckusick /*
268653Smckusick  * Copyright (c) 1989, 1993, 1995
363235Sbostic  *	The Regents of the University of California.  All rights reserved.
438414Smckusick  *
538414Smckusick  * This code is derived from software contributed to Berkeley by
638414Smckusick  * Rick Macklem at The University of Guelph.
738414Smckusick  *
844513Sbostic  * %sccs.include.redist.c%
938414Smckusick  *
10*69346Smckusick  *	@(#)nfs_vfsops.c	8.9 (Berkeley) 05/10/95
1138414Smckusick  */
1238414Smckusick 
1356535Sbostic #include <sys/param.h>
1456535Sbostic #include <sys/conf.h>
1556535Sbostic #include <sys/ioctl.h>
1656535Sbostic #include <sys/signal.h>
1756535Sbostic #include <sys/proc.h>
1856535Sbostic #include <sys/namei.h>
1956535Sbostic #include <sys/vnode.h>
2056535Sbostic #include <sys/kernel.h>
2156535Sbostic #include <sys/mount.h>
2256535Sbostic #include <sys/buf.h>
2356535Sbostic #include <sys/mbuf.h>
2456535Sbostic #include <sys/socket.h>
2568653Smckusick #include <sys/socketvar.h>
2656535Sbostic #include <sys/systm.h>
2747573Skarels 
2856535Sbostic #include <net/if.h>
2956535Sbostic #include <net/route.h>
3056535Sbostic #include <netinet/in.h>
3147573Skarels 
3256535Sbostic #include <nfs/rpcv2.h>
3368653Smckusick #include <nfs/nfsproto.h>
3456535Sbostic #include <nfs/nfsnode.h>
3568653Smckusick #include <nfs/nfs.h>
3656535Sbostic #include <nfs/nfsmount.h>
3756535Sbostic #include <nfs/xdr_subs.h>
3856535Sbostic #include <nfs/nfsm_subs.h>
3956535Sbostic #include <nfs/nfsdiskless.h>
4056535Sbostic #include <nfs/nqnfs.h>
4138414Smckusick 
4268653Smckusick struct nfsstats nfsstats;
4368653Smckusick static int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
4468653Smckusick 		      struct proc *);
4568653Smckusick extern int nfs_ticks;
4668653Smckusick 
4738414Smckusick /*
4838414Smckusick  * nfs vfs operations.
4938414Smckusick  */
5038414Smckusick struct vfsops nfs_vfsops = {
5138414Smckusick 	nfs_mount,
5238874Smckusick 	nfs_start,
5338414Smckusick 	nfs_unmount,
5438414Smckusick 	nfs_root,
5541294Smckusick 	nfs_quotactl,
5638414Smckusick 	nfs_statfs,
5738414Smckusick 	nfs_sync,
5854667Smckusick 	nfs_vget,
5938414Smckusick 	nfs_fhtovp,
6038414Smckusick 	nfs_vptofh,
6139443Smckusick 	nfs_init,
6268653Smckusick 	nfs_sysctl
6338414Smckusick };
6438414Smckusick 
6552196Smckusick /*
6652196Smckusick  * This structure must be filled in by a primary bootstrap or bootstrap
6752196Smckusick  * server for a diskless/dataless machine. It is initialized below just
6852196Smckusick  * to ensure that it is allocated to initialized data (.data not .bss).
6952196Smckusick  */
7052196Smckusick struct nfs_diskless nfs_diskless = { 0 };
7168653Smckusick int nfs_diskless_valid = 0;
7252196Smckusick 
7355082Storek void nfs_disconnect __P((struct nfsmount *));
7455082Storek void nfsargs_ntoh __P((struct nfs_args *));
7568653Smckusick int nfs_fsinfo __P((struct nfsmount *, struct vnode *, struct ucred *,
7668653Smckusick 	struct proc *));
7755082Storek static struct mount *nfs_mountdiskless __P((char *, char *, int,
7855082Storek     struct sockaddr_in *, struct nfs_args *, register struct vnode **));
7938414Smckusick 
8038414Smckusick /*
8141904Smckusick  * nfs statfs call
8241904Smckusick  */
8355082Storek int
8448055Smckusick nfs_statfs(mp, sbp, p)
8541904Smckusick 	struct mount *mp;
8641904Smckusick 	register struct statfs *sbp;
8748055Smckusick 	struct proc *p;
8841904Smckusick {
8941904Smckusick 	register struct vnode *vp;
9068653Smckusick 	register struct nfs_statfs *sfp;
9141904Smckusick 	register caddr_t cp;
9268653Smckusick 	register u_long *tl;
9368653Smckusick 	register long t1, t2;
9441904Smckusick 	caddr_t bpos, dpos, cp2;
9568653Smckusick 	struct nfsmount *nmp = VFSTONFS(mp);
9668653Smckusick 	int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr;
9741904Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
9841904Smckusick 	struct ucred *cred;
9941904Smckusick 	struct nfsnode *np;
10068653Smckusick 	u_quad_t tquad;
10141904Smckusick 
10268653Smckusick #ifndef nolint
10368653Smckusick 	sfp = (struct nfs_statfs *)0;
10468653Smckusick #endif
10568653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
10668653Smckusick 	if (error)
10741904Smckusick 		return (error);
10841904Smckusick 	vp = NFSTOV(np);
10941904Smckusick 	cred = crget();
11041904Smckusick 	cred->cr_ngroups = 1;
11168653Smckusick 	if (v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0)
11268653Smckusick 		(void)nfs_fsinfo(nmp, vp, cred, p);
11368653Smckusick 	nfsstats.rpccnt[NFSPROC_FSSTAT]++;
11468653Smckusick 	nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3));
11568653Smckusick 	nfsm_fhtom(vp, v3);
11668653Smckusick 	nfsm_request(vp, NFSPROC_FSSTAT, p, cred);
11768653Smckusick 	if (v3)
11868653Smckusick 		nfsm_postop_attr(vp, retattr);
11968653Smckusick 	if (!error)
12068653Smckusick 		nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
12168653Smckusick 	sbp->f_iosize = min(nmp->nm_rsize, nmp->nm_wsize);
12268653Smckusick 	if (v3) {
12368653Smckusick 		sbp->f_bsize = NFS_FABLKSIZE;
12468653Smckusick 		fxdr_hyper(&sfp->sf_tbytes, &tquad);
12568653Smckusick 		sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
12668653Smckusick 		fxdr_hyper(&sfp->sf_fbytes, &tquad);
12768653Smckusick 		sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
12868653Smckusick 		fxdr_hyper(&sfp->sf_abytes, &tquad);
12968653Smckusick 		sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
13068653Smckusick 		sbp->f_files = (fxdr_unsigned(long, sfp->sf_tfiles.nfsuquad[1])
13168653Smckusick 			& 0x7fffffff);
13268653Smckusick 		sbp->f_ffree = (fxdr_unsigned(long, sfp->sf_ffiles.nfsuquad[1])
13368653Smckusick 			& 0x7fffffff);
13456288Smckusick 	} else {
13568653Smckusick 		sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
13668653Smckusick 		sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
13768653Smckusick 		sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
13868653Smckusick 		sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
13956288Smckusick 		sbp->f_files = 0;
14056288Smckusick 		sbp->f_ffree = 0;
14156288Smckusick 	}
14241904Smckusick 	if (sbp != &mp->mnt_stat) {
14341904Smckusick 		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
14441904Smckusick 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
14541904Smckusick 	}
14641904Smckusick 	nfsm_reqdone;
14752196Smckusick 	vrele(vp);
14841904Smckusick 	crfree(cred);
14941904Smckusick 	return (error);
15041904Smckusick }
15141904Smckusick 
15241904Smckusick /*
15368653Smckusick  * nfs version 3 fsinfo rpc call
15468653Smckusick  */
15568653Smckusick int
15668653Smckusick nfs_fsinfo(nmp, vp, cred, p)
15768653Smckusick 	register struct nfsmount *nmp;
15868653Smckusick 	register struct vnode *vp;
15968653Smckusick 	struct ucred *cred;
16068653Smckusick 	struct proc *p;
16168653Smckusick {
16268653Smckusick 	register struct nfsv3_fsinfo *fsp;
16368653Smckusick 	register caddr_t cp;
16468653Smckusick 	register long t1, t2;
16568653Smckusick 	register u_long *tl, pref, max;
16668653Smckusick 	caddr_t bpos, dpos, cp2;
16768653Smckusick 	int error = 0, retattr;
16868653Smckusick 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
16968653Smckusick 
17068653Smckusick 	nfsstats.rpccnt[NFSPROC_FSINFO]++;
17168653Smckusick 	nfsm_reqhead(vp, NFSPROC_FSINFO, NFSX_FH(1));
17268653Smckusick 	nfsm_fhtom(vp, 1);
17368653Smckusick 	nfsm_request(vp, NFSPROC_FSINFO, p, cred);
17468653Smckusick 	nfsm_postop_attr(vp, retattr);
17568653Smckusick 	if (!error) {
17668653Smckusick 		nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
17768653Smckusick 		pref = fxdr_unsigned(u_long, fsp->fs_wtpref);
17868653Smckusick 		if (pref < nmp->nm_wsize)
17968653Smckusick 			nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) &
18068653Smckusick 				~(NFS_FABLKSIZE - 1);
18168653Smckusick 		max = fxdr_unsigned(u_long, fsp->fs_wtmax);
18268653Smckusick 		if (max < nmp->nm_wsize) {
18368653Smckusick 			nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
18468653Smckusick 			if (nmp->nm_wsize == 0)
18568653Smckusick 				nmp->nm_wsize = max;
18668653Smckusick 		}
18768653Smckusick 		pref = fxdr_unsigned(u_long, fsp->fs_rtpref);
18868653Smckusick 		if (pref < nmp->nm_rsize)
18968653Smckusick 			nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) &
19068653Smckusick 				~(NFS_FABLKSIZE - 1);
19168653Smckusick 		max = fxdr_unsigned(u_long, fsp->fs_rtmax);
19268653Smckusick 		if (max < nmp->nm_rsize) {
19368653Smckusick 			nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
19468653Smckusick 			if (nmp->nm_rsize == 0)
19568653Smckusick 				nmp->nm_rsize = max;
19668653Smckusick 		}
19768653Smckusick 		pref = fxdr_unsigned(u_long, fsp->fs_dtpref);
19868653Smckusick 		if (pref < nmp->nm_readdirsize)
19968653Smckusick 			nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) &
20068653Smckusick 				~(NFS_DIRBLKSIZ - 1);
20168653Smckusick 		if (max < nmp->nm_readdirsize) {
20268653Smckusick 			nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
20368653Smckusick 			if (nmp->nm_readdirsize == 0)
20468653Smckusick 				nmp->nm_readdirsize = max;
20568653Smckusick 		}
20668653Smckusick 		nmp->nm_flag |= NFSMNT_GOTFSINFO;
20768653Smckusick 	}
20868653Smckusick 	nfsm_reqdone;
20968653Smckusick 	return (error);
21068653Smckusick }
21168653Smckusick 
21268653Smckusick /*
21346988Smckusick  * Mount a remote root fs via. nfs. This depends on the info in the
21446988Smckusick  * nfs_diskless structure that has been filled in properly by some primary
21546988Smckusick  * bootstrap.
21646988Smckusick  * It goes something like this:
21746988Smckusick  * - do enough of "ifconfig" by calling ifioctl() so that the system
21846988Smckusick  *   can talk to the server
21946988Smckusick  * - If nfs_diskless.mygateway is filled in, use that address as
22046988Smckusick  *   a default gateway.
22146988Smckusick  * - hand craft the swap nfs vnode hanging off a fake mount point
22252196Smckusick  *	if swdevt[0].sw_dev == NODEV
22346988Smckusick  * - build the rootfs mount point and call mountnfs() to do the rest.
22438414Smckusick  */
22555082Storek int
22638414Smckusick nfs_mountroot()
22738414Smckusick {
22846988Smckusick 	register struct mount *mp;
22955082Storek 	register struct nfs_diskless *nd = &nfs_diskless;
23046988Smckusick 	struct socket *so;
23146988Smckusick 	struct vnode *vp;
23255082Storek 	struct proc *p = curproc;		/* XXX */
23365467Sbostic 	int error, i;
23468653Smckusick 	u_long l;
23568653Smckusick 	char buf[128];
23646988Smckusick 
23746988Smckusick 	/*
23855082Storek 	 * XXX time must be non-zero when we init the interface or else
23955082Storek 	 * the arp code will wedge...
24055082Storek 	 */
24155082Storek 	if (time.tv_sec == 0)
24255082Storek 		time.tv_sec = 1;
24355082Storek 
24468653Smckusick 	/*
24568653Smckusick 	 * XXX splnet, so networks will receive...
24668653Smckusick 	 */
24768653Smckusick 	splnet();
24868653Smckusick 
24955082Storek #ifdef notyet
25055082Storek 	/* Set up swap credentials. */
25157790Smckusick 	proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid);
25257790Smckusick 	proc0.p_ucred->cr_gid = ntohl(nd->swap_ucred.cr_gid);
25357790Smckusick 	if ((proc0.p_ucred->cr_ngroups = ntohs(nd->swap_ucred.cr_ngroups)) >
25457790Smckusick 		NGROUPS)
25557790Smckusick 		proc0.p_ucred->cr_ngroups = NGROUPS;
25657790Smckusick 	for (i = 0; i < proc0.p_ucred->cr_ngroups; i++)
25757790Smckusick 	    proc0.p_ucred->cr_groups[i] = ntohl(nd->swap_ucred.cr_groups[i]);
25855082Storek #endif
25955082Storek 
26055082Storek 	/*
26152196Smckusick 	 * Do enough of ifconfig(8) so that the critical net interface can
26246988Smckusick 	 * talk to the server.
26346988Smckusick 	 */
26468653Smckusick 	error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0);
26568653Smckusick 	if (error)
26668653Smckusick 		panic("nfs_mountroot: socreate(%04x): %d",
26768653Smckusick 			nd->myif.ifra_addr.sa_family, error);
26868653Smckusick 
26968653Smckusick 	/*
27068653Smckusick 	 * We might not have been told the right interface, so we pass
27168653Smckusick 	 * over the first ten interfaces of the same kind, until we get
27268653Smckusick 	 * one of them configured.
27368653Smckusick 	 */
27468653Smckusick 
27568653Smckusick 	for (i = strlen(nd->myif.ifra_name) - 1;
27668653Smckusick 		nd->myif.ifra_name[i] >= '0' &&
27768653Smckusick 		nd->myif.ifra_name[i] <= '9';
27868653Smckusick 		nd->myif.ifra_name[i] ++) {
27968653Smckusick 		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p);
28068653Smckusick 		if(!error)
28168653Smckusick 			break;
28268653Smckusick 	}
28368653Smckusick 	if (error)
28455082Storek 		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
28546988Smckusick 	soclose(so);
28646988Smckusick 
28746988Smckusick 	/*
28846988Smckusick 	 * If the gateway field is filled in, set it as the default route.
28946988Smckusick 	 */
29055082Storek 	if (nd->mygateway.sin_len != 0) {
29159005Ssklower 		struct sockaddr_in mask, sin;
29246988Smckusick 
29359005Ssklower 		bzero((caddr_t)&mask, sizeof(mask));
29459005Ssklower 		sin = mask;
29552196Smckusick 		sin.sin_family = AF_INET;
29659005Ssklower 		sin.sin_len = sizeof(sin);
29768653Smckusick 		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
29855082Storek 		    (struct sockaddr *)&nd->mygateway,
29959005Ssklower 		    (struct sockaddr *)&mask,
30068653Smckusick 		    RTF_UP | RTF_GATEWAY, (struct rtentry **)0);
30168653Smckusick 		if (error)
30255082Storek 			panic("nfs_mountroot: RTM_ADD: %d", error);
30346988Smckusick 	}
30446988Smckusick 
30568653Smckusick 	if (nd->swap_nblks) {
30668653Smckusick 		/*
30768653Smckusick 		 * Create a fake mount point just for the swap vnode so that the
30868653Smckusick 		 * swap file can be on a different server from the rootfs.
30968653Smckusick 		 */
31068653Smckusick 		nd->swap_args.fh = nd->swap_fh;
31168653Smckusick 		/*
31268653Smckusick 		 * If using nfsv3_diskless, replace NFSX_V2FH with
31368653Smckusick 		 * nd->swap_fhsize.
31468653Smckusick 		 */
31568653Smckusick 		nd->swap_args.fhsize = NFSX_V2FH;
31668653Smckusick 		l = ntohl(nd->swap_saddr.sin_addr.s_addr);
31768653Smckusick 		sprintf(buf,"%ld.%ld.%ld.%ld:%s",
31868653Smckusick 			(l >> 24) & 0xff, (l >> 16) & 0xff,
31968653Smckusick 			(l >>  8) & 0xff, (l >>  0) & 0xff,nd->swap_hostnam);
32068653Smckusick 		printf("NFS SWAP: %s\n",buf);
32168653Smckusick 		(void) nfs_mountdiskless(buf, "/swap", 0,
32255082Storek 		    &nd->swap_saddr, &nd->swap_args, &vp);
32368653Smckusick 
32468653Smckusick 		for (i=0;swdevt[i].sw_dev != NODEV;i++) ;
32568653Smckusick 
32646988Smckusick 		/*
32746988Smckusick 		 * Since the swap file is not the root dir of a file system,
32846988Smckusick 		 * hack it to a regular file.
32946988Smckusick 		 */
33046988Smckusick 		vp->v_type = VREG;
33146988Smckusick 		vp->v_flag = 0;
33246988Smckusick 		swapdev_vp = vp;
33346988Smckusick 		VREF(vp);
33468653Smckusick 		swdevt[i].sw_vp = vp;
33568653Smckusick 		swdevt[i].sw_nblks = nd->swap_nblks*2;
33646988Smckusick 
33768653Smckusick 		if (!swdevt[i].sw_nblks) {
33868653Smckusick 			swdevt[i].sw_nblks = 2048;
33968653Smckusick 			printf("defaulting to %d kbyte.\n",
34068653Smckusick 				swdevt[i].sw_nblks/2);
34168653Smckusick 		} else
34268653Smckusick 			printf("using %d kbyte.\n",swdevt[i].sw_nblks/2);
34368653Smckusick 	}
34468653Smckusick 
34546988Smckusick 	/*
34646988Smckusick 	 * Create the rootfs mount point.
34746988Smckusick 	 */
34868653Smckusick 	nd->root_args.fh = nd->root_fh;
34968653Smckusick 	/*
35068653Smckusick 	 * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize.
35168653Smckusick 	 */
35268653Smckusick 	nd->root_args.fhsize = NFSX_V2FH;
35368653Smckusick 	l = ntohl(nd->swap_saddr.sin_addr.s_addr);
35468653Smckusick 	sprintf(buf,"%ld.%ld.%ld.%ld:%s",
35568653Smckusick 		(l >> 24) & 0xff, (l >> 16) & 0xff,
35668653Smckusick 		(l >>  8) & 0xff, (l >>  0) & 0xff,nd->root_hostnam);
35768653Smckusick 	printf("NFS ROOT: %s\n",buf);
35868653Smckusick 	mp = nfs_mountdiskless(buf, "/", MNT_RDONLY,
35955082Storek 	    &nd->root_saddr, &nd->root_args, &vp);
36046988Smckusick 
36146988Smckusick 	if (vfs_lock(mp))
36255082Storek 		panic("nfs_mountroot: vfs_lock");
36369315Smckusick 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
36446988Smckusick 	mp->mnt_vnodecovered = NULLVP;
36546988Smckusick 	vfs_unlock(mp);
36646988Smckusick 	rootvp = vp;
36752445Smckusick 
36852445Smckusick 	/*
36952445Smckusick 	 * This is not really an nfs issue, but it is much easier to
37052445Smckusick 	 * set hostname here and then let the "/etc/rc.xxx" files
37152445Smckusick 	 * mount the right /var based upon its preset value.
37252445Smckusick 	 */
37355082Storek 	bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
37452445Smckusick 	hostname[MAXHOSTNAMELEN - 1] = '\0';
37552445Smckusick 	for (i = 0; i < MAXHOSTNAMELEN; i++)
37652445Smckusick 		if (hostname[i] == '\0')
37752445Smckusick 			break;
37852445Smckusick 	hostnamelen = i;
37957790Smckusick 	inittodr(ntohl(nd->root_time));
38046988Smckusick 	return (0);
38138414Smckusick }
38238414Smckusick 
38338414Smckusick /*
38455082Storek  * Internal version of mount system call for diskless setup.
38555082Storek  */
38655082Storek static struct mount *
38755082Storek nfs_mountdiskless(path, which, mountflag, sin, args, vpp)
38855082Storek 	char *path;
38955082Storek 	char *which;
39055082Storek 	int mountflag;
39155082Storek 	struct sockaddr_in *sin;
39255082Storek 	struct nfs_args *args;
39355082Storek 	register struct vnode **vpp;
39455082Storek {
39555082Storek 	register struct mount *mp;
39655082Storek 	register struct mbuf *m;
39755082Storek 	register int error;
39868789Smckusick 	struct vfsconf *vfsp;
39955082Storek 
40068789Smckusick 	for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
40168789Smckusick 		if (!strcmp(vfsp->vfc_name, "nfs"))
40268789Smckusick 			break;
40368789Smckusick 	if (vfsp == NULL)
40468789Smckusick 		panic("nfs_mountroot: NFS not configured");
40568789Smckusick 	mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
40665251Smckusick 	bzero((char *)mp, (u_long)sizeof(struct mount));
40768789Smckusick 	mp->mnt_vfc = vfsp;
40868789Smckusick 	mp->mnt_op = vfsp->vfc_vfsops;
40955082Storek 	mp->mnt_flag = mountflag;
41055082Storek 	MGET(m, MT_SONAME, M_DONTWAIT);
41155082Storek 	if (m == NULL)
41255082Storek 		panic("nfs_mountroot: %s mount mbuf", which);
41355082Storek 	bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
41455082Storek 	m->m_len = sin->sin_len;
41568789Smckusick 	if (error = mountnfs(args, mp, m, which, path, vpp))
41655082Storek 		panic("nfs_mountroot: mount %s on %s: %d", path, which, error);
41768789Smckusick 	vfsp->vfc_refcount++;
41868789Smckusick 	mp->mnt_stat.f_type = vfsp->vfc_typenum;
41968789Smckusick 	mp->mnt_flag |= (vfsp->vfc_flags & MNT_VISFLAGMASK) | MNT_ROOTFS;
42068789Smckusick 	strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
42155082Storek 
42255082Storek 	return (mp);
42355082Storek }
42455082Storek 
42552196Smckusick 
42652196Smckusick /*
42738414Smckusick  * VFS Operations.
42838414Smckusick  *
42938414Smckusick  * mount system call
43038414Smckusick  * It seems a bit dumb to copyinstr() the host and path here and then
43138414Smckusick  * bcopy() them in mountnfs(), but I wanted to detect errors before
43238414Smckusick  * doing the sockargs() call because sockargs() allocates an mbuf and
43338414Smckusick  * an error after that means that I have to release the mbuf.
43438414Smckusick  */
43539494Smckusick /* ARGSUSED */
43655082Storek int
43748055Smckusick nfs_mount(mp, path, data, ndp, p)
43838414Smckusick 	struct mount *mp;
43938414Smckusick 	char *path;
44038414Smckusick 	caddr_t data;
44138414Smckusick 	struct nameidata *ndp;
44248055Smckusick 	struct proc *p;
44338414Smckusick {
44438414Smckusick 	int error;
44538414Smckusick 	struct nfs_args args;
44641904Smckusick 	struct mbuf *nam;
44746988Smckusick 	struct vnode *vp;
44838414Smckusick 	char pth[MNAMELEN], hst[MNAMELEN];
44949108Skarels 	u_int len;
45068653Smckusick 	u_char nfh[NFSX_V3FHMAX];
45138414Smckusick 
45268653Smckusick 	error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
45368653Smckusick 	if (error)
45438414Smckusick 		return (error);
45569135Smckusick 	if (args.version != NFS_ARGSVERSION)
45669135Smckusick 		return (EPROGMISMATCH);
45768653Smckusick 	error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
45868653Smckusick 	if (error)
45938414Smckusick 		return (error);
46068653Smckusick 	error = copyinstr(path, pth, MNAMELEN-1, &len);
46168653Smckusick 	if (error)
46238414Smckusick 		return (error);
46349108Skarels 	bzero(&pth[len], MNAMELEN - len);
46468653Smckusick 	error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
46568653Smckusick 	if (error)
46638414Smckusick 		return (error);
46749108Skarels 	bzero(&hst[len], MNAMELEN - len);
46838414Smckusick 	/* sockargs() call must be after above copyin() calls */
46968653Smckusick 	error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME);
47068653Smckusick 	if (error)
47138414Smckusick 		return (error);
47268653Smckusick 	args.fh = nfh;
47346988Smckusick 	error = mountnfs(&args, mp, nam, pth, hst, &vp);
47438414Smckusick 	return (error);
47538414Smckusick }
47638414Smckusick 
47738414Smckusick /*
47838414Smckusick  * Common code for mount and mountroot
47938414Smckusick  */
48055082Storek int
48146988Smckusick mountnfs(argp, mp, nam, pth, hst, vpp)
48238414Smckusick 	register struct nfs_args *argp;
48338414Smckusick 	register struct mount *mp;
48441904Smckusick 	struct mbuf *nam;
48538414Smckusick 	char *pth, *hst;
48646988Smckusick 	struct vnode **vpp;
48738414Smckusick {
48838414Smckusick 	register struct nfsmount *nmp;
48940010Smckusick 	struct nfsnode *np;
49068653Smckusick 	int error, maxio;
49138414Smckusick 
49255082Storek 	if (mp->mnt_flag & MNT_UPDATE) {
49355082Storek 		nmp = VFSTONFS(mp);
49455082Storek 		/* update paths, file handles, etc, here	XXX */
49555082Storek 		m_freem(nam);
49655082Storek 		return (0);
49755082Storek 	} else {
49855082Storek 		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
49955082Storek 		    M_NFSMNT, M_WAITOK);
50055082Storek 		bzero((caddr_t)nmp, sizeof (struct nfsmount));
50168653Smckusick 		TAILQ_INIT(&nmp->nm_uidlruhead);
50255082Storek 		mp->mnt_data = (qaddr_t)nmp;
50355082Storek 	}
50468653Smckusick 	vfs_getnewfsid(mp);
50538414Smckusick 	nmp->nm_mountp = mp;
50638414Smckusick 	nmp->nm_flag = argp->flags;
50759759Smckusick 	if (nmp->nm_flag & NFSMNT_NQNFS)
50854985Smckusick 		/*
50954985Smckusick 		 * We have to set mnt_maxsymlink to a non-zero value so
51054985Smckusick 		 * that COMPAT_43 routines will know that we are setting
51154985Smckusick 		 * the d_type field in directories (and can zero it for
51254985Smckusick 		 * unsuspecting binaries).
51354985Smckusick 		 */
51454985Smckusick 		mp->mnt_maxsymlinklen = 1;
51552196Smckusick 	nmp->nm_timeo = NFS_TIMEO;
51640120Smckusick 	nmp->nm_retry = NFS_RETRANS;
51740120Smckusick 	nmp->nm_wsize = NFS_WSIZE;
51840120Smckusick 	nmp->nm_rsize = NFS_RSIZE;
51968653Smckusick 	nmp->nm_readdirsize = NFS_READDIRSIZE;
52052196Smckusick 	nmp->nm_numgrps = NFS_MAXGRPS;
52152196Smckusick 	nmp->nm_readahead = NFS_DEFRAHEAD;
52252196Smckusick 	nmp->nm_leaseterm = NQ_DEFLEASE;
52352196Smckusick 	nmp->nm_deadthresh = NQ_DEADTHRESH;
52467708Smckusick 	CIRCLEQ_INIT(&nmp->nm_timerhead);
52552196Smckusick 	nmp->nm_inprog = NULLVP;
52668653Smckusick 	nmp->nm_fhsize = argp->fhsize;
52768653Smckusick 	bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
52841398Smckusick 	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
52941398Smckusick 	bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
53041904Smckusick 	nmp->nm_nam = nam;
53140120Smckusick 
53240120Smckusick 	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
53352196Smckusick 		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
53452196Smckusick 		if (nmp->nm_timeo < NFS_MINTIMEO)
53552196Smckusick 			nmp->nm_timeo = NFS_MINTIMEO;
53652196Smckusick 		else if (nmp->nm_timeo > NFS_MAXTIMEO)
53752196Smckusick 			nmp->nm_timeo = NFS_MAXTIMEO;
53840120Smckusick 	}
53940120Smckusick 
54043355Smckusick 	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
54140120Smckusick 		nmp->nm_retry = argp->retrans;
54240120Smckusick 		if (nmp->nm_retry > NFS_MAXREXMIT)
54340120Smckusick 			nmp->nm_retry = NFS_MAXREXMIT;
54440120Smckusick 	}
54540120Smckusick 
54668653Smckusick 	if (argp->flags & NFSMNT_NFSV3) {
54768653Smckusick 		if (argp->sotype == SOCK_DGRAM)
54868653Smckusick 			maxio = NFS_MAXDGRAMDATA;
54968653Smckusick 		else
55068653Smckusick 			maxio = NFS_MAXDATA;
55168653Smckusick 	} else
55268653Smckusick 		maxio = NFS_V2MAXDATA;
55368653Smckusick 
55440120Smckusick 	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
55538414Smckusick 		nmp->nm_wsize = argp->wsize;
55640120Smckusick 		/* Round down to multiple of blocksize */
55768653Smckusick 		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
55840120Smckusick 		if (nmp->nm_wsize <= 0)
55968653Smckusick 			nmp->nm_wsize = NFS_FABLKSIZE;
56040120Smckusick 	}
56168653Smckusick 	if (nmp->nm_wsize > maxio)
56268653Smckusick 		nmp->nm_wsize = maxio;
56343355Smckusick 	if (nmp->nm_wsize > MAXBSIZE)
56443355Smckusick 		nmp->nm_wsize = MAXBSIZE;
56540120Smckusick 
56640120Smckusick 	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
56738414Smckusick 		nmp->nm_rsize = argp->rsize;
56840120Smckusick 		/* Round down to multiple of blocksize */
56968653Smckusick 		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
57040120Smckusick 		if (nmp->nm_rsize <= 0)
57168653Smckusick 			nmp->nm_rsize = NFS_FABLKSIZE;
57240120Smckusick 	}
57368653Smckusick 	if (nmp->nm_rsize > maxio)
57468653Smckusick 		nmp->nm_rsize = maxio;
57543355Smckusick 	if (nmp->nm_rsize > MAXBSIZE)
57643355Smckusick 		nmp->nm_rsize = MAXBSIZE;
57768653Smckusick 
57868653Smckusick 	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
57968653Smckusick 		nmp->nm_readdirsize = argp->readdirsize;
58068653Smckusick 		/* Round down to multiple of blocksize */
58168653Smckusick 		nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1);
58268653Smckusick 		if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
58368653Smckusick 			nmp->nm_readdirsize = NFS_DIRBLKSIZ;
58468653Smckusick 	}
58568653Smckusick 	if (nmp->nm_readdirsize > maxio)
58668653Smckusick 		nmp->nm_readdirsize = maxio;
58768653Smckusick 
58852196Smckusick 	if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
58952196Smckusick 		argp->maxgrouplist <= NFS_MAXGRPS)
59052196Smckusick 		nmp->nm_numgrps = argp->maxgrouplist;
59152196Smckusick 	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
59252196Smckusick 		argp->readahead <= NFS_MAXRAHEAD)
59352196Smckusick 		nmp->nm_readahead = argp->readahead;
59452196Smckusick 	if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 &&
59552196Smckusick 		argp->leaseterm <= NQ_MAXLEASE)
59652196Smckusick 		nmp->nm_leaseterm = argp->leaseterm;
59752196Smckusick 	if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 &&
59852196Smckusick 		argp->deadthresh <= NQ_NEVERDEAD)
59952196Smckusick 		nmp->nm_deadthresh = argp->deadthresh;
60040120Smckusick 	/* Set up the sockets and per-host congestion */
60141904Smckusick 	nmp->nm_sotype = argp->sotype;
60241904Smckusick 	nmp->nm_soproto = argp->proto;
60352196Smckusick 
60452196Smckusick 	/*
60552196Smckusick 	 * For Connection based sockets (TCP,...) defer the connect until
60652196Smckusick 	 * the first request, in case the server is not responding.
60752196Smckusick 	 */
60852196Smckusick 	if (nmp->nm_sotype == SOCK_DGRAM &&
60952196Smckusick 		(error = nfs_connect(nmp, (struct nfsreq *)0)))
61040120Smckusick 		goto bad;
61140120Smckusick 
61238414Smckusick 	/*
61352196Smckusick 	 * This is silly, but it has to be set so that vinifod() works.
61452196Smckusick 	 * We do not want to do an nfs_statfs() here since we can get
61552196Smckusick 	 * stuck on a dead server and we are holding a lock on the mount
61652196Smckusick 	 * point.
61752196Smckusick 	 */
61852196Smckusick 	mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA;
61952196Smckusick 	/*
62040010Smckusick 	 * A reference count is needed on the nfsnode representing the
62140010Smckusick 	 * remote root.  If this object is not persistent, then backward
62240010Smckusick 	 * traversals of the mount point (i.e. "..") will not work if
62340010Smckusick 	 * the nfsnode gets flushed out of the cache. Ufs does not have
62440010Smckusick 	 * this problem, because one can identify root inodes by their
62540010Smckusick 	 * number == ROOTINO (2).
62640010Smckusick 	 */
62768653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
62868653Smckusick 	if (error)
62940010Smckusick 		goto bad;
63046988Smckusick 	*vpp = NFSTOV(np);
63141904Smckusick 
63240353Smckusick 	return (0);
63338414Smckusick bad:
63440120Smckusick 	nfs_disconnect(nmp);
63552196Smckusick 	free((caddr_t)nmp, M_NFSMNT);
63641904Smckusick 	m_freem(nam);
63738414Smckusick 	return (error);
63838414Smckusick }
63938414Smckusick 
64038414Smckusick /*
64138414Smckusick  * unmount system call
64238414Smckusick  */
64355082Storek int
64448055Smckusick nfs_unmount(mp, mntflags, p)
64538414Smckusick 	struct mount *mp;
64641294Smckusick 	int mntflags;
64748055Smckusick 	struct proc *p;
64838414Smckusick {
64938414Smckusick 	register struct nfsmount *nmp;
65040010Smckusick 	struct nfsnode *np;
65140120Smckusick 	struct vnode *vp;
65248361Smckusick 	int error, flags = 0;
65338414Smckusick 
654*69346Smckusick 	if (mntflags & MNT_FORCE)
65541294Smckusick 		flags |= FORCECLOSE;
65641398Smckusick 	nmp = VFSTONFS(mp);
65738414Smckusick 	/*
65838414Smckusick 	 * Goes something like this..
65940120Smckusick 	 * - Check for activity on the root vnode (other than ourselves).
66040120Smckusick 	 * - Call vflush() to clear out vnodes for this file system,
66140120Smckusick 	 *   except for the root vnode.
66240120Smckusick 	 * - Decrement reference on the vnode representing remote root.
66338414Smckusick 	 * - Close the socket
66438414Smckusick 	 * - Free up the data structures
66538414Smckusick 	 */
66640010Smckusick 	/*
66740010Smckusick 	 * We need to decrement the ref. count on the nfsnode representing
66840010Smckusick 	 * the remote root.  See comment in mountnfs().  The VFS unmount()
66940010Smckusick 	 * has done vput on this vnode, otherwise we would get deadlock!
67040010Smckusick 	 */
67168653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
67268653Smckusick 	if (error)
67340010Smckusick 		return(error);
67440120Smckusick 	vp = NFSTOV(np);
67540120Smckusick 	if (vp->v_usecount > 2) {
67640120Smckusick 		vput(vp);
67740120Smckusick 		return (EBUSY);
67840120Smckusick 	}
67952196Smckusick 
68052196Smckusick 	/*
68152196Smckusick 	 * Must handshake with nqnfs_clientd() if it is active.
68252196Smckusick 	 */
68352196Smckusick 	nmp->nm_flag |= NFSMNT_DISMINPROG;
68452196Smckusick 	while (nmp->nm_inprog != NULLVP)
68552196Smckusick 		(void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
68668653Smckusick 	error = vflush(mp, vp, flags);
68768653Smckusick 	if (error) {
68840120Smckusick 		vput(vp);
68952196Smckusick 		nmp->nm_flag &= ~NFSMNT_DISMINPROG;
69040120Smckusick 		return (error);
69140120Smckusick 	}
69252196Smckusick 
69340010Smckusick 	/*
69452196Smckusick 	 * We are now committed to the unmount.
69552196Smckusick 	 * For NQNFS, let the server daemon free the nfsmount structure.
69640010Smckusick 	 */
69752196Smckusick 	if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
69852196Smckusick 		nmp->nm_flag |= NFSMNT_DISMNT;
69952196Smckusick 
70052196Smckusick 	/*
70152196Smckusick 	 * There are two reference counts to get rid of here.
70252196Smckusick 	 */
70340120Smckusick 	vrele(vp);
70452196Smckusick 	vrele(vp);
70552288Smckusick 	vgone(vp);
70640120Smckusick 	nfs_disconnect(nmp);
70741904Smckusick 	m_freem(nmp->nm_nam);
70852196Smckusick 
70952196Smckusick 	if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0)
71052196Smckusick 		free((caddr_t)nmp, M_NFSMNT);
71138414Smckusick 	return (0);
71238414Smckusick }
71338414Smckusick 
71438414Smckusick /*
71538414Smckusick  * Return root of a filesystem
71638414Smckusick  */
71755082Storek int
71838414Smckusick nfs_root(mp, vpp)
71938414Smckusick 	struct mount *mp;
72038414Smckusick 	struct vnode **vpp;
72138414Smckusick {
72238414Smckusick 	register struct vnode *vp;
72338414Smckusick 	struct nfsmount *nmp;
72438414Smckusick 	struct nfsnode *np;
72538414Smckusick 	int error;
72638414Smckusick 
72741398Smckusick 	nmp = VFSTONFS(mp);
72868653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
72968653Smckusick 	if (error)
73038414Smckusick 		return (error);
73138414Smckusick 	vp = NFSTOV(np);
73238414Smckusick 	vp->v_type = VDIR;
73338414Smckusick 	vp->v_flag = VROOT;
73438414Smckusick 	*vpp = vp;
73538414Smckusick 	return (0);
73638414Smckusick }
73738414Smckusick 
73838884Smacklem extern int syncprt;
73938884Smacklem 
74038414Smckusick /*
74138884Smacklem  * Flush out the buffer cache
74238414Smckusick  */
74339494Smckusick /* ARGSUSED */
74455082Storek int
74554450Smckusick nfs_sync(mp, waitfor, cred, p)
74638414Smckusick 	struct mount *mp;
74738414Smckusick 	int waitfor;
74854450Smckusick 	struct ucred *cred;
74954450Smckusick 	struct proc *p;
75038414Smckusick {
75154450Smckusick 	register struct vnode *vp;
75254450Smckusick 	int error, allerror = 0;
75354450Smckusick 
75438884Smacklem 	/*
75538884Smacklem 	 * Force stale buffer cache information to be flushed.
75638884Smacklem 	 */
75754450Smckusick loop:
75865251Smckusick 	for (vp = mp->mnt_vnodelist.lh_first;
75965251Smckusick 	     vp != NULL;
76065251Smckusick 	     vp = vp->v_mntvnodes.le_next) {
76154450Smckusick 		/*
76254450Smckusick 		 * If the vnode that we are about to sync is no longer
76354450Smckusick 		 * associated with this mount point, start over.
76454450Smckusick 		 */
76554450Smckusick 		if (vp->v_mount != mp)
76654450Smckusick 			goto loop;
76765251Smckusick 		if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
76854450Smckusick 			continue;
76965251Smckusick 		if (vget(vp, 1))
77054450Smckusick 			goto loop;
77168653Smckusick 		error = VOP_FSYNC(vp, cred, waitfor, p);
77268653Smckusick 		if (error)
77354450Smckusick 			allerror = error;
77454450Smckusick 		vput(vp);
77554450Smckusick 	}
77654450Smckusick 	return (allerror);
77738414Smckusick }
77838414Smckusick 
77938414Smckusick /*
78054667Smckusick  * NFS flat namespace lookup.
78154667Smckusick  * Currently unsupported.
78254667Smckusick  */
78354667Smckusick /* ARGSUSED */
78454667Smckusick int
78554667Smckusick nfs_vget(mp, ino, vpp)
78654667Smckusick 	struct mount *mp;
78754667Smckusick 	ino_t ino;
78854667Smckusick 	struct vnode **vpp;
78954667Smckusick {
79054667Smckusick 
79154667Smckusick 	return (EOPNOTSUPP);
79254667Smckusick }
79354667Smckusick 
79454667Smckusick /*
79538414Smckusick  * At this point, this should never happen
79638414Smckusick  */
79739494Smckusick /* ARGSUSED */
79855082Storek int
79954737Smckusick nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
80054737Smckusick 	register struct mount *mp;
80138414Smckusick 	struct fid *fhp;
80254737Smckusick 	struct mbuf *nam;
80338414Smckusick 	struct vnode **vpp;
80454737Smckusick 	int *exflagsp;
80554737Smckusick 	struct ucred **credanonp;
80638414Smckusick {
80739494Smckusick 
80838414Smckusick 	return (EINVAL);
80938414Smckusick }
81038414Smckusick 
81138414Smckusick /*
81238414Smckusick  * Vnode pointer to File handle, should never happen either
81338414Smckusick  */
81439494Smckusick /* ARGSUSED */
81555082Storek int
81648055Smckusick nfs_vptofh(vp, fhp)
81748055Smckusick 	struct vnode *vp;
81838414Smckusick 	struct fid *fhp;
81938414Smckusick {
82039494Smckusick 
82138414Smckusick 	return (EINVAL);
82238414Smckusick }
82338884Smacklem 
82438884Smacklem /*
82538884Smacklem  * Vfs start routine, a no-op.
82638884Smacklem  */
82739494Smckusick /* ARGSUSED */
82855082Storek int
82948055Smckusick nfs_start(mp, flags, p)
83038884Smacklem 	struct mount *mp;
83138884Smacklem 	int flags;
83248055Smckusick 	struct proc *p;
83338884Smacklem {
83439494Smckusick 
83538884Smacklem 	return (0);
83638884Smacklem }
83741294Smckusick 
83841294Smckusick /*
83941294Smckusick  * Do operations associated with quotas, not supported
84041294Smckusick  */
84151574Smckusick /* ARGSUSED */
84255082Storek int
84348055Smckusick nfs_quotactl(mp, cmd, uid, arg, p)
84441294Smckusick 	struct mount *mp;
84541294Smckusick 	int cmd;
84654450Smckusick 	uid_t uid;
84741294Smckusick 	caddr_t arg;
84848055Smckusick 	struct proc *p;
84941294Smckusick {
85051574Smckusick 
85141294Smckusick 	return (EOPNOTSUPP);
85241294Smckusick }
85368653Smckusick 
85468653Smckusick /*
85568653Smckusick  * Do that sysctl thang...
85668653Smckusick  */
85768653Smckusick static int
85868653Smckusick nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
85968653Smckusick 	   size_t newlen, struct proc *p)
86068653Smckusick {
86168653Smckusick 	int rv;
86268653Smckusick 
86368653Smckusick 	/*
86468653Smckusick 	 * All names at this level are terminal.
86568653Smckusick 	 */
86668653Smckusick 	if(namelen > 1)
86768653Smckusick 		return ENOTDIR;	/* overloaded */
86868653Smckusick 
86968653Smckusick 	switch(name[0]) {
87068653Smckusick 	case NFS_NFSSTATS:
87168653Smckusick 		if(!oldp) {
87268653Smckusick 			*oldlenp = sizeof nfsstats;
87368653Smckusick 			return 0;
87468653Smckusick 		}
87568653Smckusick 
87668653Smckusick 		if(*oldlenp < sizeof nfsstats) {
87768653Smckusick 			*oldlenp = sizeof nfsstats;
87868653Smckusick 			return ENOMEM;
87968653Smckusick 		}
88068653Smckusick 
88168653Smckusick 		rv = copyout(&nfsstats, oldp, sizeof nfsstats);
88268653Smckusick 		if(rv) return rv;
88368653Smckusick 
88468653Smckusick 		if(newp && newlen != sizeof nfsstats)
88568653Smckusick 			return EINVAL;
88668653Smckusick 
88768653Smckusick 		if(newp) {
88868653Smckusick 			return copyin(newp, &nfsstats, sizeof nfsstats);
88968653Smckusick 		}
89068653Smckusick 		return 0;
89168653Smckusick 
89268653Smckusick 	default:
89368653Smckusick 		return EOPNOTSUPP;
89468653Smckusick 	}
89568653Smckusick }
89668653Smckusick 
897