nfs_clvfsops.c revision 191990
1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993, 1995
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem *	from nfs_vfsops.c	8.12 (Berkeley) 5/20/95
33191783Srmacklem */
34191783Srmacklem
35191783Srmacklem#include <sys/cdefs.h>
36191783Srmacklem__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clvfsops.c 191990 2009-05-11 15:33:26Z attilio $");
37191783Srmacklem
38191783Srmacklem
39191783Srmacklem#include "opt_bootp.h"
40191783Srmacklem#include "opt_nfsroot.h"
41191783Srmacklem
42191783Srmacklem#include <sys/param.h>
43191783Srmacklem#include <sys/systm.h>
44191783Srmacklem#include <sys/kernel.h>
45191783Srmacklem#include <sys/bio.h>
46191783Srmacklem#include <sys/buf.h>
47191783Srmacklem#include <sys/clock.h>
48191783Srmacklem#include <sys/lock.h>
49191783Srmacklem#include <sys/malloc.h>
50191783Srmacklem#include <sys/mbuf.h>
51191783Srmacklem#include <sys/module.h>
52191783Srmacklem#include <sys/mount.h>
53191783Srmacklem#include <sys/proc.h>
54191783Srmacklem#include <sys/socket.h>
55191783Srmacklem#include <sys/socketvar.h>
56191783Srmacklem#include <sys/sockio.h>
57191783Srmacklem#include <sys/sysctl.h>
58191783Srmacklem#include <sys/vnode.h>
59191783Srmacklem#include <sys/signalvar.h>
60191783Srmacklem
61191783Srmacklem#include <vm/vm.h>
62191783Srmacklem#include <vm/vm_extern.h>
63191783Srmacklem#include <vm/uma.h>
64191783Srmacklem
65191783Srmacklem#include <net/if.h>
66191783Srmacklem#include <net/route.h>
67191783Srmacklem#include <netinet/in.h>
68191783Srmacklem
69191783Srmacklem#include <fs/nfs/nfsport.h>
70191783Srmacklem#include <fs/nfsclient/nfsnode.h>
71191783Srmacklem#include <fs/nfsclient/nfsmount.h>
72191783Srmacklem#include <fs/nfsclient/nfs.h>
73191783Srmacklem#include <fs/nfsclient/nfsdiskless.h>
74191783Srmacklem
75191783Srmacklemextern int nfscl_ticks;
76191783Srmacklemextern struct timeval nfsboottime;
77191783Srmacklemextern struct nfsstats	newnfsstats;
78191783Srmacklem
79191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
80191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
81191783Srmacklem
82191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
83191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW,
84191783Srmacklem	&newnfsstats, nfsstats, "S,nfsstats");
85191783Srmacklemstatic int nfs_ip_paranoia = 1;
86191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
87191783Srmacklem    &nfs_ip_paranoia, 0, "");
88191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
89191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_INITIAL_DELAY,
90191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
91191783Srmacklem/* how long between console messages "nfs server foo not responding" */
92191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
93191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY,
94191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
95191783Srmacklem
96191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
97191783Srmacklem		    struct nfs_args *argp, struct ucred *, struct thread *);
98191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
99191783Srmacklem		    struct sockaddr *, char *, u_char *, u_char *, u_char *,
100191783Srmacklem		    struct vnode **, struct ucred *, struct thread *);
101191783Srmacklemstatic vfs_mount_t nfs_mount;
102191783Srmacklemstatic vfs_cmount_t nfs_cmount;
103191783Srmacklemstatic vfs_unmount_t nfs_unmount;
104191783Srmacklemstatic vfs_root_t nfs_root;
105191783Srmacklemstatic vfs_statfs_t nfs_statfs;
106191783Srmacklemstatic vfs_sync_t nfs_sync;
107191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
108191783Srmacklem
109191783Srmacklem/*
110191783Srmacklem * nfs vfs operations.
111191783Srmacklem */
112191783Srmacklemstatic struct vfsops nfs_vfsops = {
113191783Srmacklem	.vfs_init =		ncl_init,
114191783Srmacklem	.vfs_mount =		nfs_mount,
115191783Srmacklem	.vfs_cmount =		nfs_cmount,
116191783Srmacklem	.vfs_root =		nfs_root,
117191783Srmacklem	.vfs_statfs =		nfs_statfs,
118191783Srmacklem	.vfs_sync =		nfs_sync,
119191783Srmacklem	.vfs_uninit =		ncl_uninit,
120191783Srmacklem	.vfs_unmount =		nfs_unmount,
121191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
122191783Srmacklem};
123191783SrmacklemVFS_SET(nfs_vfsops, newnfs, VFCF_NETWORK);
124191783Srmacklem
125191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
126191783SrmacklemMODULE_VERSION(newnfs, 1);
127191783Srmacklem
128191783Srmacklem/*
129191783Srmacklem * This structure must be filled in by a primary bootstrap or bootstrap
130191783Srmacklem * server for a diskless/dataless machine. It is initialized below just
131191783Srmacklem * to ensure that it is allocated to initialized data (.data not .bss).
132191783Srmacklem */
133191783Srmacklemstruct nfs_diskless newnfs_diskless = { { { 0 } } };
134191783Srmacklemstruct nfsv3_diskless newnfsv3_diskless = { { { 0 } } };
135191783Srmacklemint newnfs_diskless_valid = 0;
136191783Srmacklem
137191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
138191783Srmacklem	&newnfs_diskless_valid, 0, "");
139191783Srmacklem
140191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
141191783Srmacklem	newnfsv3_diskless.root_hostnam, 0, "");
142191783Srmacklem
143191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
144191783Srmacklem	&newnfsv3_diskless.root_saddr, sizeof newnfsv3_diskless.root_saddr,
145191783Srmacklem	"%Ssockaddr_in", "");
146191783Srmacklem
147191783Srmacklem
148191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
149191783Srmacklemstatic int	nfs_mountdiskless(char *,
150191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
151191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
152191783Srmacklemstatic void	nfs_convert_diskless(void);
153191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
154191783Srmacklem		    struct onfs_args *oargs);
155191783Srmacklem
156191783Srmacklemint
157191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
158191783Srmacklem{
159191783Srmacklem	int iosize, maxio;
160191783Srmacklem
161191783Srmacklem	/* First, set the upper limit for iosize */
162191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
163191783Srmacklem		maxio = NFS_MAXBSIZE;
164191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
165191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
166191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
167191783Srmacklem		else
168191783Srmacklem			maxio = NFS_MAXBSIZE;
169191783Srmacklem	} else {
170191783Srmacklem		maxio = NFS_V2MAXDATA;
171191783Srmacklem	}
172191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
173191783Srmacklem		nmp->nm_rsize = maxio;
174191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
175191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
176191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
177191783Srmacklem		nmp->nm_readdirsize = maxio;
178191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
179191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
180191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
181191783Srmacklem		nmp->nm_wsize = maxio;
182191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
183191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
184191783Srmacklem
185191783Srmacklem	/*
186191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
187191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
188191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
189191783Srmacklem	 * space.
190191783Srmacklem	 */
191191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
192191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
193191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
194191783Srmacklem	return (iosize);
195191783Srmacklem}
196191783Srmacklem
197191783Srmacklemstatic void
198191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
199191783Srmacklem{
200191783Srmacklem
201191783Srmacklem	args->version = NFS_ARGSVERSION;
202191783Srmacklem	args->addr = oargs->addr;
203191783Srmacklem	args->addrlen = oargs->addrlen;
204191783Srmacklem	args->sotype = oargs->sotype;
205191783Srmacklem	args->proto = oargs->proto;
206191783Srmacklem	args->fh = oargs->fh;
207191783Srmacklem	args->fhsize = oargs->fhsize;
208191783Srmacklem	args->flags = oargs->flags;
209191783Srmacklem	args->wsize = oargs->wsize;
210191783Srmacklem	args->rsize = oargs->rsize;
211191783Srmacklem	args->readdirsize = oargs->readdirsize;
212191783Srmacklem	args->timeo = oargs->timeo;
213191783Srmacklem	args->retrans = oargs->retrans;
214191783Srmacklem	args->readahead = oargs->readahead;
215191783Srmacklem	args->hostname = oargs->hostname;
216191783Srmacklem}
217191783Srmacklem
218191783Srmacklemstatic void
219191783Srmacklemnfs_convert_diskless(void)
220191783Srmacklem{
221191783Srmacklem
222191783Srmacklem	bcopy(&newnfs_diskless.myif, &newnfsv3_diskless.myif,
223191783Srmacklem		sizeof(struct ifaliasreq));
224191783Srmacklem	bcopy(&newnfs_diskless.mygateway, &newnfsv3_diskless.mygateway,
225191783Srmacklem		sizeof(struct sockaddr_in));
226191783Srmacklem	nfs_convert_oargs(&newnfsv3_diskless.root_args,&newnfs_diskless.root_args);
227191783Srmacklem	if (newnfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
228191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_MYFH;
229191783Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh, NFSX_MYFH);
230191783Srmacklem	} else {
231191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_V2FH;
232191783Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh, NFSX_V2FH);
233191783Srmacklem	}
234191783Srmacklem	bcopy(&newnfs_diskless.root_saddr,&newnfsv3_diskless.root_saddr,
235191783Srmacklem		sizeof(struct sockaddr_in));
236191783Srmacklem	bcopy(newnfs_diskless.root_hostnam, newnfsv3_diskless.root_hostnam, MNAMELEN);
237191783Srmacklem	newnfsv3_diskless.root_time = newnfs_diskless.root_time;
238191783Srmacklem	bcopy(newnfs_diskless.my_hostnam, newnfsv3_diskless.my_hostnam,
239191783Srmacklem		MAXHOSTNAMELEN);
240191783Srmacklem	newnfs_diskless_valid = 3;
241191783Srmacklem}
242191783Srmacklem
243191783Srmacklem/*
244191783Srmacklem * nfs statfs call
245191783Srmacklem */
246191783Srmacklemstatic int
247191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
248191783Srmacklem{
249191783Srmacklem	struct vnode *vp;
250191990Sattilio	struct thread *td;
251191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
252191783Srmacklem	struct nfsvattr nfsva;
253191783Srmacklem	struct nfsfsinfo fs;
254191783Srmacklem	struct nfsstatfs sb;
255191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
256191783Srmacklem	struct nfsnode *np;
257191783Srmacklem
258191990Sattilio	td = curthread;
259191990Sattilio
260191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
261191783Srmacklem	if (error)
262191783Srmacklem		return (error);
263191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
264191783Srmacklem	if (error) {
265191783Srmacklem		vfs_unbusy(mp);
266191783Srmacklem		return (error);
267191783Srmacklem	}
268191783Srmacklem	vp = NFSTOV(np);
269191783Srmacklem	mtx_lock(&nmp->nm_mtx);
270191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
271191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
272191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
273191783Srmacklem		    &attrflag, NULL);
274191783Srmacklem		if (!error)
275191783Srmacklem			gotfsinfo = 1;
276191783Srmacklem	} else
277191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
278191783Srmacklem	if (!error)
279191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
280191783Srmacklem		    &attrflag, NULL);
281191783Srmacklem	if (attrflag == 0) {
282191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
283191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
284191783Srmacklem		if (ret) {
285191783Srmacklem			/*
286191783Srmacklem			 * Just set default values to get things going.
287191783Srmacklem			 */
288191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
289191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
290191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
291191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
292191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
293191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
294191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
295191783Srmacklem			nfsva.na_vattr.va_gen = 1;
296191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
297191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
298191783Srmacklem		}
299191783Srmacklem	}
300191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
301191783Srmacklem	if (!error) {
302191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
303191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
304191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
305191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
306191783Srmacklem	    sbp->f_flags = nmp->nm_flag;
307191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
308191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
309191783Srmacklem	    if (sbp != &mp->mnt_stat) {
310191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
311191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
312191783Srmacklem	    }
313191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
314191783Srmacklem	} else if (NFS_ISV4(vp)) {
315191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
316191783Srmacklem	}
317191783Srmacklem	vput(vp);
318191783Srmacklem	vfs_unbusy(mp);
319191783Srmacklem	return (error);
320191783Srmacklem}
321191783Srmacklem
322191783Srmacklem/*
323191783Srmacklem * nfs version 3 fsinfo rpc call
324191783Srmacklem */
325191783Srmacklemint
326191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
327191783Srmacklem    struct thread *td)
328191783Srmacklem{
329191783Srmacklem	struct nfsfsinfo fs;
330191783Srmacklem	struct nfsvattr nfsva;
331191783Srmacklem	int error, attrflag;
332191783Srmacklem
333191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
334191783Srmacklem	if (!error) {
335191783Srmacklem		if (attrflag)
336191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
337191783Srmacklem			    1);
338191783Srmacklem		mtx_lock(&nmp->nm_mtx);
339191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
340191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
341191783Srmacklem	}
342191783Srmacklem	return (error);
343191783Srmacklem}
344191783Srmacklem
345191783Srmacklem/*
346191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
347191783Srmacklem * newnfs_diskless structure that has been filled in properly by some primary
348191783Srmacklem * bootstrap.
349191783Srmacklem * It goes something like this:
350191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
351191783Srmacklem *   can talk to the server
352191783Srmacklem * - If newnfs_diskless.mygateway is filled in, use that address as
353191783Srmacklem *   a default gateway.
354191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
355191783Srmacklem *
356191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
357191783Srmacklem * structure, as well as other global NFS client variables here, as
358191783Srmacklem * ncl_mountroot() will be called once in the boot before any other NFS
359191783Srmacklem * client activity occurs.
360191783Srmacklem */
361191783Srmacklemint
362191783Srmacklemncl_mountroot(struct mount *mp, struct thread *td)
363191783Srmacklem{
364191783Srmacklem	struct nfsv3_diskless *nd = &newnfsv3_diskless;
365191783Srmacklem	struct socket *so;
366191783Srmacklem	struct vnode *vp;
367191783Srmacklem	struct ifreq ir;
368191783Srmacklem	int error, i;
369191783Srmacklem	u_long l;
370191783Srmacklem	char buf[128];
371191783Srmacklem	char *cp;
372191783Srmacklem
373191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
374191783Srmacklem	bootpc_init();		/* use bootp to get newnfs_diskless filled in */
375191783Srmacklem#elif defined(NFS_ROOT)
376191783Srmacklem	nfs_setup_diskless();
377191783Srmacklem#endif
378191783Srmacklem
379191783Srmacklem	nfscl_init();
380191783Srmacklem
381191783Srmacklem	if (newnfs_diskless_valid == 0)
382191783Srmacklem		return (-1);
383191783Srmacklem	if (newnfs_diskless_valid == 1)
384191783Srmacklem		nfs_convert_diskless();
385191783Srmacklem
386191783Srmacklem	/*
387191783Srmacklem	 * XXX splnet, so networks will receive...
388191783Srmacklem	 */
389191783Srmacklem	splnet();
390191783Srmacklem
391191783Srmacklem	/*
392191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
393191783Srmacklem	 * talk to the server.
394191783Srmacklem	 */
395191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
396191783Srmacklem	    td->td_ucred, td);
397191783Srmacklem	if (error)
398191783Srmacklem		panic("ncl_mountroot: socreate(%04x): %d",
399191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
400191783Srmacklem
401191783Srmacklem#if 0 /* XXX Bad idea */
402191783Srmacklem	/*
403191783Srmacklem	 * We might not have been told the right interface, so we pass
404191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
405191783Srmacklem	 * one of them configured.
406191783Srmacklem	 */
407191783Srmacklem
408191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
409191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
410191783Srmacklem		nd->myif.ifra_name[i] <= '9';
411191783Srmacklem		nd->myif.ifra_name[i] ++) {
412191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
413191783Srmacklem		if(!error)
414191783Srmacklem			break;
415191783Srmacklem	}
416191783Srmacklem#endif
417191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
418191783Srmacklem	if (error)
419191783Srmacklem		panic("ncl_mountroot: SIOCAIFADDR: %d", error);
420191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
421191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
422191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
423191783Srmacklem		freeenv(cp);
424191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
425191783Srmacklem		if (error)
426191783Srmacklem			printf("ncl_mountroot: SIOCSIFMTU: %d", error);
427191783Srmacklem	}
428191783Srmacklem	soclose(so);
429191783Srmacklem
430191783Srmacklem	/*
431191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
432191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
433191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
434191783Srmacklem	 * to avoid panicking inappropriately in that situation.
435191783Srmacklem	 */
436191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
437191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
438191783Srmacklem		struct sockaddr_in mask, sin;
439191783Srmacklem
440191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
441191783Srmacklem		sin = mask;
442191783Srmacklem		sin.sin_family = AF_INET;
443191783Srmacklem		sin.sin_len = sizeof(sin);
444191783Srmacklem		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
445191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
446191783Srmacklem		    (struct sockaddr *)&mask,
447191783Srmacklem		    RTF_UP | RTF_GATEWAY, NULL);
448191783Srmacklem		if (error)
449191783Srmacklem			panic("ncl_mountroot: RTM_ADD: %d", error);
450191783Srmacklem	}
451191783Srmacklem
452191783Srmacklem	/*
453191783Srmacklem	 * Create the rootfs mount point.
454191783Srmacklem	 */
455191783Srmacklem	nd->root_args.fh = nd->root_fh;
456191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
457191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
458191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
459191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
460191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
461191783Srmacklem	printf("NFS ROOT: %s\n", buf);
462191783Srmacklem	if ((error = nfs_mountdiskless(buf,
463191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
464191783Srmacklem		return (error);
465191783Srmacklem	}
466191783Srmacklem
467191783Srmacklem	/*
468191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
469191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
470191783Srmacklem	 * mount the right /var based upon its preset value.
471191783Srmacklem	 */
472191783Srmacklem	bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
473191783Srmacklem	hostname[MAXHOSTNAMELEN - 1] = '\0';
474191783Srmacklem	for (i = 0; i < MAXHOSTNAMELEN; i++)
475191783Srmacklem		if (hostname[i] == '\0')
476191783Srmacklem			break;
477191783Srmacklem	inittodr(ntohl(nd->root_time));
478191783Srmacklem	return (0);
479191783Srmacklem}
480191783Srmacklem
481191783Srmacklem/*
482191783Srmacklem * Internal version of mount system call for diskless setup.
483191783Srmacklem */
484191783Srmacklemstatic int
485191783Srmacklemnfs_mountdiskless(char *path,
486191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
487191783Srmacklem    struct vnode **vpp, struct mount *mp)
488191783Srmacklem{
489191783Srmacklem	struct sockaddr *nam;
490191783Srmacklem	int error;
491191783Srmacklem
492191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
493191783Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, NULL, NULL, vpp,
494191783Srmacklem	    td->td_ucred, td)) != 0) {
495191783Srmacklem		printf("ncl_mountroot: mount %s on /: %d\n", path, error);
496191783Srmacklem		return (error);
497191783Srmacklem	}
498191783Srmacklem	return (0);
499191783Srmacklem}
500191783Srmacklem
501191783Srmacklemstatic void
502191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
503191783Srmacklem    struct ucred *cred, struct thread *td)
504191783Srmacklem{
505191783Srmacklem	int s;
506191783Srmacklem	int adjsock;
507191783Srmacklem
508191783Srmacklem	s = splnet();
509191783Srmacklem
510191783Srmacklem	/*
511191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
512191783Srmacklem	 * an update.  If this is not an update, then either the read-only
513191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
514191783Srmacklem	 * intentionally at some previous point.
515191783Srmacklem	 */
516191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
517191783Srmacklem		MNT_ILOCK(mp);
518191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
519191783Srmacklem		MNT_IUNLOCK(mp);
520191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
521191783Srmacklem		MNT_ILOCK(mp);
522191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
523191783Srmacklem		MNT_IUNLOCK(mp);
524191783Srmacklem	}
525191783Srmacklem
526191783Srmacklem	/*
527191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
528191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
529191783Srmacklem	 * and soft timeout behavior.
530191783Srmacklem	 */
531191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
532191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
533191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
534191783Srmacklem	}
535191783Srmacklem
536191783Srmacklem	/* Also clear RDIRPLUS if not NFSv3, it crashes some servers */
537191783Srmacklem	if ((argp->flags & NFSMNT_NFSV3) == 0)
538191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
539191783Srmacklem
540191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
541191783Srmacklem	adjsock = ((nmp->nm_flag & NFSMNT_NOCONN) !=
542191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
543191783Srmacklem
544191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
545191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
546191783Srmacklem	splx(s);
547191783Srmacklem
548191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
549191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
550191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
551191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
552191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
553191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
554191783Srmacklem	}
555191783Srmacklem
556191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
557191783Srmacklem		nmp->nm_retry = argp->retrans;
558191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
559191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
560191783Srmacklem	}
561191783Srmacklem
562191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
563191783Srmacklem		nmp->nm_wsize = argp->wsize;
564191783Srmacklem		/* Round down to multiple of blocksize */
565191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
566191783Srmacklem		if (nmp->nm_wsize <= 0)
567191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
568191783Srmacklem	}
569191783Srmacklem
570191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
571191783Srmacklem		nmp->nm_rsize = argp->rsize;
572191783Srmacklem		/* Round down to multiple of blocksize */
573191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
574191783Srmacklem		if (nmp->nm_rsize <= 0)
575191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
576191783Srmacklem	}
577191783Srmacklem
578191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
579191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
580191783Srmacklem	}
581191783Srmacklem
582191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
583191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
584191783Srmacklem	else
585191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
586191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
587191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
588191783Srmacklem	else
589191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
590191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
591191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
592191783Srmacklem	else
593191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
594191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
595191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
596191783Srmacklem	else
597191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
598191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
599191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
600191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
601191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
602191783Srmacklem
603191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
604191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
605191783Srmacklem			nmp->nm_readahead = argp->readahead;
606191783Srmacklem		else
607191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
608191783Srmacklem	}
609191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
610191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
611191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
612191783Srmacklem		else
613191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
614191783Srmacklem	}
615191783Srmacklem
616191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
617191783Srmacklem		    (nmp->nm_soproto != argp->proto));
618191783Srmacklem
619191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
620191783Srmacklem		int haslock = 0, error = 0;
621191783Srmacklem
622191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
623191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
624191783Srmacklem			if (!error)
625191783Srmacklem				haslock = 1;
626191783Srmacklem		}
627191783Srmacklem		if (!error) {
628191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
629191783Srmacklem		    if (haslock)
630191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
631191783Srmacklem		    nmp->nm_sotype = argp->sotype;
632191783Srmacklem		    nmp->nm_soproto = argp->proto;
633191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
634191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
635191783Srmacklem			    cred, td, 0)) {
636191783Srmacklem				printf("newnfs_args: retrying connect\n");
637191783Srmacklem				(void) nfs_catnap(PSOCK, "newnfscon");
638191783Srmacklem			}
639191783Srmacklem		}
640191783Srmacklem	} else {
641191783Srmacklem		nmp->nm_sotype = argp->sotype;
642191783Srmacklem		nmp->nm_soproto = argp->proto;
643191783Srmacklem	}
644191783Srmacklem}
645191783Srmacklem
646191783Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
647191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
648191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
649191783Srmacklem    "async", "dumbtimer", "noconn", "nolockd", "intr", "rdirplus", "resvport",
650191783Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "wsize", "rsize",
651191783Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax",
652191783Srmacklem    NULL };
653191783Srmacklem
654191783Srmacklem/*
655191783Srmacklem * VFS Operations.
656191783Srmacklem *
657191783Srmacklem * mount system call
658191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
659191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
660191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
661191783Srmacklem * an error after that means that I have to release the mbuf.
662191783Srmacklem */
663191783Srmacklem/* ARGSUSED */
664191783Srmacklemstatic int
665191990Sattilionfs_mount(struct mount *mp)
666191783Srmacklem{
667191783Srmacklem	struct nfs_args args = {
668191783Srmacklem	    .version = NFS_ARGSVERSION,
669191783Srmacklem	    .addr = NULL,
670191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
671191783Srmacklem	    .sotype = SOCK_STREAM,
672191783Srmacklem	    .proto = 0,
673191783Srmacklem	    .fh = NULL,
674191783Srmacklem	    .fhsize = 0,
675191783Srmacklem	    .flags = 0,
676191783Srmacklem	    .wsize = NFS_WSIZE,
677191783Srmacklem	    .rsize = NFS_RSIZE,
678191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
679191783Srmacklem	    .timeo = 10,
680191783Srmacklem	    .retrans = NFS_RETRANS,
681191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
682191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
683191783Srmacklem	    .hostname = NULL,
684191783Srmacklem	    /* args version 4 */
685191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
686191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
687191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
688191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
689191783Srmacklem	    .dirlen = 0,
690191783Srmacklem	    .krbnamelen = 0,
691191783Srmacklem	};
692191783Srmacklem	int error;
693191783Srmacklem	struct sockaddr *nam;
694191783Srmacklem	struct vnode *vp;
695191990Sattilio	struct thread *td;
696191783Srmacklem	char hst[MNAMELEN];
697191783Srmacklem	size_t len;
698191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
699191783Srmacklem
700191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
701191783Srmacklem		error = EINVAL;
702191783Srmacklem		goto out;
703191783Srmacklem	}
704191783Srmacklem
705191990Sattilio	td = curthread;
706191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
707191783Srmacklem		error = ncl_mountroot(mp, td);
708191783Srmacklem		goto out;
709191783Srmacklem	}
710191783Srmacklem
711191783Srmacklem	error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args, sizeof args);
712191783Srmacklem	if (error)
713191783Srmacklem		goto out;
714191783Srmacklem
715191783Srmacklem	if (args.version != NFS_ARGSVERSION) {
716191783Srmacklem		error = EPROGMISMATCH;
717191783Srmacklem		goto out;
718191783Srmacklem	}
719191783Srmacklem
720191783Srmacklem	nfscl_init();
721191783Srmacklem
722191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
723191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
724191783Srmacklem
725191783Srmacklem		if (nmp == NULL) {
726191783Srmacklem			error = EIO;
727191783Srmacklem			goto out;
728191783Srmacklem		}
729191783Srmacklem		/*
730191783Srmacklem		 * When doing an update, we can't change version,
731191783Srmacklem		 * security, switch lockd strategies or change cookie
732191783Srmacklem		 * translation
733191783Srmacklem		 */
734191783Srmacklem		args.flags = (args.flags &
735191783Srmacklem		    ~(NFSMNT_NFSV3 |
736191783Srmacklem		      NFSMNT_NFSV4 |
737191783Srmacklem		      NFSMNT_KERB |
738191783Srmacklem		      NFSMNT_INTEGRITY |
739191783Srmacklem		      NFSMNT_PRIVACY |
740191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
741191783Srmacklem		    (nmp->nm_flag &
742191783Srmacklem			(NFSMNT_NFSV3 |
743191783Srmacklem			 NFSMNT_NFSV4 |
744191783Srmacklem			 NFSMNT_KERB |
745191783Srmacklem			 NFSMNT_INTEGRITY |
746191783Srmacklem			 NFSMNT_PRIVACY |
747191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
748191783Srmacklem		nfs_decode_args(mp, nmp, &args, td->td_ucred, td);
749191783Srmacklem		goto out;
750191783Srmacklem	}
751191783Srmacklem
752191783Srmacklem	/*
753191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
754191783Srmacklem	 * or no-connection mode for those protocols that support
755191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
756191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
757191783Srmacklem	 * to receive replies from a different IP then the request was
758191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
759191783Srmacklem	 * not 0.
760191783Srmacklem	 */
761191783Srmacklem	if (nfs_ip_paranoia == 0)
762191783Srmacklem		args.flags |= NFSMNT_NOCONN;
763191783Srmacklem	if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
764191783Srmacklem		error = EINVAL;
765191783Srmacklem		goto out;
766191783Srmacklem	}
767191783Srmacklem	if (args.fhsize > 0) {
768191783Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
769191783Srmacklem		if (error)
770191783Srmacklem			goto out;
771191783Srmacklem	}
772191783Srmacklem	error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
773191783Srmacklem	if (error)
774191783Srmacklem		goto out;
775191783Srmacklem	bzero(&hst[len], MNAMELEN - len);
776191783Srmacklem	if (args.krbnamelen > 0) {
777191783Srmacklem		if (args.krbnamelen >= 100) {
778191783Srmacklem			error = EINVAL;
779191783Srmacklem			goto out;
780191783Srmacklem		}
781191783Srmacklem		error = copyin(args.krbname, krbname, args.krbnamelen);
782191783Srmacklem		if (error)
783191783Srmacklem			goto out;
784191783Srmacklem		krbname[args.krbnamelen] = '\0';
785191783Srmacklem	} else {
786191783Srmacklem		krbname[0] = '\0';
787191783Srmacklem		args.krbnamelen = 0;
788191783Srmacklem	}
789191783Srmacklem	if (args.dirlen > 0) {
790191783Srmacklem		if (args.dirlen >= 100) {
791191783Srmacklem			error = EINVAL;
792191783Srmacklem			goto out;
793191783Srmacklem		}
794191783Srmacklem		error = copyin(args.dirpath, dirpath, args.dirlen);
795191783Srmacklem		if (error)
796191783Srmacklem			goto out;
797191783Srmacklem		dirpath[args.dirlen] = '\0';
798191783Srmacklem	} else {
799191783Srmacklem		dirpath[0] = '\0';
800191783Srmacklem		args.dirlen = 0;
801191783Srmacklem	}
802191783Srmacklem	if (args.srvkrbnamelen > 0) {
803191783Srmacklem		if (args.srvkrbnamelen >= 100) {
804191783Srmacklem			error = EINVAL;
805191783Srmacklem			goto out;
806191783Srmacklem		}
807191783Srmacklem		error = copyin(args.srvkrbname, srvkrbname, args.srvkrbnamelen);
808191783Srmacklem		if (error)
809191783Srmacklem			goto out;
810191783Srmacklem		srvkrbname[args.srvkrbnamelen] = '\0';
811191783Srmacklem	} else {
812191783Srmacklem		srvkrbname[0] = '\0';
813191783Srmacklem		args.srvkrbnamelen = 0;
814191783Srmacklem	}
815191783Srmacklem	/* sockargs() call must be after above copyin() calls */
816191783Srmacklem	error = getsockaddr(&nam, (caddr_t)args.addr, args.addrlen);
817191783Srmacklem	if (error)
818191783Srmacklem		goto out;
819191783Srmacklem	args.fh = nfh;
820191783Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, dirpath, srvkrbname,
821191783Srmacklem	    &vp, td->td_ucred, td);
822191783Srmacklemout:
823191783Srmacklem	if (!error) {
824191783Srmacklem		MNT_ILOCK(mp);
825191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
826191783Srmacklem		MNT_IUNLOCK(mp);
827191783Srmacklem	}
828191783Srmacklem	return (error);
829191783Srmacklem}
830191783Srmacklem
831191783Srmacklem
832191783Srmacklem/*
833191783Srmacklem * VFS Operations.
834191783Srmacklem *
835191783Srmacklem * mount system call
836191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
837191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
838191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
839191783Srmacklem * an error after that means that I have to release the mbuf.
840191783Srmacklem */
841191783Srmacklem/* ARGSUSED */
842191783Srmacklemstatic int
843191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags)
844191783Srmacklem{
845191783Srmacklem	int error;
846191783Srmacklem	struct nfs_args args;
847191783Srmacklem
848191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
849191783Srmacklem	if (error)
850191783Srmacklem		return error;
851191783Srmacklem
852191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
853191783Srmacklem
854191783Srmacklem	error = kernel_mount(ma, flags);
855191783Srmacklem	return (error);
856191783Srmacklem}
857191783Srmacklem
858191783Srmacklem/*
859191783Srmacklem * Common code for mount and mountroot
860191783Srmacklem */
861191783Srmacklemstatic int
862191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
863191783Srmacklem    char *hst, u_char *krbname, u_char *dirpath, u_char *srvkrbname,
864191783Srmacklem    struct vnode **vpp, struct ucred *cred, struct thread *td)
865191783Srmacklem{
866191783Srmacklem	struct nfsmount *nmp;
867191783Srmacklem	struct nfsnode *np;
868191783Srmacklem	int error, trycnt, ret, clearintr;
869191783Srmacklem	struct nfsvattr nfsva;
870191783Srmacklem	static u_int64_t clval = 0;
871191783Srmacklem
872191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
873191783Srmacklem		nmp = VFSTONFS(mp);
874191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
875191783Srmacklem		FREE(nam, M_SONAME);
876191783Srmacklem		return (0);
877191783Srmacklem	} else {
878191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
879191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2,
880191783Srmacklem		    M_NEWNFSMNT, M_WAITOK);
881191783Srmacklem		bzero((caddr_t)nmp, sizeof (struct nfsmount) +
882191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2);
883191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
884191783Srmacklem		if (clval == 0)
885191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
886191783Srmacklem		nmp->nm_clval = clval++;
887191783Srmacklem		nmp->nm_krbnamelen = argp->krbnamelen;
888191783Srmacklem		nmp->nm_dirpathlen = argp->dirlen;
889191783Srmacklem		nmp->nm_srvkrbnamelen = argp->srvkrbnamelen;
890191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
891191783Srmacklem			/*
892191783Srmacklem			 * Since we will be doing dirpath as root,
893191783Srmacklem			 * set nm_uid to the real uid doing the mount,
894191783Srmacklem			 * since that is normally the user with a valid TGT.
895191783Srmacklem			 */
896191783Srmacklem			nmp->nm_uid = td->td_ucred->cr_ruid;
897191783Srmacklem		} else {
898191783Srmacklem			/*
899191783Srmacklem			 * Just set to -1, so the first Op
900191783Srmacklem			 * will set it later, to the uid of
901191783Srmacklem			 * the process doing that (usually
902191783Srmacklem			 * from a first open in the mount
903191783Srmacklem			 * point).
904191783Srmacklem			 */
905191783Srmacklem			nmp->nm_uid = (uid_t)-1;
906191783Srmacklem		}
907191783Srmacklem
908191783Srmacklem		/* Copy and null terminate all the names */
909191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
910191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
911191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
912191783Srmacklem		}
913191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
914191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
915191783Srmacklem			    nmp->nm_dirpathlen);
916191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
917191783Srmacklem			    + 1] = '\0';
918191783Srmacklem		}
919191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
920191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
921191783Srmacklem			    nmp->nm_srvkrbnamelen);
922191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
923191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
924191783Srmacklem		}
925191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
926191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
927191783Srmacklem		mp->mnt_data = nmp;
928191783Srmacklem	}
929191783Srmacklem	vfs_getnewfsid(mp);
930191783Srmacklem	nmp->nm_mountp = mp;
931191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
932191783Srmacklem
933191783Srmacklem	/*
934191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
935191783Srmacklem	 * high, depending on whether we end up with negative offsets in
936191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
937191783Srmacklem	 *
938191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
939191783Srmacklem	 * that we can handle until we find out otherwise.
940191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
941191783Srmacklem	 * buffer cache using signed(!) block numbers.
942191783Srmacklem	 */
943191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
944191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
945191783Srmacklem	else
946191783Srmacklem		nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1;
947191783Srmacklem
948191783Srmacklem	nmp->nm_timeo = NFS_TIMEO;
949191783Srmacklem	nmp->nm_retry = NFS_RETRANS;
950191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
951191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
952191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
953191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
954191783Srmacklem	}
955191783Srmacklem	nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
956191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
957191783Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
958191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
959191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
960191783Srmacklem		nmp->nm_tprintf_delay = 0;
961191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
962191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
963191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
964191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
965191783Srmacklem	if (nmp->nm_fhsize > 0)
966191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
967191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
968191783Srmacklem	nmp->nm_nam = nam;
969191783Srmacklem	/* Set up the sockets and per-host congestion */
970191783Srmacklem	nmp->nm_sotype = argp->sotype;
971191783Srmacklem	nmp->nm_soproto = argp->proto;
972191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
973191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
974191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
975191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
976191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
977191783Srmacklem	else
978191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
979191783Srmacklem
980191783Srmacklem	nfs_decode_args(mp, nmp, argp, cred, td);
981191783Srmacklem
982191783Srmacklem	/*
983191783Srmacklem	 * For Connection based sockets (TCP,...) do the connect here,
984191783Srmacklem	 * but make it interruptible, even for non-interuptible mounts.
985191783Srmacklem	 */
986191783Srmacklem	if ((nmp->nm_flag & NFSMNT_INT) == 0) {
987191783Srmacklem		nmp->nm_flag |= NFSMNT_INT;
988191783Srmacklem		clearintr = 1;
989191783Srmacklem	} else {
990191783Srmacklem		clearintr = 0;
991191783Srmacklem	}
992191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
993191783Srmacklem		goto bad;
994191783Srmacklem	if (clearintr)
995191783Srmacklem		nmp->nm_flag &= ~NFSMNT_INT;
996191783Srmacklem
997191783Srmacklem	/*
998191783Srmacklem	 * A reference count is needed on the nfsnode representing the
999191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1000191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1001191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1002191783Srmacklem	 * this problem, because one can identify root inodes by their
1003191783Srmacklem	 * number == ROOTINO (2).
1004191783Srmacklem	 */
1005191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1006191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1007191783Srmacklem		/*
1008191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1009191783Srmacklem		 * path needs to be looked up.
1010191783Srmacklem		 */
1011191783Srmacklem		trycnt = 3;
1012191783Srmacklem		do {
1013191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1014191783Srmacklem			    cred, td);
1015191783Srmacklem			if (error)
1016191783Srmacklem				(void) nfs_catnap(PZERO, "nfsgetdirp");
1017191783Srmacklem		} while (error && --trycnt > 0);
1018191783Srmacklem		if (error) {
1019191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1020191783Srmacklem			goto bad;
1021191783Srmacklem		}
1022191783Srmacklem	}
1023191783Srmacklem	if (nmp->nm_fhsize > 0) {
1024191783Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1025191783Srmacklem		if (error)
1026191783Srmacklem			goto bad;
1027191783Srmacklem		*vpp = NFSTOV(np);
1028191783Srmacklem
1029191783Srmacklem		/*
1030191783Srmacklem		 * Get file attributes and transfer parameters for the
1031191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1032191783Srmacklem		 * (*vpp)->v_type with the correct value.
1033191783Srmacklem		 */
1034191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1035191783Srmacklem		    cred, td, &nfsva, NULL);
1036191783Srmacklem		if (ret) {
1037191783Srmacklem			/*
1038191783Srmacklem			 * Just set default values to get things going.
1039191783Srmacklem			 */
1040191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1041191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1042191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1043191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1044191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1045191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1046191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1047191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1048191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1049191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1050191783Srmacklem		}
1051191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1052191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1053191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1054191783Srmacklem
1055191783Srmacklem		/*
1056191783Srmacklem		 * Lose the lock but keep the ref.
1057191783Srmacklem		 */
1058191783Srmacklem		VOP_UNLOCK(*vpp, 0);
1059191783Srmacklem		return (0);
1060191783Srmacklem	}
1061191783Srmacklem	error = EIO;
1062191783Srmacklem
1063191783Srmacklembad:
1064191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1065191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1066191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1067191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1068191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1069191783Srmacklem	FREE(nam, M_SONAME);
1070191783Srmacklem	return (error);
1071191783Srmacklem}
1072191783Srmacklem
1073191783Srmacklem/*
1074191783Srmacklem * unmount system call
1075191783Srmacklem */
1076191783Srmacklemstatic int
1077191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1078191783Srmacklem{
1079191990Sattilio	struct thread *td;
1080191783Srmacklem	struct nfsmount *nmp;
1081191783Srmacklem	int error, flags = 0, trycnt = 0;
1082191783Srmacklem
1083191990Sattilio	td = curthread;
1084191990Sattilio
1085191783Srmacklem	if (mntflags & MNT_FORCE)
1086191783Srmacklem		flags |= FORCECLOSE;
1087191783Srmacklem	nmp = VFSTONFS(mp);
1088191783Srmacklem	/*
1089191783Srmacklem	 * Goes something like this..
1090191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1091191783Srmacklem	 * - Close the socket
1092191783Srmacklem	 * - Free up the data structures
1093191783Srmacklem	 */
1094191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1095191783Srmacklem	if (mntflags & MNT_FORCE) {
1096191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1097191783Srmacklem		if (error)
1098191783Srmacklem			goto out;
1099191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1100191783Srmacklem		nfscl_umount(nmp, td);
1101191783Srmacklem	}
1102191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1103191783Srmacklem	do {
1104191783Srmacklem		error = vflush(mp, 1, flags, td);
1105191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1106191783Srmacklem			(void) nfs_catnap(PSOCK, "newndm");
1107191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1108191783Srmacklem	if (error)
1109191783Srmacklem		goto out;
1110191783Srmacklem
1111191783Srmacklem	/*
1112191783Srmacklem	 * We are now committed to the unmount.
1113191783Srmacklem	 */
1114191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1115191783Srmacklem		nfscl_umount(nmp, td);
1116191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1117191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1118191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1119191783Srmacklem
1120191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1121191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1122191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1123191783Srmacklemout:
1124191783Srmacklem	return (error);
1125191783Srmacklem}
1126191783Srmacklem
1127191783Srmacklem/*
1128191783Srmacklem * Return root of a filesystem
1129191783Srmacklem */
1130191783Srmacklemstatic int
1131191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1132191783Srmacklem{
1133191783Srmacklem	struct vnode *vp;
1134191783Srmacklem	struct nfsmount *nmp;
1135191783Srmacklem	struct nfsnode *np;
1136191783Srmacklem	int error;
1137191783Srmacklem
1138191783Srmacklem	nmp = VFSTONFS(mp);
1139191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1140191783Srmacklem	if (error)
1141191783Srmacklem		return error;
1142191783Srmacklem	vp = NFSTOV(np);
1143191783Srmacklem	/*
1144191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1145191783Srmacklem	 */
1146191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1147191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1148191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1149191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1150191783Srmacklem	} else
1151191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1152191783Srmacklem	if (vp->v_type == VNON)
1153191783Srmacklem	    vp->v_type = VDIR;
1154191783Srmacklem	vp->v_vflag |= VV_ROOT;
1155191783Srmacklem	*vpp = vp;
1156191783Srmacklem	return (0);
1157191783Srmacklem}
1158191783Srmacklem
1159191783Srmacklem/*
1160191783Srmacklem * Flush out the buffer cache
1161191783Srmacklem */
1162191783Srmacklem/* ARGSUSED */
1163191783Srmacklemstatic int
1164191990Sattilionfs_sync(struct mount *mp, int waitfor)
1165191783Srmacklem{
1166191783Srmacklem	struct vnode *vp, *mvp;
1167191990Sattilio	struct thread *td;
1168191783Srmacklem	int error, allerror = 0;
1169191783Srmacklem
1170191990Sattilio	td = curthread;
1171191990Sattilio
1172191783Srmacklem	/*
1173191783Srmacklem	 * Force stale buffer cache information to be flushed.
1174191783Srmacklem	 */
1175191783Srmacklem	MNT_ILOCK(mp);
1176191783Srmacklemloop:
1177191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1178191783Srmacklem		VI_LOCK(vp);
1179191783Srmacklem		MNT_IUNLOCK(mp);
1180191783Srmacklem		/* XXX Racy bv_cnt check. */
1181191783Srmacklem		if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1182191783Srmacklem		    waitfor == MNT_LAZY) {
1183191783Srmacklem			VI_UNLOCK(vp);
1184191783Srmacklem			MNT_ILOCK(mp);
1185191783Srmacklem			continue;
1186191783Srmacklem		}
1187191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1188191783Srmacklem			MNT_ILOCK(mp);
1189191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1190191783Srmacklem			goto loop;
1191191783Srmacklem		}
1192191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1193191783Srmacklem		if (error)
1194191783Srmacklem			allerror = error;
1195191783Srmacklem		VOP_UNLOCK(vp, 0);
1196191783Srmacklem		vrele(vp);
1197191783Srmacklem
1198191783Srmacklem		MNT_ILOCK(mp);
1199191783Srmacklem	}
1200191783Srmacklem	MNT_IUNLOCK(mp);
1201191783Srmacklem	return (allerror);
1202191783Srmacklem}
1203191783Srmacklem
1204191783Srmacklemstatic int
1205191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1206191783Srmacklem{
1207191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1208191783Srmacklem	struct vfsquery vq;
1209191783Srmacklem	int error;
1210191783Srmacklem
1211191783Srmacklem	bzero(&vq, sizeof(vq));
1212191783Srmacklem	switch (op) {
1213191783Srmacklem#if 0
1214191783Srmacklem	case VFS_CTL_NOLOCKS:
1215191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1216191783Srmacklem 		if (req->oldptr != NULL) {
1217191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1218191783Srmacklem 			if (error)
1219191783Srmacklem 				return (error);
1220191783Srmacklem 		}
1221191783Srmacklem 		if (req->newptr != NULL) {
1222191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1223191783Srmacklem 			if (error)
1224191783Srmacklem 				return (error);
1225191783Srmacklem			if (val)
1226191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1227191783Srmacklem			else
1228191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1229191783Srmacklem 		}
1230191783Srmacklem		break;
1231191783Srmacklem#endif
1232191783Srmacklem	case VFS_CTL_QUERY:
1233191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1234191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1235191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1236191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1237191783Srmacklem#if 0
1238191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1239191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1240191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1241191783Srmacklem#endif
1242191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1243191783Srmacklem		break;
1244191783Srmacklem 	case VFS_CTL_TIMEO:
1245191783Srmacklem 		if (req->oldptr != NULL) {
1246191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1247191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1248191783Srmacklem 			if (error)
1249191783Srmacklem 				return (error);
1250191783Srmacklem 		}
1251191783Srmacklem 		if (req->newptr != NULL) {
1252191783Srmacklem			error = vfs_suser(mp, req->td);
1253191783Srmacklem			if (error)
1254191783Srmacklem				return (error);
1255191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1256191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1257191783Srmacklem 			if (error)
1258191783Srmacklem 				return (error);
1259191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1260191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1261191783Srmacklem 		}
1262191783Srmacklem		break;
1263191783Srmacklem	default:
1264191783Srmacklem		return (ENOTSUP);
1265191783Srmacklem	}
1266191783Srmacklem	return (0);
1267191783Srmacklem}
1268191783Srmacklem
1269