xref: /csrg-svn/sys/nfs/nfs_vfsops.c (revision 69315)
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*69315Smckusick  *	@(#)nfs_vfsops.c	8.8 (Berkeley) 05/09/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");
363*69315Smckusick 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
36465251Smckusick 	mp->mnt_flag |= MNT_ROOTFS;
36546988Smckusick 	mp->mnt_vnodecovered = NULLVP;
36646988Smckusick 	vfs_unlock(mp);
36746988Smckusick 	rootvp = vp;
36852445Smckusick 
36952445Smckusick 	/*
37052445Smckusick 	 * This is not really an nfs issue, but it is much easier to
37152445Smckusick 	 * set hostname here and then let the "/etc/rc.xxx" files
37252445Smckusick 	 * mount the right /var based upon its preset value.
37352445Smckusick 	 */
37455082Storek 	bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
37552445Smckusick 	hostname[MAXHOSTNAMELEN - 1] = '\0';
37652445Smckusick 	for (i = 0; i < MAXHOSTNAMELEN; i++)
37752445Smckusick 		if (hostname[i] == '\0')
37852445Smckusick 			break;
37952445Smckusick 	hostnamelen = i;
38057790Smckusick 	inittodr(ntohl(nd->root_time));
38146988Smckusick 	return (0);
38238414Smckusick }
38338414Smckusick 
38438414Smckusick /*
38555082Storek  * Internal version of mount system call for diskless setup.
38655082Storek  */
38755082Storek static struct mount *
38855082Storek nfs_mountdiskless(path, which, mountflag, sin, args, vpp)
38955082Storek 	char *path;
39055082Storek 	char *which;
39155082Storek 	int mountflag;
39255082Storek 	struct sockaddr_in *sin;
39355082Storek 	struct nfs_args *args;
39455082Storek 	register struct vnode **vpp;
39555082Storek {
39655082Storek 	register struct mount *mp;
39755082Storek 	register struct mbuf *m;
39855082Storek 	register int error;
39968789Smckusick 	struct vfsconf *vfsp;
40055082Storek 
40168789Smckusick 	for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
40268789Smckusick 		if (!strcmp(vfsp->vfc_name, "nfs"))
40368789Smckusick 			break;
40468789Smckusick 	if (vfsp == NULL)
40568789Smckusick 		panic("nfs_mountroot: NFS not configured");
40668789Smckusick 	mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
40765251Smckusick 	bzero((char *)mp, (u_long)sizeof(struct mount));
40868789Smckusick 	mp->mnt_vfc = vfsp;
40968789Smckusick 	mp->mnt_op = vfsp->vfc_vfsops;
41055082Storek 	mp->mnt_flag = mountflag;
41155082Storek 	MGET(m, MT_SONAME, M_DONTWAIT);
41255082Storek 	if (m == NULL)
41355082Storek 		panic("nfs_mountroot: %s mount mbuf", which);
41455082Storek 	bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
41555082Storek 	m->m_len = sin->sin_len;
41668789Smckusick 	if (error = mountnfs(args, mp, m, which, path, vpp))
41755082Storek 		panic("nfs_mountroot: mount %s on %s: %d", path, which, error);
41868789Smckusick 	vfsp->vfc_refcount++;
41968789Smckusick 	mp->mnt_stat.f_type = vfsp->vfc_typenum;
42068789Smckusick 	mp->mnt_flag |= (vfsp->vfc_flags & MNT_VISFLAGMASK) | MNT_ROOTFS;
42168789Smckusick 	strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
42255082Storek 
42355082Storek 	return (mp);
42455082Storek }
42555082Storek 
42652196Smckusick 
42752196Smckusick /*
42838414Smckusick  * VFS Operations.
42938414Smckusick  *
43038414Smckusick  * mount system call
43138414Smckusick  * It seems a bit dumb to copyinstr() the host and path here and then
43238414Smckusick  * bcopy() them in mountnfs(), but I wanted to detect errors before
43338414Smckusick  * doing the sockargs() call because sockargs() allocates an mbuf and
43438414Smckusick  * an error after that means that I have to release the mbuf.
43538414Smckusick  */
43639494Smckusick /* ARGSUSED */
43755082Storek int
43848055Smckusick nfs_mount(mp, path, data, ndp, p)
43938414Smckusick 	struct mount *mp;
44038414Smckusick 	char *path;
44138414Smckusick 	caddr_t data;
44238414Smckusick 	struct nameidata *ndp;
44348055Smckusick 	struct proc *p;
44438414Smckusick {
44538414Smckusick 	int error;
44638414Smckusick 	struct nfs_args args;
44741904Smckusick 	struct mbuf *nam;
44846988Smckusick 	struct vnode *vp;
44938414Smckusick 	char pth[MNAMELEN], hst[MNAMELEN];
45049108Skarels 	u_int len;
45168653Smckusick 	u_char nfh[NFSX_V3FHMAX];
45238414Smckusick 
45368653Smckusick 	error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
45468653Smckusick 	if (error)
45538414Smckusick 		return (error);
45669135Smckusick 	if (args.version != NFS_ARGSVERSION)
45769135Smckusick 		return (EPROGMISMATCH);
45868653Smckusick 	error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
45968653Smckusick 	if (error)
46038414Smckusick 		return (error);
46168653Smckusick 	error = copyinstr(path, pth, MNAMELEN-1, &len);
46268653Smckusick 	if (error)
46338414Smckusick 		return (error);
46449108Skarels 	bzero(&pth[len], MNAMELEN - len);
46568653Smckusick 	error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
46668653Smckusick 	if (error)
46738414Smckusick 		return (error);
46849108Skarels 	bzero(&hst[len], MNAMELEN - len);
46938414Smckusick 	/* sockargs() call must be after above copyin() calls */
47068653Smckusick 	error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME);
47168653Smckusick 	if (error)
47238414Smckusick 		return (error);
47368653Smckusick 	args.fh = nfh;
47446988Smckusick 	error = mountnfs(&args, mp, nam, pth, hst, &vp);
47538414Smckusick 	return (error);
47638414Smckusick }
47738414Smckusick 
47838414Smckusick /*
47938414Smckusick  * Common code for mount and mountroot
48038414Smckusick  */
48155082Storek int
48246988Smckusick mountnfs(argp, mp, nam, pth, hst, vpp)
48338414Smckusick 	register struct nfs_args *argp;
48438414Smckusick 	register struct mount *mp;
48541904Smckusick 	struct mbuf *nam;
48638414Smckusick 	char *pth, *hst;
48746988Smckusick 	struct vnode **vpp;
48838414Smckusick {
48938414Smckusick 	register struct nfsmount *nmp;
49040010Smckusick 	struct nfsnode *np;
49168653Smckusick 	int error, maxio;
49238414Smckusick 
49355082Storek 	if (mp->mnt_flag & MNT_UPDATE) {
49455082Storek 		nmp = VFSTONFS(mp);
49555082Storek 		/* update paths, file handles, etc, here	XXX */
49655082Storek 		m_freem(nam);
49755082Storek 		return (0);
49855082Storek 	} else {
49955082Storek 		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
50055082Storek 		    M_NFSMNT, M_WAITOK);
50155082Storek 		bzero((caddr_t)nmp, sizeof (struct nfsmount));
50268653Smckusick 		TAILQ_INIT(&nmp->nm_uidlruhead);
50355082Storek 		mp->mnt_data = (qaddr_t)nmp;
50455082Storek 	}
50568653Smckusick 	vfs_getnewfsid(mp);
50638414Smckusick 	nmp->nm_mountp = mp;
50738414Smckusick 	nmp->nm_flag = argp->flags;
50859759Smckusick 	if (nmp->nm_flag & NFSMNT_NQNFS)
50954985Smckusick 		/*
51054985Smckusick 		 * We have to set mnt_maxsymlink to a non-zero value so
51154985Smckusick 		 * that COMPAT_43 routines will know that we are setting
51254985Smckusick 		 * the d_type field in directories (and can zero it for
51354985Smckusick 		 * unsuspecting binaries).
51454985Smckusick 		 */
51554985Smckusick 		mp->mnt_maxsymlinklen = 1;
51652196Smckusick 	nmp->nm_timeo = NFS_TIMEO;
51740120Smckusick 	nmp->nm_retry = NFS_RETRANS;
51840120Smckusick 	nmp->nm_wsize = NFS_WSIZE;
51940120Smckusick 	nmp->nm_rsize = NFS_RSIZE;
52068653Smckusick 	nmp->nm_readdirsize = NFS_READDIRSIZE;
52152196Smckusick 	nmp->nm_numgrps = NFS_MAXGRPS;
52252196Smckusick 	nmp->nm_readahead = NFS_DEFRAHEAD;
52352196Smckusick 	nmp->nm_leaseterm = NQ_DEFLEASE;
52452196Smckusick 	nmp->nm_deadthresh = NQ_DEADTHRESH;
52567708Smckusick 	CIRCLEQ_INIT(&nmp->nm_timerhead);
52652196Smckusick 	nmp->nm_inprog = NULLVP;
52768653Smckusick 	nmp->nm_fhsize = argp->fhsize;
52868653Smckusick 	bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
52941398Smckusick 	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
53041398Smckusick 	bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
53141904Smckusick 	nmp->nm_nam = nam;
53240120Smckusick 
53340120Smckusick 	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
53452196Smckusick 		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
53552196Smckusick 		if (nmp->nm_timeo < NFS_MINTIMEO)
53652196Smckusick 			nmp->nm_timeo = NFS_MINTIMEO;
53752196Smckusick 		else if (nmp->nm_timeo > NFS_MAXTIMEO)
53852196Smckusick 			nmp->nm_timeo = NFS_MAXTIMEO;
53940120Smckusick 	}
54040120Smckusick 
54143355Smckusick 	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
54240120Smckusick 		nmp->nm_retry = argp->retrans;
54340120Smckusick 		if (nmp->nm_retry > NFS_MAXREXMIT)
54440120Smckusick 			nmp->nm_retry = NFS_MAXREXMIT;
54540120Smckusick 	}
54640120Smckusick 
54768653Smckusick 	if (argp->flags & NFSMNT_NFSV3) {
54868653Smckusick 		if (argp->sotype == SOCK_DGRAM)
54968653Smckusick 			maxio = NFS_MAXDGRAMDATA;
55068653Smckusick 		else
55168653Smckusick 			maxio = NFS_MAXDATA;
55268653Smckusick 	} else
55368653Smckusick 		maxio = NFS_V2MAXDATA;
55468653Smckusick 
55540120Smckusick 	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
55638414Smckusick 		nmp->nm_wsize = argp->wsize;
55740120Smckusick 		/* Round down to multiple of blocksize */
55868653Smckusick 		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
55940120Smckusick 		if (nmp->nm_wsize <= 0)
56068653Smckusick 			nmp->nm_wsize = NFS_FABLKSIZE;
56140120Smckusick 	}
56268653Smckusick 	if (nmp->nm_wsize > maxio)
56368653Smckusick 		nmp->nm_wsize = maxio;
56443355Smckusick 	if (nmp->nm_wsize > MAXBSIZE)
56543355Smckusick 		nmp->nm_wsize = MAXBSIZE;
56640120Smckusick 
56740120Smckusick 	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
56838414Smckusick 		nmp->nm_rsize = argp->rsize;
56940120Smckusick 		/* Round down to multiple of blocksize */
57068653Smckusick 		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
57140120Smckusick 		if (nmp->nm_rsize <= 0)
57268653Smckusick 			nmp->nm_rsize = NFS_FABLKSIZE;
57340120Smckusick 	}
57468653Smckusick 	if (nmp->nm_rsize > maxio)
57568653Smckusick 		nmp->nm_rsize = maxio;
57643355Smckusick 	if (nmp->nm_rsize > MAXBSIZE)
57743355Smckusick 		nmp->nm_rsize = MAXBSIZE;
57868653Smckusick 
57968653Smckusick 	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
58068653Smckusick 		nmp->nm_readdirsize = argp->readdirsize;
58168653Smckusick 		/* Round down to multiple of blocksize */
58268653Smckusick 		nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1);
58368653Smckusick 		if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
58468653Smckusick 			nmp->nm_readdirsize = NFS_DIRBLKSIZ;
58568653Smckusick 	}
58668653Smckusick 	if (nmp->nm_readdirsize > maxio)
58768653Smckusick 		nmp->nm_readdirsize = maxio;
58868653Smckusick 
58952196Smckusick 	if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
59052196Smckusick 		argp->maxgrouplist <= NFS_MAXGRPS)
59152196Smckusick 		nmp->nm_numgrps = argp->maxgrouplist;
59252196Smckusick 	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
59352196Smckusick 		argp->readahead <= NFS_MAXRAHEAD)
59452196Smckusick 		nmp->nm_readahead = argp->readahead;
59552196Smckusick 	if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 &&
59652196Smckusick 		argp->leaseterm <= NQ_MAXLEASE)
59752196Smckusick 		nmp->nm_leaseterm = argp->leaseterm;
59852196Smckusick 	if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 &&
59952196Smckusick 		argp->deadthresh <= NQ_NEVERDEAD)
60052196Smckusick 		nmp->nm_deadthresh = argp->deadthresh;
60140120Smckusick 	/* Set up the sockets and per-host congestion */
60241904Smckusick 	nmp->nm_sotype = argp->sotype;
60341904Smckusick 	nmp->nm_soproto = argp->proto;
60452196Smckusick 
60552196Smckusick 	/*
60652196Smckusick 	 * For Connection based sockets (TCP,...) defer the connect until
60752196Smckusick 	 * the first request, in case the server is not responding.
60852196Smckusick 	 */
60952196Smckusick 	if (nmp->nm_sotype == SOCK_DGRAM &&
61052196Smckusick 		(error = nfs_connect(nmp, (struct nfsreq *)0)))
61140120Smckusick 		goto bad;
61240120Smckusick 
61338414Smckusick 	/*
61452196Smckusick 	 * This is silly, but it has to be set so that vinifod() works.
61552196Smckusick 	 * We do not want to do an nfs_statfs() here since we can get
61652196Smckusick 	 * stuck on a dead server and we are holding a lock on the mount
61752196Smckusick 	 * point.
61852196Smckusick 	 */
61952196Smckusick 	mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA;
62052196Smckusick 	/*
62140010Smckusick 	 * A reference count is needed on the nfsnode representing the
62240010Smckusick 	 * remote root.  If this object is not persistent, then backward
62340010Smckusick 	 * traversals of the mount point (i.e. "..") will not work if
62440010Smckusick 	 * the nfsnode gets flushed out of the cache. Ufs does not have
62540010Smckusick 	 * this problem, because one can identify root inodes by their
62640010Smckusick 	 * number == ROOTINO (2).
62740010Smckusick 	 */
62868653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
62968653Smckusick 	if (error)
63040010Smckusick 		goto bad;
63146988Smckusick 	*vpp = NFSTOV(np);
63241904Smckusick 
63340353Smckusick 	return (0);
63438414Smckusick bad:
63540120Smckusick 	nfs_disconnect(nmp);
63652196Smckusick 	free((caddr_t)nmp, M_NFSMNT);
63741904Smckusick 	m_freem(nam);
63838414Smckusick 	return (error);
63938414Smckusick }
64038414Smckusick 
64138414Smckusick /*
64238414Smckusick  * unmount system call
64338414Smckusick  */
64455082Storek int
64548055Smckusick nfs_unmount(mp, mntflags, p)
64638414Smckusick 	struct mount *mp;
64741294Smckusick 	int mntflags;
64848055Smckusick 	struct proc *p;
64938414Smckusick {
65038414Smckusick 	register struct nfsmount *nmp;
65140010Smckusick 	struct nfsnode *np;
65240120Smckusick 	struct vnode *vp;
65348361Smckusick 	int error, flags = 0;
65448361Smckusick 	extern int doforce;
65538414Smckusick 
65648066Smckusick 	if (mntflags & MNT_FORCE) {
65768653Smckusick 		if (!doforce)
65848066Smckusick 			return (EINVAL);
65941294Smckusick 		flags |= FORCECLOSE;
66048066Smckusick 	}
66141398Smckusick 	nmp = VFSTONFS(mp);
66238414Smckusick 	/*
66338414Smckusick 	 * Goes something like this..
66440120Smckusick 	 * - Check for activity on the root vnode (other than ourselves).
66540120Smckusick 	 * - Call vflush() to clear out vnodes for this file system,
66640120Smckusick 	 *   except for the root vnode.
66740120Smckusick 	 * - Decrement reference on the vnode representing remote root.
66838414Smckusick 	 * - Close the socket
66938414Smckusick 	 * - Free up the data structures
67038414Smckusick 	 */
67140010Smckusick 	/*
67240010Smckusick 	 * We need to decrement the ref. count on the nfsnode representing
67340010Smckusick 	 * the remote root.  See comment in mountnfs().  The VFS unmount()
67440010Smckusick 	 * has done vput on this vnode, otherwise we would get deadlock!
67540010Smckusick 	 */
67668653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
67768653Smckusick 	if (error)
67840010Smckusick 		return(error);
67940120Smckusick 	vp = NFSTOV(np);
68040120Smckusick 	if (vp->v_usecount > 2) {
68140120Smckusick 		vput(vp);
68240120Smckusick 		return (EBUSY);
68340120Smckusick 	}
68452196Smckusick 
68552196Smckusick 	/*
68652196Smckusick 	 * Must handshake with nqnfs_clientd() if it is active.
68752196Smckusick 	 */
68852196Smckusick 	nmp->nm_flag |= NFSMNT_DISMINPROG;
68952196Smckusick 	while (nmp->nm_inprog != NULLVP)
69052196Smckusick 		(void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
69168653Smckusick 	error = vflush(mp, vp, flags);
69268653Smckusick 	if (error) {
69340120Smckusick 		vput(vp);
69452196Smckusick 		nmp->nm_flag &= ~NFSMNT_DISMINPROG;
69540120Smckusick 		return (error);
69640120Smckusick 	}
69752196Smckusick 
69840010Smckusick 	/*
69952196Smckusick 	 * We are now committed to the unmount.
70052196Smckusick 	 * For NQNFS, let the server daemon free the nfsmount structure.
70140010Smckusick 	 */
70252196Smckusick 	if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
70352196Smckusick 		nmp->nm_flag |= NFSMNT_DISMNT;
70452196Smckusick 
70552196Smckusick 	/*
70652196Smckusick 	 * There are two reference counts to get rid of here.
70752196Smckusick 	 */
70840120Smckusick 	vrele(vp);
70952196Smckusick 	vrele(vp);
71052288Smckusick 	vgone(vp);
71140120Smckusick 	nfs_disconnect(nmp);
71241904Smckusick 	m_freem(nmp->nm_nam);
71352196Smckusick 
71452196Smckusick 	if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0)
71552196Smckusick 		free((caddr_t)nmp, M_NFSMNT);
71638414Smckusick 	return (0);
71738414Smckusick }
71838414Smckusick 
71938414Smckusick /*
72038414Smckusick  * Return root of a filesystem
72138414Smckusick  */
72255082Storek int
72338414Smckusick nfs_root(mp, vpp)
72438414Smckusick 	struct mount *mp;
72538414Smckusick 	struct vnode **vpp;
72638414Smckusick {
72738414Smckusick 	register struct vnode *vp;
72838414Smckusick 	struct nfsmount *nmp;
72938414Smckusick 	struct nfsnode *np;
73038414Smckusick 	int error;
73138414Smckusick 
73241398Smckusick 	nmp = VFSTONFS(mp);
73368653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
73468653Smckusick 	if (error)
73538414Smckusick 		return (error);
73638414Smckusick 	vp = NFSTOV(np);
73738414Smckusick 	vp->v_type = VDIR;
73838414Smckusick 	vp->v_flag = VROOT;
73938414Smckusick 	*vpp = vp;
74038414Smckusick 	return (0);
74138414Smckusick }
74238414Smckusick 
74338884Smacklem extern int syncprt;
74438884Smacklem 
74538414Smckusick /*
74638884Smacklem  * Flush out the buffer cache
74738414Smckusick  */
74839494Smckusick /* ARGSUSED */
74955082Storek int
75054450Smckusick nfs_sync(mp, waitfor, cred, p)
75138414Smckusick 	struct mount *mp;
75238414Smckusick 	int waitfor;
75354450Smckusick 	struct ucred *cred;
75454450Smckusick 	struct proc *p;
75538414Smckusick {
75654450Smckusick 	register struct vnode *vp;
75754450Smckusick 	int error, allerror = 0;
75854450Smckusick 
75938884Smacklem 	/*
76038884Smacklem 	 * Force stale buffer cache information to be flushed.
76138884Smacklem 	 */
76254450Smckusick loop:
76365251Smckusick 	for (vp = mp->mnt_vnodelist.lh_first;
76465251Smckusick 	     vp != NULL;
76565251Smckusick 	     vp = vp->v_mntvnodes.le_next) {
76654450Smckusick 		/*
76754450Smckusick 		 * If the vnode that we are about to sync is no longer
76854450Smckusick 		 * associated with this mount point, start over.
76954450Smckusick 		 */
77054450Smckusick 		if (vp->v_mount != mp)
77154450Smckusick 			goto loop;
77265251Smckusick 		if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
77354450Smckusick 			continue;
77465251Smckusick 		if (vget(vp, 1))
77554450Smckusick 			goto loop;
77668653Smckusick 		error = VOP_FSYNC(vp, cred, waitfor, p);
77768653Smckusick 		if (error)
77854450Smckusick 			allerror = error;
77954450Smckusick 		vput(vp);
78054450Smckusick 	}
78154450Smckusick 	return (allerror);
78238414Smckusick }
78338414Smckusick 
78438414Smckusick /*
78554667Smckusick  * NFS flat namespace lookup.
78654667Smckusick  * Currently unsupported.
78754667Smckusick  */
78854667Smckusick /* ARGSUSED */
78954667Smckusick int
79054667Smckusick nfs_vget(mp, ino, vpp)
79154667Smckusick 	struct mount *mp;
79254667Smckusick 	ino_t ino;
79354667Smckusick 	struct vnode **vpp;
79454667Smckusick {
79554667Smckusick 
79654667Smckusick 	return (EOPNOTSUPP);
79754667Smckusick }
79854667Smckusick 
79954667Smckusick /*
80038414Smckusick  * At this point, this should never happen
80138414Smckusick  */
80239494Smckusick /* ARGSUSED */
80355082Storek int
80454737Smckusick nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
80554737Smckusick 	register struct mount *mp;
80638414Smckusick 	struct fid *fhp;
80754737Smckusick 	struct mbuf *nam;
80838414Smckusick 	struct vnode **vpp;
80954737Smckusick 	int *exflagsp;
81054737Smckusick 	struct ucred **credanonp;
81138414Smckusick {
81239494Smckusick 
81338414Smckusick 	return (EINVAL);
81438414Smckusick }
81538414Smckusick 
81638414Smckusick /*
81738414Smckusick  * Vnode pointer to File handle, should never happen either
81838414Smckusick  */
81939494Smckusick /* ARGSUSED */
82055082Storek int
82148055Smckusick nfs_vptofh(vp, fhp)
82248055Smckusick 	struct vnode *vp;
82338414Smckusick 	struct fid *fhp;
82438414Smckusick {
82539494Smckusick 
82638414Smckusick 	return (EINVAL);
82738414Smckusick }
82838884Smacklem 
82938884Smacklem /*
83038884Smacklem  * Vfs start routine, a no-op.
83138884Smacklem  */
83239494Smckusick /* ARGSUSED */
83355082Storek int
83448055Smckusick nfs_start(mp, flags, p)
83538884Smacklem 	struct mount *mp;
83638884Smacklem 	int flags;
83748055Smckusick 	struct proc *p;
83838884Smacklem {
83939494Smckusick 
84038884Smacklem 	return (0);
84138884Smacklem }
84241294Smckusick 
84341294Smckusick /*
84441294Smckusick  * Do operations associated with quotas, not supported
84541294Smckusick  */
84651574Smckusick /* ARGSUSED */
84755082Storek int
84848055Smckusick nfs_quotactl(mp, cmd, uid, arg, p)
84941294Smckusick 	struct mount *mp;
85041294Smckusick 	int cmd;
85154450Smckusick 	uid_t uid;
85241294Smckusick 	caddr_t arg;
85348055Smckusick 	struct proc *p;
85441294Smckusick {
85551574Smckusick 
85641294Smckusick 	return (EOPNOTSUPP);
85741294Smckusick }
85868653Smckusick 
85968653Smckusick /*
86068653Smckusick  * Do that sysctl thang...
86168653Smckusick  */
86268653Smckusick static int
86368653Smckusick nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
86468653Smckusick 	   size_t newlen, struct proc *p)
86568653Smckusick {
86668653Smckusick 	int rv;
86768653Smckusick 
86868653Smckusick 	/*
86968653Smckusick 	 * All names at this level are terminal.
87068653Smckusick 	 */
87168653Smckusick 	if(namelen > 1)
87268653Smckusick 		return ENOTDIR;	/* overloaded */
87368653Smckusick 
87468653Smckusick 	switch(name[0]) {
87568653Smckusick 	case NFS_NFSSTATS:
87668653Smckusick 		if(!oldp) {
87768653Smckusick 			*oldlenp = sizeof nfsstats;
87868653Smckusick 			return 0;
87968653Smckusick 		}
88068653Smckusick 
88168653Smckusick 		if(*oldlenp < sizeof nfsstats) {
88268653Smckusick 			*oldlenp = sizeof nfsstats;
88368653Smckusick 			return ENOMEM;
88468653Smckusick 		}
88568653Smckusick 
88668653Smckusick 		rv = copyout(&nfsstats, oldp, sizeof nfsstats);
88768653Smckusick 		if(rv) return rv;
88868653Smckusick 
88968653Smckusick 		if(newp && newlen != sizeof nfsstats)
89068653Smckusick 			return EINVAL;
89168653Smckusick 
89268653Smckusick 		if(newp) {
89368653Smckusick 			return copyin(newp, &nfsstats, sizeof nfsstats);
89468653Smckusick 		}
89568653Smckusick 		return 0;
89668653Smckusick 
89768653Smckusick 	default:
89868653Smckusick 		return EOPNOTSUPP;
89968653Smckusick 	}
90068653Smckusick }
90168653Smckusick 
902