xref: /csrg-svn/sys/nfs/nfs_vfsops.c (revision 69574)
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*69574Smckusick  *	@(#)nfs_vfsops.c	8.12 (Berkeley) 05/20/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 *,
78*69574Smckusick 	struct nfs_args *, struct proc *, struct vnode **, struct mount **));
7938414Smckusick 
8038414Smckusick /*
8141904Smckusick  * nfs statfs call
8241904Smckusick  */
8355082Storek int
nfs_statfs(mp,sbp,p)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
nfs_fsinfo(nmp,vp,cred,p)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
nfs_mountroot()22638414Smckusick nfs_mountroot()
22738414Smckusick {
228*69574Smckusick 	struct mount *mp, *swap_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 
311*69574Smckusick 	swap_mp = NULL;
31268653Smckusick 	if (nd->swap_nblks) {
31368653Smckusick 		/*
31468653Smckusick 		 * Create a fake mount point just for the swap vnode so that the
31568653Smckusick 		 * swap file can be on a different server from the rootfs.
31668653Smckusick 		 */
31768653Smckusick 		nd->swap_args.fh = nd->swap_fh;
31868653Smckusick 		/*
31968653Smckusick 		 * If using nfsv3_diskless, replace NFSX_V2FH with
32068653Smckusick 		 * nd->swap_fhsize.
32168653Smckusick 		 */
32268653Smckusick 		nd->swap_args.fhsize = NFSX_V2FH;
32368653Smckusick 		l = ntohl(nd->swap_saddr.sin_addr.s_addr);
32468653Smckusick 		sprintf(buf,"%ld.%ld.%ld.%ld:%s",
32568653Smckusick 			(l >> 24) & 0xff, (l >> 16) & 0xff,
32668653Smckusick 			(l >>  8) & 0xff, (l >>  0) & 0xff,nd->swap_hostnam);
32768653Smckusick 		printf("NFS SWAP: %s\n",buf);
32869368Smckusick 		if (error = nfs_mountdiskless(buf, "/swap", 0,
329*69574Smckusick 		    &nd->swap_saddr, &nd->swap_args, p, &vp, &swap_mp))
33069368Smckusick 			return (error);
331*69574Smckusick 		vfs_unbusy(swap_mp, p);
33268653Smckusick 
33368653Smckusick 		for (i=0;swdevt[i].sw_dev != NODEV;i++) ;
33468653Smckusick 
33546988Smckusick 		/*
33646988Smckusick 		 * Since the swap file is not the root dir of a file system,
33746988Smckusick 		 * hack it to a regular file.
33846988Smckusick 		 */
33946988Smckusick 		vp->v_type = VREG;
34046988Smckusick 		vp->v_flag = 0;
34146988Smckusick 		swapdev_vp = vp;
34246988Smckusick 		VREF(vp);
34368653Smckusick 		swdevt[i].sw_vp = vp;
34468653Smckusick 		swdevt[i].sw_nblks = nd->swap_nblks*2;
34546988Smckusick 
34668653Smckusick 		if (!swdevt[i].sw_nblks) {
34768653Smckusick 			swdevt[i].sw_nblks = 2048;
34868653Smckusick 			printf("defaulting to %d kbyte.\n",
34968653Smckusick 				swdevt[i].sw_nblks/2);
35068653Smckusick 		} else
35168653Smckusick 			printf("using %d kbyte.\n",swdevt[i].sw_nblks/2);
35268653Smckusick 	}
35368653Smckusick 
35446988Smckusick 	/*
35546988Smckusick 	 * Create the rootfs mount point.
35646988Smckusick 	 */
35768653Smckusick 	nd->root_args.fh = nd->root_fh;
35868653Smckusick 	/*
35968653Smckusick 	 * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize.
36068653Smckusick 	 */
36168653Smckusick 	nd->root_args.fhsize = NFSX_V2FH;
36268653Smckusick 	l = ntohl(nd->swap_saddr.sin_addr.s_addr);
36368653Smckusick 	sprintf(buf,"%ld.%ld.%ld.%ld:%s",
36468653Smckusick 		(l >> 24) & 0xff, (l >> 16) & 0xff,
36568653Smckusick 		(l >>  8) & 0xff, (l >>  0) & 0xff,nd->root_hostnam);
36668653Smckusick 	printf("NFS ROOT: %s\n",buf);
36769368Smckusick 	if (error = nfs_mountdiskless(buf, "/", MNT_RDONLY,
368*69574Smckusick 	    &nd->root_saddr, &nd->root_args, p, &vp, &mp)) {
369*69574Smckusick 		if (swap_mp) {
370*69574Smckusick 			mp->mnt_vfc->vfc_refcount--;
371*69574Smckusick 			free(swap_mp, M_MOUNT);
372*69574Smckusick 		}
37369368Smckusick 		return (error);
374*69574Smckusick 	}
37546988Smckusick 
376*69574Smckusick 	simple_lock(&mountlist_slock);
37769315Smckusick 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
378*69574Smckusick 	simple_unlock(&mountlist_slock);
37946988Smckusick 	rootvp = vp;
380*69574Smckusick 	vfs_unbusy(mp, p);
38152445Smckusick 
38252445Smckusick 	/*
38352445Smckusick 	 * This is not really an nfs issue, but it is much easier to
38452445Smckusick 	 * set hostname here and then let the "/etc/rc.xxx" files
38552445Smckusick 	 * mount the right /var based upon its preset value.
38652445Smckusick 	 */
38755082Storek 	bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
38852445Smckusick 	hostname[MAXHOSTNAMELEN - 1] = '\0';
38952445Smckusick 	for (i = 0; i < MAXHOSTNAMELEN; i++)
39052445Smckusick 		if (hostname[i] == '\0')
39152445Smckusick 			break;
39252445Smckusick 	hostnamelen = i;
39357790Smckusick 	inittodr(ntohl(nd->root_time));
39446988Smckusick 	return (0);
39538414Smckusick }
39638414Smckusick 
39738414Smckusick /*
39855082Storek  * Internal version of mount system call for diskless setup.
39955082Storek  */
40069368Smckusick static int
nfs_mountdiskless(path,which,mountflag,sin,args,p,vpp,mpp)401*69574Smckusick nfs_mountdiskless(path, which, mountflag, sin, args, p, vpp, mpp)
40255082Storek 	char *path;
40355082Storek 	char *which;
40455082Storek 	int mountflag;
40555082Storek 	struct sockaddr_in *sin;
40655082Storek 	struct nfs_args *args;
407*69574Smckusick 	struct proc *p;
40869368Smckusick 	struct vnode **vpp;
40969368Smckusick 	struct mount **mpp;
41055082Storek {
41169368Smckusick 	struct mount *mp;
41269368Smckusick 	struct mbuf *m;
41369368Smckusick 	int error;
41455082Storek 
41569368Smckusick 	if (error = vfs_rootmountalloc("nfs", path, &mp)) {
41669368Smckusick 		printf("nfs_mountroot: NFS not configured");
41769368Smckusick 		return (error);
41869368Smckusick 	}
41955082Storek 	mp->mnt_flag = mountflag;
420*69574Smckusick 	MGET(m, MT_SONAME, M_WAITOK);
42155082Storek 	bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
42255082Storek 	m->m_len = sin->sin_len;
42369368Smckusick 	if (error = mountnfs(args, mp, m, which, path, vpp)) {
42469368Smckusick 		printf("nfs_mountroot: mount %s on %s: %d", path, which, error);
425*69574Smckusick 		mp->mnt_vfc->vfc_refcount--;
426*69574Smckusick 		vfs_unbusy(mp, p);
427*69574Smckusick 		free(mp, M_MOUNT);
42869368Smckusick 		return (error);
42969368Smckusick 	}
43069368Smckusick 	(void) copystr(which, mp->mnt_stat.f_mntonname, MNAMELEN - 1, 0);
43169368Smckusick 	*mpp = mp;
43269368Smckusick 	return (0);
43355082Storek }
43455082Storek 
43552196Smckusick /*
43638414Smckusick  * VFS Operations.
43738414Smckusick  *
43838414Smckusick  * mount system call
43938414Smckusick  * It seems a bit dumb to copyinstr() the host and path here and then
44038414Smckusick  * bcopy() them in mountnfs(), but I wanted to detect errors before
44138414Smckusick  * doing the sockargs() call because sockargs() allocates an mbuf and
44238414Smckusick  * an error after that means that I have to release the mbuf.
44338414Smckusick  */
44439494Smckusick /* ARGSUSED */
44555082Storek int
nfs_mount(mp,path,data,ndp,p)44648055Smckusick nfs_mount(mp, path, data, ndp, p)
44738414Smckusick 	struct mount *mp;
44838414Smckusick 	char *path;
44938414Smckusick 	caddr_t data;
45038414Smckusick 	struct nameidata *ndp;
45148055Smckusick 	struct proc *p;
45238414Smckusick {
45338414Smckusick 	int error;
45438414Smckusick 	struct nfs_args args;
45541904Smckusick 	struct mbuf *nam;
45646988Smckusick 	struct vnode *vp;
45738414Smckusick 	char pth[MNAMELEN], hst[MNAMELEN];
45849108Skarels 	u_int len;
45968653Smckusick 	u_char nfh[NFSX_V3FHMAX];
46038414Smckusick 
46168653Smckusick 	error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
46268653Smckusick 	if (error)
46338414Smckusick 		return (error);
46469135Smckusick 	if (args.version != NFS_ARGSVERSION)
46569135Smckusick 		return (EPROGMISMATCH);
46668653Smckusick 	error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
46768653Smckusick 	if (error)
46838414Smckusick 		return (error);
46968653Smckusick 	error = copyinstr(path, pth, MNAMELEN-1, &len);
47068653Smckusick 	if (error)
47138414Smckusick 		return (error);
47249108Skarels 	bzero(&pth[len], MNAMELEN - len);
47368653Smckusick 	error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
47468653Smckusick 	if (error)
47538414Smckusick 		return (error);
47649108Skarels 	bzero(&hst[len], MNAMELEN - len);
47738414Smckusick 	/* sockargs() call must be after above copyin() calls */
47868653Smckusick 	error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME);
47968653Smckusick 	if (error)
48038414Smckusick 		return (error);
48168653Smckusick 	args.fh = nfh;
48246988Smckusick 	error = mountnfs(&args, mp, nam, pth, hst, &vp);
48338414Smckusick 	return (error);
48438414Smckusick }
48538414Smckusick 
48638414Smckusick /*
48738414Smckusick  * Common code for mount and mountroot
48838414Smckusick  */
48955082Storek int
mountnfs(argp,mp,nam,pth,hst,vpp)49046988Smckusick mountnfs(argp, mp, nam, pth, hst, vpp)
49138414Smckusick 	register struct nfs_args *argp;
49238414Smckusick 	register struct mount *mp;
49341904Smckusick 	struct mbuf *nam;
49438414Smckusick 	char *pth, *hst;
49546988Smckusick 	struct vnode **vpp;
49638414Smckusick {
49738414Smckusick 	register struct nfsmount *nmp;
49840010Smckusick 	struct nfsnode *np;
49968653Smckusick 	int error, maxio;
50038414Smckusick 
50155082Storek 	if (mp->mnt_flag & MNT_UPDATE) {
50255082Storek 		nmp = VFSTONFS(mp);
50355082Storek 		/* update paths, file handles, etc, here	XXX */
50455082Storek 		m_freem(nam);
50555082Storek 		return (0);
50655082Storek 	} else {
50755082Storek 		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount),
50855082Storek 		    M_NFSMNT, M_WAITOK);
50955082Storek 		bzero((caddr_t)nmp, sizeof (struct nfsmount));
51068653Smckusick 		TAILQ_INIT(&nmp->nm_uidlruhead);
51155082Storek 		mp->mnt_data = (qaddr_t)nmp;
51255082Storek 	}
51368653Smckusick 	vfs_getnewfsid(mp);
51438414Smckusick 	nmp->nm_mountp = mp;
51538414Smckusick 	nmp->nm_flag = argp->flags;
51659759Smckusick 	if (nmp->nm_flag & NFSMNT_NQNFS)
51754985Smckusick 		/*
51854985Smckusick 		 * We have to set mnt_maxsymlink to a non-zero value so
51954985Smckusick 		 * that COMPAT_43 routines will know that we are setting
52054985Smckusick 		 * the d_type field in directories (and can zero it for
52154985Smckusick 		 * unsuspecting binaries).
52254985Smckusick 		 */
52354985Smckusick 		mp->mnt_maxsymlinklen = 1;
52452196Smckusick 	nmp->nm_timeo = NFS_TIMEO;
52540120Smckusick 	nmp->nm_retry = NFS_RETRANS;
52640120Smckusick 	nmp->nm_wsize = NFS_WSIZE;
52740120Smckusick 	nmp->nm_rsize = NFS_RSIZE;
52868653Smckusick 	nmp->nm_readdirsize = NFS_READDIRSIZE;
52952196Smckusick 	nmp->nm_numgrps = NFS_MAXGRPS;
53052196Smckusick 	nmp->nm_readahead = NFS_DEFRAHEAD;
53152196Smckusick 	nmp->nm_leaseterm = NQ_DEFLEASE;
53252196Smckusick 	nmp->nm_deadthresh = NQ_DEADTHRESH;
53367708Smckusick 	CIRCLEQ_INIT(&nmp->nm_timerhead);
53452196Smckusick 	nmp->nm_inprog = NULLVP;
53568653Smckusick 	nmp->nm_fhsize = argp->fhsize;
53668653Smckusick 	bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
53741398Smckusick 	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
53841398Smckusick 	bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
53941904Smckusick 	nmp->nm_nam = nam;
54040120Smckusick 
54140120Smckusick 	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
54252196Smckusick 		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
54352196Smckusick 		if (nmp->nm_timeo < NFS_MINTIMEO)
54452196Smckusick 			nmp->nm_timeo = NFS_MINTIMEO;
54552196Smckusick 		else if (nmp->nm_timeo > NFS_MAXTIMEO)
54652196Smckusick 			nmp->nm_timeo = NFS_MAXTIMEO;
54740120Smckusick 	}
54840120Smckusick 
54943355Smckusick 	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
55040120Smckusick 		nmp->nm_retry = argp->retrans;
55140120Smckusick 		if (nmp->nm_retry > NFS_MAXREXMIT)
55240120Smckusick 			nmp->nm_retry = NFS_MAXREXMIT;
55340120Smckusick 	}
55440120Smckusick 
55568653Smckusick 	if (argp->flags & NFSMNT_NFSV3) {
55668653Smckusick 		if (argp->sotype == SOCK_DGRAM)
55768653Smckusick 			maxio = NFS_MAXDGRAMDATA;
55868653Smckusick 		else
55968653Smckusick 			maxio = NFS_MAXDATA;
56068653Smckusick 	} else
56168653Smckusick 		maxio = NFS_V2MAXDATA;
56268653Smckusick 
56340120Smckusick 	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
56438414Smckusick 		nmp->nm_wsize = argp->wsize;
56540120Smckusick 		/* Round down to multiple of blocksize */
56668653Smckusick 		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
56740120Smckusick 		if (nmp->nm_wsize <= 0)
56868653Smckusick 			nmp->nm_wsize = NFS_FABLKSIZE;
56940120Smckusick 	}
57068653Smckusick 	if (nmp->nm_wsize > maxio)
57168653Smckusick 		nmp->nm_wsize = maxio;
57243355Smckusick 	if (nmp->nm_wsize > MAXBSIZE)
57343355Smckusick 		nmp->nm_wsize = MAXBSIZE;
57440120Smckusick 
57540120Smckusick 	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
57638414Smckusick 		nmp->nm_rsize = argp->rsize;
57740120Smckusick 		/* Round down to multiple of blocksize */
57868653Smckusick 		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
57940120Smckusick 		if (nmp->nm_rsize <= 0)
58068653Smckusick 			nmp->nm_rsize = NFS_FABLKSIZE;
58140120Smckusick 	}
58268653Smckusick 	if (nmp->nm_rsize > maxio)
58368653Smckusick 		nmp->nm_rsize = maxio;
58443355Smckusick 	if (nmp->nm_rsize > MAXBSIZE)
58543355Smckusick 		nmp->nm_rsize = MAXBSIZE;
58668653Smckusick 
58768653Smckusick 	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
58868653Smckusick 		nmp->nm_readdirsize = argp->readdirsize;
58968653Smckusick 		/* Round down to multiple of blocksize */
59068653Smckusick 		nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1);
59168653Smckusick 		if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
59268653Smckusick 			nmp->nm_readdirsize = NFS_DIRBLKSIZ;
59368653Smckusick 	}
59468653Smckusick 	if (nmp->nm_readdirsize > maxio)
59568653Smckusick 		nmp->nm_readdirsize = maxio;
59668653Smckusick 
59752196Smckusick 	if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
59852196Smckusick 		argp->maxgrouplist <= NFS_MAXGRPS)
59952196Smckusick 		nmp->nm_numgrps = argp->maxgrouplist;
60052196Smckusick 	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
60152196Smckusick 		argp->readahead <= NFS_MAXRAHEAD)
60252196Smckusick 		nmp->nm_readahead = argp->readahead;
60352196Smckusick 	if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 &&
60452196Smckusick 		argp->leaseterm <= NQ_MAXLEASE)
60552196Smckusick 		nmp->nm_leaseterm = argp->leaseterm;
60652196Smckusick 	if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 &&
60752196Smckusick 		argp->deadthresh <= NQ_NEVERDEAD)
60852196Smckusick 		nmp->nm_deadthresh = argp->deadthresh;
60940120Smckusick 	/* Set up the sockets and per-host congestion */
61041904Smckusick 	nmp->nm_sotype = argp->sotype;
61141904Smckusick 	nmp->nm_soproto = argp->proto;
61252196Smckusick 
61352196Smckusick 	/*
61452196Smckusick 	 * For Connection based sockets (TCP,...) defer the connect until
61552196Smckusick 	 * the first request, in case the server is not responding.
61652196Smckusick 	 */
61752196Smckusick 	if (nmp->nm_sotype == SOCK_DGRAM &&
61852196Smckusick 		(error = nfs_connect(nmp, (struct nfsreq *)0)))
61940120Smckusick 		goto bad;
62040120Smckusick 
62138414Smckusick 	/*
62252196Smckusick 	 * This is silly, but it has to be set so that vinifod() works.
62352196Smckusick 	 * We do not want to do an nfs_statfs() here since we can get
62452196Smckusick 	 * stuck on a dead server and we are holding a lock on the mount
62552196Smckusick 	 * point.
62652196Smckusick 	 */
62752196Smckusick 	mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA;
62852196Smckusick 	/*
62940010Smckusick 	 * A reference count is needed on the nfsnode representing the
63040010Smckusick 	 * remote root.  If this object is not persistent, then backward
63140010Smckusick 	 * traversals of the mount point (i.e. "..") will not work if
63240010Smckusick 	 * the nfsnode gets flushed out of the cache. Ufs does not have
63340010Smckusick 	 * this problem, because one can identify root inodes by their
63440010Smckusick 	 * number == ROOTINO (2).
63540010Smckusick 	 */
63668653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
63768653Smckusick 	if (error)
63840010Smckusick 		goto bad;
63946988Smckusick 	*vpp = NFSTOV(np);
64041904Smckusick 
64140353Smckusick 	return (0);
64238414Smckusick bad:
64340120Smckusick 	nfs_disconnect(nmp);
64452196Smckusick 	free((caddr_t)nmp, M_NFSMNT);
64541904Smckusick 	m_freem(nam);
64638414Smckusick 	return (error);
64738414Smckusick }
64838414Smckusick 
64938414Smckusick /*
65038414Smckusick  * unmount system call
65138414Smckusick  */
65255082Storek int
nfs_unmount(mp,mntflags,p)65348055Smckusick nfs_unmount(mp, mntflags, p)
65438414Smckusick 	struct mount *mp;
65541294Smckusick 	int mntflags;
65648055Smckusick 	struct proc *p;
65738414Smckusick {
65838414Smckusick 	register struct nfsmount *nmp;
65940010Smckusick 	struct nfsnode *np;
66040120Smckusick 	struct vnode *vp;
66148361Smckusick 	int error, flags = 0;
66238414Smckusick 
66369346Smckusick 	if (mntflags & MNT_FORCE)
66441294Smckusick 		flags |= FORCECLOSE;
66541398Smckusick 	nmp = VFSTONFS(mp);
66638414Smckusick 	/*
66738414Smckusick 	 * Goes something like this..
66840120Smckusick 	 * - Check for activity on the root vnode (other than ourselves).
66940120Smckusick 	 * - Call vflush() to clear out vnodes for this file system,
67040120Smckusick 	 *   except for the root vnode.
67140120Smckusick 	 * - Decrement reference on the vnode representing remote root.
67238414Smckusick 	 * - Close the socket
67338414Smckusick 	 * - Free up the data structures
67438414Smckusick 	 */
67540010Smckusick 	/*
67640010Smckusick 	 * We need to decrement the ref. count on the nfsnode representing
67740010Smckusick 	 * the remote root.  See comment in mountnfs().  The VFS unmount()
67840010Smckusick 	 * has done vput on this vnode, otherwise we would get deadlock!
67940010Smckusick 	 */
68068653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
68168653Smckusick 	if (error)
68240010Smckusick 		return(error);
68340120Smckusick 	vp = NFSTOV(np);
68440120Smckusick 	if (vp->v_usecount > 2) {
68540120Smckusick 		vput(vp);
68640120Smckusick 		return (EBUSY);
68740120Smckusick 	}
68852196Smckusick 
68952196Smckusick 	/*
69052196Smckusick 	 * Must handshake with nqnfs_clientd() if it is active.
69152196Smckusick 	 */
69252196Smckusick 	nmp->nm_flag |= NFSMNT_DISMINPROG;
69352196Smckusick 	while (nmp->nm_inprog != NULLVP)
69452196Smckusick 		(void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
69568653Smckusick 	error = vflush(mp, vp, flags);
69668653Smckusick 	if (error) {
69740120Smckusick 		vput(vp);
69852196Smckusick 		nmp->nm_flag &= ~NFSMNT_DISMINPROG;
69940120Smckusick 		return (error);
70040120Smckusick 	}
70152196Smckusick 
70240010Smckusick 	/*
70352196Smckusick 	 * We are now committed to the unmount.
70452196Smckusick 	 * For NQNFS, let the server daemon free the nfsmount structure.
70540010Smckusick 	 */
70652196Smckusick 	if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
70752196Smckusick 		nmp->nm_flag |= NFSMNT_DISMNT;
70852196Smckusick 
70952196Smckusick 	/*
71052196Smckusick 	 * There are two reference counts to get rid of here.
71152196Smckusick 	 */
71240120Smckusick 	vrele(vp);
71352196Smckusick 	vrele(vp);
71452288Smckusick 	vgone(vp);
71540120Smckusick 	nfs_disconnect(nmp);
71641904Smckusick 	m_freem(nmp->nm_nam);
71752196Smckusick 
71852196Smckusick 	if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0)
71952196Smckusick 		free((caddr_t)nmp, M_NFSMNT);
72038414Smckusick 	return (0);
72138414Smckusick }
72238414Smckusick 
72338414Smckusick /*
72438414Smckusick  * Return root of a filesystem
72538414Smckusick  */
72655082Storek int
nfs_root(mp,vpp)72738414Smckusick nfs_root(mp, vpp)
72838414Smckusick 	struct mount *mp;
72938414Smckusick 	struct vnode **vpp;
73038414Smckusick {
73138414Smckusick 	register struct vnode *vp;
73238414Smckusick 	struct nfsmount *nmp;
73338414Smckusick 	struct nfsnode *np;
73438414Smckusick 	int error;
73538414Smckusick 
73641398Smckusick 	nmp = VFSTONFS(mp);
73768653Smckusick 	error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np);
73868653Smckusick 	if (error)
73938414Smckusick 		return (error);
74038414Smckusick 	vp = NFSTOV(np);
74138414Smckusick 	vp->v_type = VDIR;
74238414Smckusick 	vp->v_flag = VROOT;
74338414Smckusick 	*vpp = vp;
74438414Smckusick 	return (0);
74538414Smckusick }
74638414Smckusick 
74738884Smacklem extern int syncprt;
74838884Smacklem 
74938414Smckusick /*
75038884Smacklem  * Flush out the buffer cache
75138414Smckusick  */
75239494Smckusick /* ARGSUSED */
75355082Storek int
nfs_sync(mp,waitfor,cred,p)75454450Smckusick nfs_sync(mp, waitfor, cred, p)
75538414Smckusick 	struct mount *mp;
75638414Smckusick 	int waitfor;
75754450Smckusick 	struct ucred *cred;
75854450Smckusick 	struct proc *p;
75938414Smckusick {
76054450Smckusick 	register struct vnode *vp;
76154450Smckusick 	int error, allerror = 0;
76254450Smckusick 
76338884Smacklem 	/*
76438884Smacklem 	 * Force stale buffer cache information to be flushed.
76538884Smacklem 	 */
76654450Smckusick loop:
76765251Smckusick 	for (vp = mp->mnt_vnodelist.lh_first;
76865251Smckusick 	     vp != NULL;
76965251Smckusick 	     vp = vp->v_mntvnodes.le_next) {
77054450Smckusick 		/*
77154450Smckusick 		 * If the vnode that we are about to sync is no longer
77254450Smckusick 		 * associated with this mount point, start over.
77354450Smckusick 		 */
77454450Smckusick 		if (vp->v_mount != mp)
77554450Smckusick 			goto loop;
77665251Smckusick 		if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
77754450Smckusick 			continue;
77869424Smckusick 		if (vget(vp, LK_EXCLUSIVE, p))
77954450Smckusick 			goto loop;
78068653Smckusick 		error = VOP_FSYNC(vp, cred, waitfor, p);
78168653Smckusick 		if (error)
78254450Smckusick 			allerror = error;
78354450Smckusick 		vput(vp);
78454450Smckusick 	}
78554450Smckusick 	return (allerror);
78638414Smckusick }
78738414Smckusick 
78838414Smckusick /*
78954667Smckusick  * NFS flat namespace lookup.
79054667Smckusick  * Currently unsupported.
79154667Smckusick  */
79254667Smckusick /* ARGSUSED */
79354667Smckusick int
nfs_vget(mp,ino,vpp)79454667Smckusick nfs_vget(mp, ino, vpp)
79554667Smckusick 	struct mount *mp;
79654667Smckusick 	ino_t ino;
79754667Smckusick 	struct vnode **vpp;
79854667Smckusick {
79954667Smckusick 
80054667Smckusick 	return (EOPNOTSUPP);
80154667Smckusick }
80254667Smckusick 
80354667Smckusick /*
80438414Smckusick  * At this point, this should never happen
80538414Smckusick  */
80639494Smckusick /* ARGSUSED */
80755082Storek int
nfs_fhtovp(mp,fhp,nam,vpp,exflagsp,credanonp)80854737Smckusick nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
80954737Smckusick 	register struct mount *mp;
81038414Smckusick 	struct fid *fhp;
81154737Smckusick 	struct mbuf *nam;
81238414Smckusick 	struct vnode **vpp;
81354737Smckusick 	int *exflagsp;
81454737Smckusick 	struct ucred **credanonp;
81538414Smckusick {
81639494Smckusick 
81738414Smckusick 	return (EINVAL);
81838414Smckusick }
81938414Smckusick 
82038414Smckusick /*
82138414Smckusick  * Vnode pointer to File handle, should never happen either
82238414Smckusick  */
82339494Smckusick /* ARGSUSED */
82455082Storek int
nfs_vptofh(vp,fhp)82548055Smckusick nfs_vptofh(vp, fhp)
82648055Smckusick 	struct vnode *vp;
82738414Smckusick 	struct fid *fhp;
82838414Smckusick {
82939494Smckusick 
83038414Smckusick 	return (EINVAL);
83138414Smckusick }
83238884Smacklem 
83338884Smacklem /*
83438884Smacklem  * Vfs start routine, a no-op.
83538884Smacklem  */
83639494Smckusick /* ARGSUSED */
83755082Storek int
nfs_start(mp,flags,p)83848055Smckusick nfs_start(mp, flags, p)
83938884Smacklem 	struct mount *mp;
84038884Smacklem 	int flags;
84148055Smckusick 	struct proc *p;
84238884Smacklem {
84339494Smckusick 
84438884Smacklem 	return (0);
84538884Smacklem }
84641294Smckusick 
84741294Smckusick /*
84841294Smckusick  * Do operations associated with quotas, not supported
84941294Smckusick  */
85051574Smckusick /* ARGSUSED */
85155082Storek int
nfs_quotactl(mp,cmd,uid,arg,p)85248055Smckusick nfs_quotactl(mp, cmd, uid, arg, p)
85341294Smckusick 	struct mount *mp;
85441294Smckusick 	int cmd;
85554450Smckusick 	uid_t uid;
85641294Smckusick 	caddr_t arg;
85748055Smckusick 	struct proc *p;
85841294Smckusick {
85951574Smckusick 
86041294Smckusick 	return (EOPNOTSUPP);
86141294Smckusick }
86268653Smckusick 
86368653Smckusick /*
86468653Smckusick  * Do that sysctl thang...
86568653Smckusick  */
86668653Smckusick static int
nfs_sysctl(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen,struct proc * p)86768653Smckusick nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
86868653Smckusick 	   size_t newlen, struct proc *p)
86968653Smckusick {
87068653Smckusick 	int rv;
87168653Smckusick 
87268653Smckusick 	/*
87368653Smckusick 	 * All names at this level are terminal.
87468653Smckusick 	 */
87568653Smckusick 	if(namelen > 1)
87668653Smckusick 		return ENOTDIR;	/* overloaded */
87768653Smckusick 
87868653Smckusick 	switch(name[0]) {
87968653Smckusick 	case NFS_NFSSTATS:
88068653Smckusick 		if(!oldp) {
88168653Smckusick 			*oldlenp = sizeof nfsstats;
88268653Smckusick 			return 0;
88368653Smckusick 		}
88468653Smckusick 
88568653Smckusick 		if(*oldlenp < sizeof nfsstats) {
88668653Smckusick 			*oldlenp = sizeof nfsstats;
88768653Smckusick 			return ENOMEM;
88868653Smckusick 		}
88968653Smckusick 
89068653Smckusick 		rv = copyout(&nfsstats, oldp, sizeof nfsstats);
89168653Smckusick 		if(rv) return rv;
89268653Smckusick 
89368653Smckusick 		if(newp && newlen != sizeof nfsstats)
89468653Smckusick 			return EINVAL;
89568653Smckusick 
89668653Smckusick 		if(newp) {
89768653Smckusick 			return copyin(newp, &nfsstats, sizeof nfsstats);
89868653Smckusick 		}
89968653Smckusick 		return 0;
90068653Smckusick 
90168653Smckusick 	default:
90268653Smckusick 		return EOPNOTSUPP;
90368653Smckusick 	}
90468653Smckusick }
90568653Smckusick 
906