xref: /csrg-svn/sys/nfs/nfs_vfsops.c (revision 69424)
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*69424Smckusick  *	@(#)nfs_vfsops.c	8.11 (Berkeley) 05/14/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 *));
7769368Smckusick static int nfs_mountdiskless __P((char *, char *, int, struct sockaddr_in *,
7869368Smckusick 	struct nfs_args *, struct vnode **, struct mount **));
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 {
22869368Smckusick 	struct mount *mp;
22969368Smckusick 	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);
26569368Smckusick 	if (error) {
26669368Smckusick 		printf("nfs_mountroot: socreate(%04x): %d",
26768653Smckusick 			nd->myif.ifra_addr.sa_family, error);
26869368Smckusick 		return (error);
26969368Smckusick 	}
27068653Smckusick 
27168653Smckusick 	/*
27268653Smckusick 	 * We might not have been told the right interface, so we pass
27368653Smckusick 	 * over the first ten interfaces of the same kind, until we get
27468653Smckusick 	 * one of them configured.
27568653Smckusick 	 */
27668653Smckusick 
27768653Smckusick 	for (i = strlen(nd->myif.ifra_name) - 1;
27868653Smckusick 		nd->myif.ifra_name[i] >= '0' &&
27968653Smckusick 		nd->myif.ifra_name[i] <= '9';
28068653Smckusick 		nd->myif.ifra_name[i] ++) {
28168653Smckusick 		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p);
28268653Smckusick 		if(!error)
28368653Smckusick 			break;
28468653Smckusick 	}
28569368Smckusick 	if (error) {
28669368Smckusick 		printf("nfs_mountroot: SIOCAIFADDR: %d", error);
28769368Smckusick 		return (error);
28869368Smckusick 	}
28946988Smckusick 	soclose(so);
29046988Smckusick 
29146988Smckusick 	/*
29246988Smckusick 	 * If the gateway field is filled in, set it as the default route.
29346988Smckusick 	 */
29455082Storek 	if (nd->mygateway.sin_len != 0) {
29559005Ssklower 		struct sockaddr_in mask, sin;
29646988Smckusick 
29759005Ssklower 		bzero((caddr_t)&mask, sizeof(mask));
29859005Ssklower 		sin = mask;
29952196Smckusick 		sin.sin_family = AF_INET;
30059005Ssklower 		sin.sin_len = sizeof(sin);
30168653Smckusick 		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
30255082Storek 		    (struct sockaddr *)&nd->mygateway,
30359005Ssklower 		    (struct sockaddr *)&mask,
30468653Smckusick 		    RTF_UP | RTF_GATEWAY, (struct rtentry **)0);
30569368Smckusick 		if (error) {
30669368Smckusick 			printf("nfs_mountroot: RTM_ADD: %d", error);
30769368Smckusick 			return (error);
30869368Smckusick 		}
30946988Smckusick 	}
31046988Smckusick 
31168653Smckusick 	if (nd->swap_nblks) {
31268653Smckusick 		/*
31368653Smckusick 		 * Create a fake mount point just for the swap vnode so that the
31468653Smckusick 		 * swap file can be on a different server from the rootfs.
31568653Smckusick 		 */
31668653Smckusick 		nd->swap_args.fh = nd->swap_fh;
31768653Smckusick 		/*
31868653Smckusick 		 * If using nfsv3_diskless, replace NFSX_V2FH with
31968653Smckusick 		 * nd->swap_fhsize.
32068653Smckusick 		 */
32168653Smckusick 		nd->swap_args.fhsize = NFSX_V2FH;
32268653Smckusick 		l = ntohl(nd->swap_saddr.sin_addr.s_addr);
32368653Smckusick 		sprintf(buf,"%ld.%ld.%ld.%ld:%s",
32468653Smckusick 			(l >> 24) & 0xff, (l >> 16) & 0xff,
32568653Smckusick 			(l >>  8) & 0xff, (l >>  0) & 0xff,nd->swap_hostnam);
32668653Smckusick 		printf("NFS SWAP: %s\n",buf);
32769368Smckusick 		if (error = nfs_mountdiskless(buf, "/swap", 0,
32869368Smckusick 		    &nd->swap_saddr, &nd->swap_args, &vp, &mp))
32969368Smckusick 			return (error);
33068653Smckusick 
33168653Smckusick 		for (i=0;swdevt[i].sw_dev != NODEV;i++) ;
33268653Smckusick 
33346988Smckusick 		/*
33446988Smckusick 		 * Since the swap file is not the root dir of a file system,
33546988Smckusick 		 * hack it to a regular file.
33646988Smckusick 		 */
33746988Smckusick 		vp->v_type = VREG;
33846988Smckusick 		vp->v_flag = 0;
33946988Smckusick 		swapdev_vp = vp;
34046988Smckusick 		VREF(vp);
34168653Smckusick 		swdevt[i].sw_vp = vp;
34268653Smckusick 		swdevt[i].sw_nblks = nd->swap_nblks*2;
34346988Smckusick 
34468653Smckusick 		if (!swdevt[i].sw_nblks) {
34568653Smckusick 			swdevt[i].sw_nblks = 2048;
34668653Smckusick 			printf("defaulting to %d kbyte.\n",
34768653Smckusick 				swdevt[i].sw_nblks/2);
34868653Smckusick 		} else
34968653Smckusick 			printf("using %d kbyte.\n",swdevt[i].sw_nblks/2);
35068653Smckusick 	}
35168653Smckusick 
35246988Smckusick 	/*
35346988Smckusick 	 * Create the rootfs mount point.
35446988Smckusick 	 */
35568653Smckusick 	nd->root_args.fh = nd->root_fh;
35668653Smckusick 	/*
35768653Smckusick 	 * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize.
35868653Smckusick 	 */
35968653Smckusick 	nd->root_args.fhsize = NFSX_V2FH;
36068653Smckusick 	l = ntohl(nd->swap_saddr.sin_addr.s_addr);
36168653Smckusick 	sprintf(buf,"%ld.%ld.%ld.%ld:%s",
36268653Smckusick 		(l >> 24) & 0xff, (l >> 16) & 0xff,
36368653Smckusick 		(l >>  8) & 0xff, (l >>  0) & 0xff,nd->root_hostnam);
36468653Smckusick 	printf("NFS ROOT: %s\n",buf);
36569368Smckusick 	if (error = nfs_mountdiskless(buf, "/", MNT_RDONLY,
36669368Smckusick 	    &nd->root_saddr, &nd->root_args, &vp, &mp))
36769368Smckusick 		return (error);
36846988Smckusick 
36969368Smckusick 	if (error = vfs_lock(mp)) {
37069368Smckusick 		printf("nfs_mountroot: vfs_lock");
37169368Smckusick 		return (error);
37269368Smckusick 	}
37369315Smckusick 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
37446988Smckusick 	vfs_unlock(mp);
37546988Smckusick 	rootvp = vp;
37652445Smckusick 
37752445Smckusick 	/*
37852445Smckusick 	 * This is not really an nfs issue, but it is much easier to
37952445Smckusick 	 * set hostname here and then let the "/etc/rc.xxx" files
38052445Smckusick 	 * mount the right /var based upon its preset value.
38152445Smckusick 	 */
38255082Storek 	bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
38352445Smckusick 	hostname[MAXHOSTNAMELEN - 1] = '\0';
38452445Smckusick 	for (i = 0; i < MAXHOSTNAMELEN; i++)
38552445Smckusick 		if (hostname[i] == '\0')
38652445Smckusick 			break;
38752445Smckusick 	hostnamelen = i;
38857790Smckusick 	inittodr(ntohl(nd->root_time));
38946988Smckusick 	return (0);
39038414Smckusick }
39138414Smckusick 
39238414Smckusick /*
39355082Storek  * Internal version of mount system call for diskless setup.
39455082Storek  */
39569368Smckusick static int
39669368Smckusick nfs_mountdiskless(path, which, mountflag, sin, args, vpp, mpp)
39755082Storek 	char *path;
39855082Storek 	char *which;
39955082Storek 	int mountflag;
40055082Storek 	struct sockaddr_in *sin;
40155082Storek 	struct nfs_args *args;
40269368Smckusick 	struct vnode **vpp;
40369368Smckusick 	struct mount **mpp;
40455082Storek {
40569368Smckusick 	struct mount *mp;
40669368Smckusick 	struct mbuf *m;
40769368Smckusick 	int error;
40855082Storek 
40969368Smckusick 	if (error = vfs_rootmountalloc("nfs", path, &mp)) {
41069368Smckusick 		printf("nfs_mountroot: NFS not configured");
41169368Smckusick 		return (error);
41269368Smckusick 	}
41355082Storek 	mp->mnt_flag = mountflag;
41455082Storek 	MGET(m, MT_SONAME, M_DONTWAIT);
41569368Smckusick 	if (m == NULL) {
41669368Smckusick 		printf("nfs_mountroot: %s mount mbuf", which);
41769368Smckusick 		return (error);
41869368Smckusick 	}
41955082Storek 	bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
42055082Storek 	m->m_len = sin->sin_len;
42169368Smckusick 	if (error = mountnfs(args, mp, m, which, path, vpp)) {
42269368Smckusick 		printf("nfs_mountroot: mount %s on %s: %d", path, which, error);
42369368Smckusick 		return (error);
42469368Smckusick 	}
42569368Smckusick 	(void) copystr(which, mp->mnt_stat.f_mntonname, MNAMELEN - 1, 0);
42669368Smckusick 	*mpp = mp;
42769368Smckusick 	return (0);
42855082Storek }
42955082Storek 
43052196Smckusick /*
43138414Smckusick  * VFS Operations.
43238414Smckusick  *
43338414Smckusick  * mount system call
43438414Smckusick  * It seems a bit dumb to copyinstr() the host and path here and then
43538414Smckusick  * bcopy() them in mountnfs(), but I wanted to detect errors before
43638414Smckusick  * doing the sockargs() call because sockargs() allocates an mbuf and
43738414Smckusick  * an error after that means that I have to release the mbuf.
43838414Smckusick  */
43939494Smckusick /* ARGSUSED */
44055082Storek int
44148055Smckusick nfs_mount(mp, path, data, ndp, p)
44238414Smckusick 	struct mount *mp;
44338414Smckusick 	char *path;
44438414Smckusick 	caddr_t data;
44538414Smckusick 	struct nameidata *ndp;
44648055Smckusick 	struct proc *p;
44738414Smckusick {
44838414Smckusick 	int error;
44938414Smckusick 	struct nfs_args args;
45041904Smckusick 	struct mbuf *nam;
45146988Smckusick 	struct vnode *vp;
45238414Smckusick 	char pth[MNAMELEN], hst[MNAMELEN];
45349108Skarels 	u_int len;
45468653Smckusick 	u_char nfh[NFSX_V3FHMAX];
45538414Smckusick 
45668653Smckusick 	error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
45768653Smckusick 	if (error)
45838414Smckusick 		return (error);
45969135Smckusick 	if (args.version != NFS_ARGSVERSION)
46069135Smckusick 		return (EPROGMISMATCH);
46168653Smckusick 	error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
46268653Smckusick 	if (error)
46338414Smckusick 		return (error);
46468653Smckusick 	error = copyinstr(path, pth, MNAMELEN-1, &len);
46568653Smckusick 	if (error)
46638414Smckusick 		return (error);
46749108Skarels 	bzero(&pth[len], MNAMELEN - len);
46868653Smckusick 	error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
46968653Smckusick 	if (error)
47038414Smckusick 		return (error);
47149108Skarels 	bzero(&hst[len], MNAMELEN - len);
47238414Smckusick 	/* sockargs() call must be after above copyin() calls */
47368653Smckusick 	error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME);
47468653Smckusick 	if (error)
47538414Smckusick 		return (error);
47668653Smckusick 	args.fh = nfh;
47746988Smckusick 	error = mountnfs(&args, mp, nam, pth, hst, &vp);
47838414Smckusick 	return (error);
47938414Smckusick }
48038414Smckusick 
48138414Smckusick /*
48238414Smckusick  * Common code for mount and mountroot
48338414Smckusick  */
48455082Storek int
48546988Smckusick mountnfs(argp, mp, nam, pth, hst, vpp)
48638414Smckusick 	register struct nfs_args *argp;
48738414Smckusick 	register struct mount *mp;
48841904Smckusick 	struct mbuf *nam;
48938414Smckusick 	char *pth, *hst;
49046988Smckusick 	struct vnode **vpp;
49138414Smckusick {
49238414Smckusick 	register struct nfsmount *nmp;
49340010Smckusick 	struct nfsnode *np;
49468653Smckusick 	int error, maxio;
49538414Smckusick 
49655082Storek 	if (mp->mnt_flag & MNT_UPDATE) {
49755082Storek 		nmp = VFSTONFS(mp);
49855082Storek 		/* update paths, file handles, etc, here	XXX */
49955082Storek 		m_freem(nam);
50055082Storek 		return (0);
50155082Storek 	} else {
50255082Storek 		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
50355082Storek 		    M_NFSMNT, M_WAITOK);
50455082Storek 		bzero((caddr_t)nmp, sizeof (struct nfsmount));
50568653Smckusick 		TAILQ_INIT(&nmp->nm_uidlruhead);
50655082Storek 		mp->mnt_data = (qaddr_t)nmp;
50755082Storek 	}
50868653Smckusick 	vfs_getnewfsid(mp);
50938414Smckusick 	nmp->nm_mountp = mp;
51038414Smckusick 	nmp->nm_flag = argp->flags;
51159759Smckusick 	if (nmp->nm_flag & NFSMNT_NQNFS)
51254985Smckusick 		/*
51354985Smckusick 		 * We have to set mnt_maxsymlink to a non-zero value so
51454985Smckusick 		 * that COMPAT_43 routines will know that we are setting
51554985Smckusick 		 * the d_type field in directories (and can zero it for
51654985Smckusick 		 * unsuspecting binaries).
51754985Smckusick 		 */
51854985Smckusick 		mp->mnt_maxsymlinklen = 1;
51952196Smckusick 	nmp->nm_timeo = NFS_TIMEO;
52040120Smckusick 	nmp->nm_retry = NFS_RETRANS;
52140120Smckusick 	nmp->nm_wsize = NFS_WSIZE;
52240120Smckusick 	nmp->nm_rsize = NFS_RSIZE;
52368653Smckusick 	nmp->nm_readdirsize = NFS_READDIRSIZE;
52452196Smckusick 	nmp->nm_numgrps = NFS_MAXGRPS;
52552196Smckusick 	nmp->nm_readahead = NFS_DEFRAHEAD;
52652196Smckusick 	nmp->nm_leaseterm = NQ_DEFLEASE;
52752196Smckusick 	nmp->nm_deadthresh = NQ_DEADTHRESH;
52867708Smckusick 	CIRCLEQ_INIT(&nmp->nm_timerhead);
52952196Smckusick 	nmp->nm_inprog = NULLVP;
53068653Smckusick 	nmp->nm_fhsize = argp->fhsize;
53168653Smckusick 	bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
53241398Smckusick 	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
53341398Smckusick 	bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
53441904Smckusick 	nmp->nm_nam = nam;
53540120Smckusick 
53640120Smckusick 	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
53752196Smckusick 		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
53852196Smckusick 		if (nmp->nm_timeo < NFS_MINTIMEO)
53952196Smckusick 			nmp->nm_timeo = NFS_MINTIMEO;
54052196Smckusick 		else if (nmp->nm_timeo > NFS_MAXTIMEO)
54152196Smckusick 			nmp->nm_timeo = NFS_MAXTIMEO;
54240120Smckusick 	}
54340120Smckusick 
54443355Smckusick 	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
54540120Smckusick 		nmp->nm_retry = argp->retrans;
54640120Smckusick 		if (nmp->nm_retry > NFS_MAXREXMIT)
54740120Smckusick 			nmp->nm_retry = NFS_MAXREXMIT;
54840120Smckusick 	}
54940120Smckusick 
55068653Smckusick 	if (argp->flags & NFSMNT_NFSV3) {
55168653Smckusick 		if (argp->sotype == SOCK_DGRAM)
55268653Smckusick 			maxio = NFS_MAXDGRAMDATA;
55368653Smckusick 		else
55468653Smckusick 			maxio = NFS_MAXDATA;
55568653Smckusick 	} else
55668653Smckusick 		maxio = NFS_V2MAXDATA;
55768653Smckusick 
55840120Smckusick 	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
55938414Smckusick 		nmp->nm_wsize = argp->wsize;
56040120Smckusick 		/* Round down to multiple of blocksize */
56168653Smckusick 		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
56240120Smckusick 		if (nmp->nm_wsize <= 0)
56368653Smckusick 			nmp->nm_wsize = NFS_FABLKSIZE;
56440120Smckusick 	}
56568653Smckusick 	if (nmp->nm_wsize > maxio)
56668653Smckusick 		nmp->nm_wsize = maxio;
56743355Smckusick 	if (nmp->nm_wsize > MAXBSIZE)
56843355Smckusick 		nmp->nm_wsize = MAXBSIZE;
56940120Smckusick 
57040120Smckusick 	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
57138414Smckusick 		nmp->nm_rsize = argp->rsize;
57240120Smckusick 		/* Round down to multiple of blocksize */
57368653Smckusick 		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
57440120Smckusick 		if (nmp->nm_rsize <= 0)
57568653Smckusick 			nmp->nm_rsize = NFS_FABLKSIZE;
57640120Smckusick 	}
57768653Smckusick 	if (nmp->nm_rsize > maxio)
57868653Smckusick 		nmp->nm_rsize = maxio;
57943355Smckusick 	if (nmp->nm_rsize > MAXBSIZE)
58043355Smckusick 		nmp->nm_rsize = MAXBSIZE;
58168653Smckusick 
58268653Smckusick 	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
58368653Smckusick 		nmp->nm_readdirsize = argp->readdirsize;
58468653Smckusick 		/* Round down to multiple of blocksize */
58568653Smckusick 		nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1);
58668653Smckusick 		if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
58768653Smckusick 			nmp->nm_readdirsize = NFS_DIRBLKSIZ;
58868653Smckusick 	}
58968653Smckusick 	if (nmp->nm_readdirsize > maxio)
59068653Smckusick 		nmp->nm_readdirsize = maxio;
59168653Smckusick 
59252196Smckusick 	if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
59352196Smckusick 		argp->maxgrouplist <= NFS_MAXGRPS)
59452196Smckusick 		nmp->nm_numgrps = argp->maxgrouplist;
59552196Smckusick 	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
59652196Smckusick 		argp->readahead <= NFS_MAXRAHEAD)
59752196Smckusick 		nmp->nm_readahead = argp->readahead;
59852196Smckusick 	if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 &&
59952196Smckusick 		argp->leaseterm <= NQ_MAXLEASE)
60052196Smckusick 		nmp->nm_leaseterm = argp->leaseterm;
60152196Smckusick 	if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 &&
60252196Smckusick 		argp->deadthresh <= NQ_NEVERDEAD)
60352196Smckusick 		nmp->nm_deadthresh = argp->deadthresh;
60440120Smckusick 	/* Set up the sockets and per-host congestion */
60541904Smckusick 	nmp->nm_sotype = argp->sotype;
60641904Smckusick 	nmp->nm_soproto = argp->proto;
60752196Smckusick 
60852196Smckusick 	/*
60952196Smckusick 	 * For Connection based sockets (TCP,...) defer the connect until
61052196Smckusick 	 * the first request, in case the server is not responding.
61152196Smckusick 	 */
61252196Smckusick 	if (nmp->nm_sotype == SOCK_DGRAM &&
61352196Smckusick 		(error = nfs_connect(nmp, (struct nfsreq *)0)))
61440120Smckusick 		goto bad;
61540120Smckusick 
61638414Smckusick 	/*
61752196Smckusick 	 * This is silly, but it has to be set so that vinifod() works.
61852196Smckusick 	 * We do not want to do an nfs_statfs() here since we can get
61952196Smckusick 	 * stuck on a dead server and we are holding a lock on the mount
62052196Smckusick 	 * point.
62152196Smckusick 	 */
62252196Smckusick 	mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA;
62352196Smckusick 	/*
62440010Smckusick 	 * A reference count is needed on the nfsnode representing the
62540010Smckusick 	 * remote root.  If this object is not persistent, then backward
62640010Smckusick 	 * traversals of the mount point (i.e. "..") will not work if
62740010Smckusick 	 * the nfsnode gets flushed out of the cache. Ufs does not have
62840010Smckusick 	 * this problem, because one can identify root inodes by their
62940010Smckusick 	 * number == ROOTINO (2).
63040010Smckusick 	 */
63168653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
63268653Smckusick 	if (error)
63340010Smckusick 		goto bad;
63446988Smckusick 	*vpp = NFSTOV(np);
63541904Smckusick 
63640353Smckusick 	return (0);
63738414Smckusick bad:
63840120Smckusick 	nfs_disconnect(nmp);
63952196Smckusick 	free((caddr_t)nmp, M_NFSMNT);
64041904Smckusick 	m_freem(nam);
64138414Smckusick 	return (error);
64238414Smckusick }
64338414Smckusick 
64438414Smckusick /*
64538414Smckusick  * unmount system call
64638414Smckusick  */
64755082Storek int
64848055Smckusick nfs_unmount(mp, mntflags, p)
64938414Smckusick 	struct mount *mp;
65041294Smckusick 	int mntflags;
65148055Smckusick 	struct proc *p;
65238414Smckusick {
65338414Smckusick 	register struct nfsmount *nmp;
65440010Smckusick 	struct nfsnode *np;
65540120Smckusick 	struct vnode *vp;
65648361Smckusick 	int error, flags = 0;
65738414Smckusick 
65869346Smckusick 	if (mntflags & MNT_FORCE)
65941294Smckusick 		flags |= FORCECLOSE;
66041398Smckusick 	nmp = VFSTONFS(mp);
66138414Smckusick 	/*
66238414Smckusick 	 * Goes something like this..
66340120Smckusick 	 * - Check for activity on the root vnode (other than ourselves).
66440120Smckusick 	 * - Call vflush() to clear out vnodes for this file system,
66540120Smckusick 	 *   except for the root vnode.
66640120Smckusick 	 * - Decrement reference on the vnode representing remote root.
66738414Smckusick 	 * - Close the socket
66838414Smckusick 	 * - Free up the data structures
66938414Smckusick 	 */
67040010Smckusick 	/*
67140010Smckusick 	 * We need to decrement the ref. count on the nfsnode representing
67240010Smckusick 	 * the remote root.  See comment in mountnfs().  The VFS unmount()
67340010Smckusick 	 * has done vput on this vnode, otherwise we would get deadlock!
67440010Smckusick 	 */
67568653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
67668653Smckusick 	if (error)
67740010Smckusick 		return(error);
67840120Smckusick 	vp = NFSTOV(np);
67940120Smckusick 	if (vp->v_usecount > 2) {
68040120Smckusick 		vput(vp);
68140120Smckusick 		return (EBUSY);
68240120Smckusick 	}
68352196Smckusick 
68452196Smckusick 	/*
68552196Smckusick 	 * Must handshake with nqnfs_clientd() if it is active.
68652196Smckusick 	 */
68752196Smckusick 	nmp->nm_flag |= NFSMNT_DISMINPROG;
68852196Smckusick 	while (nmp->nm_inprog != NULLVP)
68952196Smckusick 		(void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
69068653Smckusick 	error = vflush(mp, vp, flags);
69168653Smckusick 	if (error) {
69240120Smckusick 		vput(vp);
69352196Smckusick 		nmp->nm_flag &= ~NFSMNT_DISMINPROG;
69440120Smckusick 		return (error);
69540120Smckusick 	}
69652196Smckusick 
69740010Smckusick 	/*
69852196Smckusick 	 * We are now committed to the unmount.
69952196Smckusick 	 * For NQNFS, let the server daemon free the nfsmount structure.
70040010Smckusick 	 */
70152196Smckusick 	if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
70252196Smckusick 		nmp->nm_flag |= NFSMNT_DISMNT;
70352196Smckusick 
70452196Smckusick 	/*
70552196Smckusick 	 * There are two reference counts to get rid of here.
70652196Smckusick 	 */
70740120Smckusick 	vrele(vp);
70852196Smckusick 	vrele(vp);
70952288Smckusick 	vgone(vp);
71040120Smckusick 	nfs_disconnect(nmp);
71141904Smckusick 	m_freem(nmp->nm_nam);
71252196Smckusick 
71352196Smckusick 	if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0)
71452196Smckusick 		free((caddr_t)nmp, M_NFSMNT);
71538414Smckusick 	return (0);
71638414Smckusick }
71738414Smckusick 
71838414Smckusick /*
71938414Smckusick  * Return root of a filesystem
72038414Smckusick  */
72155082Storek int
72238414Smckusick nfs_root(mp, vpp)
72338414Smckusick 	struct mount *mp;
72438414Smckusick 	struct vnode **vpp;
72538414Smckusick {
72638414Smckusick 	register struct vnode *vp;
72738414Smckusick 	struct nfsmount *nmp;
72838414Smckusick 	struct nfsnode *np;
72938414Smckusick 	int error;
73038414Smckusick 
73141398Smckusick 	nmp = VFSTONFS(mp);
73268653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
73368653Smckusick 	if (error)
73438414Smckusick 		return (error);
73538414Smckusick 	vp = NFSTOV(np);
73638414Smckusick 	vp->v_type = VDIR;
73738414Smckusick 	vp->v_flag = VROOT;
73838414Smckusick 	*vpp = vp;
73938414Smckusick 	return (0);
74038414Smckusick }
74138414Smckusick 
74238884Smacklem extern int syncprt;
74338884Smacklem 
74438414Smckusick /*
74538884Smacklem  * Flush out the buffer cache
74638414Smckusick  */
74739494Smckusick /* ARGSUSED */
74855082Storek int
74954450Smckusick nfs_sync(mp, waitfor, cred, p)
75038414Smckusick 	struct mount *mp;
75138414Smckusick 	int waitfor;
75254450Smckusick 	struct ucred *cred;
75354450Smckusick 	struct proc *p;
75438414Smckusick {
75554450Smckusick 	register struct vnode *vp;
75654450Smckusick 	int error, allerror = 0;
75754450Smckusick 
75838884Smacklem 	/*
75938884Smacklem 	 * Force stale buffer cache information to be flushed.
76038884Smacklem 	 */
76154450Smckusick loop:
76265251Smckusick 	for (vp = mp->mnt_vnodelist.lh_first;
76365251Smckusick 	     vp != NULL;
76465251Smckusick 	     vp = vp->v_mntvnodes.le_next) {
76554450Smckusick 		/*
76654450Smckusick 		 * If the vnode that we are about to sync is no longer
76754450Smckusick 		 * associated with this mount point, start over.
76854450Smckusick 		 */
76954450Smckusick 		if (vp->v_mount != mp)
77054450Smckusick 			goto loop;
77165251Smckusick 		if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
77254450Smckusick 			continue;
773*69424Smckusick 		if (vget(vp, LK_EXCLUSIVE, p))
77454450Smckusick 			goto loop;
77568653Smckusick 		error = VOP_FSYNC(vp, cred, waitfor, p);
77668653Smckusick 		if (error)
77754450Smckusick 			allerror = error;
77854450Smckusick 		vput(vp);
77954450Smckusick 	}
78054450Smckusick 	return (allerror);
78138414Smckusick }
78238414Smckusick 
78338414Smckusick /*
78454667Smckusick  * NFS flat namespace lookup.
78554667Smckusick  * Currently unsupported.
78654667Smckusick  */
78754667Smckusick /* ARGSUSED */
78854667Smckusick int
78954667Smckusick nfs_vget(mp, ino, vpp)
79054667Smckusick 	struct mount *mp;
79154667Smckusick 	ino_t ino;
79254667Smckusick 	struct vnode **vpp;
79354667Smckusick {
79454667Smckusick 
79554667Smckusick 	return (EOPNOTSUPP);
79654667Smckusick }
79754667Smckusick 
79854667Smckusick /*
79938414Smckusick  * At this point, this should never happen
80038414Smckusick  */
80139494Smckusick /* ARGSUSED */
80255082Storek int
80354737Smckusick nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
80454737Smckusick 	register struct mount *mp;
80538414Smckusick 	struct fid *fhp;
80654737Smckusick 	struct mbuf *nam;
80738414Smckusick 	struct vnode **vpp;
80854737Smckusick 	int *exflagsp;
80954737Smckusick 	struct ucred **credanonp;
81038414Smckusick {
81139494Smckusick 
81238414Smckusick 	return (EINVAL);
81338414Smckusick }
81438414Smckusick 
81538414Smckusick /*
81638414Smckusick  * Vnode pointer to File handle, should never happen either
81738414Smckusick  */
81839494Smckusick /* ARGSUSED */
81955082Storek int
82048055Smckusick nfs_vptofh(vp, fhp)
82148055Smckusick 	struct vnode *vp;
82238414Smckusick 	struct fid *fhp;
82338414Smckusick {
82439494Smckusick 
82538414Smckusick 	return (EINVAL);
82638414Smckusick }
82738884Smacklem 
82838884Smacklem /*
82938884Smacklem  * Vfs start routine, a no-op.
83038884Smacklem  */
83139494Smckusick /* ARGSUSED */
83255082Storek int
83348055Smckusick nfs_start(mp, flags, p)
83438884Smacklem 	struct mount *mp;
83538884Smacklem 	int flags;
83648055Smckusick 	struct proc *p;
83738884Smacklem {
83839494Smckusick 
83938884Smacklem 	return (0);
84038884Smacklem }
84141294Smckusick 
84241294Smckusick /*
84341294Smckusick  * Do operations associated with quotas, not supported
84441294Smckusick  */
84551574Smckusick /* ARGSUSED */
84655082Storek int
84748055Smckusick nfs_quotactl(mp, cmd, uid, arg, p)
84841294Smckusick 	struct mount *mp;
84941294Smckusick 	int cmd;
85054450Smckusick 	uid_t uid;
85141294Smckusick 	caddr_t arg;
85248055Smckusick 	struct proc *p;
85341294Smckusick {
85451574Smckusick 
85541294Smckusick 	return (EOPNOTSUPP);
85641294Smckusick }
85768653Smckusick 
85868653Smckusick /*
85968653Smckusick  * Do that sysctl thang...
86068653Smckusick  */
86168653Smckusick static int
86268653Smckusick nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
86368653Smckusick 	   size_t newlen, struct proc *p)
86468653Smckusick {
86568653Smckusick 	int rv;
86668653Smckusick 
86768653Smckusick 	/*
86868653Smckusick 	 * All names at this level are terminal.
86968653Smckusick 	 */
87068653Smckusick 	if(namelen > 1)
87168653Smckusick 		return ENOTDIR;	/* overloaded */
87268653Smckusick 
87368653Smckusick 	switch(name[0]) {
87468653Smckusick 	case NFS_NFSSTATS:
87568653Smckusick 		if(!oldp) {
87668653Smckusick 			*oldlenp = sizeof nfsstats;
87768653Smckusick 			return 0;
87868653Smckusick 		}
87968653Smckusick 
88068653Smckusick 		if(*oldlenp < sizeof nfsstats) {
88168653Smckusick 			*oldlenp = sizeof nfsstats;
88268653Smckusick 			return ENOMEM;
88368653Smckusick 		}
88468653Smckusick 
88568653Smckusick 		rv = copyout(&nfsstats, oldp, sizeof nfsstats);
88668653Smckusick 		if(rv) return rv;
88768653Smckusick 
88868653Smckusick 		if(newp && newlen != sizeof nfsstats)
88968653Smckusick 			return EINVAL;
89068653Smckusick 
89168653Smckusick 		if(newp) {
89268653Smckusick 			return copyin(newp, &nfsstats, sizeof nfsstats);
89368653Smckusick 		}
89468653Smckusick 		return 0;
89568653Smckusick 
89668653Smckusick 	default:
89768653Smckusick 		return EOPNOTSUPP;
89868653Smckusick 	}
89968653Smckusick }
90068653Smckusick 
901