nfs_clvfsops.c revision 232292
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: stable/9/sys/fs/nfsclient/nfs_clvfsops.c 232292 2012-02-29 09:47:26Z bz $");
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>
48193066Sjamie#include <sys/jail.h>
49220739Srmacklem#include <sys/limits.h>
50191783Srmacklem#include <sys/lock.h>
51191783Srmacklem#include <sys/malloc.h>
52191783Srmacklem#include <sys/mbuf.h>
53191783Srmacklem#include <sys/module.h>
54191783Srmacklem#include <sys/mount.h>
55191783Srmacklem#include <sys/proc.h>
56191783Srmacklem#include <sys/socket.h>
57191783Srmacklem#include <sys/socketvar.h>
58191783Srmacklem#include <sys/sockio.h>
59191783Srmacklem#include <sys/sysctl.h>
60191783Srmacklem#include <sys/vnode.h>
61191783Srmacklem#include <sys/signalvar.h>
62191783Srmacklem
63191783Srmacklem#include <vm/vm.h>
64191783Srmacklem#include <vm/vm_extern.h>
65191783Srmacklem#include <vm/uma.h>
66191783Srmacklem
67191783Srmacklem#include <net/if.h>
68191783Srmacklem#include <net/route.h>
69191783Srmacklem#include <netinet/in.h>
70191783Srmacklem
71191783Srmacklem#include <fs/nfs/nfsport.h>
72191783Srmacklem#include <fs/nfsclient/nfsnode.h>
73191783Srmacklem#include <fs/nfsclient/nfsmount.h>
74191783Srmacklem#include <fs/nfsclient/nfs.h>
75221040Srmacklem#include <nfs/nfsdiskless.h>
76191783Srmacklem
77219028SnetchildFEATURE(nfscl, "NFSv4 client");
78219028Snetchild
79191783Srmacklemextern int nfscl_ticks;
80191783Srmacklemextern struct timeval nfsboottime;
81191783Srmacklemextern struct nfsstats	newnfsstats;
82222233Srmacklemextern int nfsrv_useacl;
83191783Srmacklem
84191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
85191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
86191783Srmacklem
87221973SrmacklemSYSCTL_DECL(_vfs_nfs);
88191783Srmacklemstatic int nfs_ip_paranoia = 1;
89221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
90191783Srmacklem    &nfs_ip_paranoia, 0, "");
91191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
92221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_INITIAL_DELAY,
93191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
94191783Srmacklem/* how long between console messages "nfs server foo not responding" */
95191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
96221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_DELAY,
97191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
98191783Srmacklem
99221040Srmacklemstatic int	nfs_mountroot(struct mount *);
100192585Srmacklemstatic void	nfs_sec_name(char *, int *);
101191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
102214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
103214048Srmacklem		    struct thread *);
104191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
105221014Srmacklem		    struct sockaddr *, char *, u_char *, int, u_char *, int,
106221014Srmacklem		    u_char *, int, struct vnode **, struct ucred *,
107221014Srmacklem		    struct thread *, int);
108214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
109216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
110216931Srmacklem		    struct timeval *);
111191783Srmacklemstatic vfs_mount_t nfs_mount;
112191783Srmacklemstatic vfs_cmount_t nfs_cmount;
113191783Srmacklemstatic vfs_unmount_t nfs_unmount;
114191783Srmacklemstatic vfs_root_t nfs_root;
115191783Srmacklemstatic vfs_statfs_t nfs_statfs;
116191783Srmacklemstatic vfs_sync_t nfs_sync;
117191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
118191783Srmacklem
119191783Srmacklem/*
120191783Srmacklem * nfs vfs operations.
121191783Srmacklem */
122191783Srmacklemstatic struct vfsops nfs_vfsops = {
123191783Srmacklem	.vfs_init =		ncl_init,
124191783Srmacklem	.vfs_mount =		nfs_mount,
125191783Srmacklem	.vfs_cmount =		nfs_cmount,
126191783Srmacklem	.vfs_root =		nfs_root,
127191783Srmacklem	.vfs_statfs =		nfs_statfs,
128191783Srmacklem	.vfs_sync =		nfs_sync,
129191783Srmacklem	.vfs_uninit =		ncl_uninit,
130191783Srmacklem	.vfs_unmount =		nfs_unmount,
131191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
132191783Srmacklem};
133221124SrmacklemVFS_SET(nfs_vfsops, nfs, VFCF_NETWORK);
134191783Srmacklem
135191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
136221139SrmacklemMODULE_VERSION(nfs, 1);
137221139SrmacklemMODULE_DEPEND(nfs, nfscommon, 1, 1, 1);
138221139SrmacklemMODULE_DEPEND(nfs, krpc, 1, 1, 1);
139221139SrmacklemMODULE_DEPEND(nfs, nfssvc, 1, 1, 1);
140221139SrmacklemMODULE_DEPEND(nfs, nfslock, 1, 1, 1);
141191783Srmacklem
142191783Srmacklem/*
143221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it
144221066Srmacklem * can be shared by both NFS clients. It is declared here so that it
145221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it
146221066Srmacklem * isn't used in that case.
147191783Srmacklem */
148221066Srmacklem#if !defined(NFS_ROOT) && !defined(NFSCLIENT)
149221066Srmacklemstruct nfs_diskless	nfs_diskless = { { { 0 } } };
150221066Srmacklemstruct nfsv3_diskless	nfsv3_diskless = { { { 0 } } };
151221066Srmacklemint			nfs_diskless_valid = 0;
152221066Srmacklem#endif
153221066Srmacklem
154221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
155221040Srmacklem    &nfs_diskless_valid, 0,
156192145Srmacklem    "Has the diskless struct been filled correctly");
157191783Srmacklem
158221973SrmacklemSYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
159221040Srmacklem    nfsv3_diskless.root_hostnam, 0, "Path to nfs root");
160191783Srmacklem
161221973SrmacklemSYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
162221040Srmacklem    &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr),
163192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
164191783Srmacklem
165191783Srmacklem
166191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
167191783Srmacklemstatic int	nfs_mountdiskless(char *,
168191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
169191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
170191783Srmacklemstatic void	nfs_convert_diskless(void);
171191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
172191783Srmacklem		    struct onfs_args *oargs);
173191783Srmacklem
174191783Srmacklemint
175191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
176191783Srmacklem{
177191783Srmacklem	int iosize, maxio;
178191783Srmacklem
179191783Srmacklem	/* First, set the upper limit for iosize */
180191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
181191783Srmacklem		maxio = NFS_MAXBSIZE;
182191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
183191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
184191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
185191783Srmacklem		else
186191783Srmacklem			maxio = NFS_MAXBSIZE;
187191783Srmacklem	} else {
188191783Srmacklem		maxio = NFS_V2MAXDATA;
189191783Srmacklem	}
190191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
191191783Srmacklem		nmp->nm_rsize = maxio;
192191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
193191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
194191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
195191783Srmacklem		nmp->nm_readdirsize = maxio;
196191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
197191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
198191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
199191783Srmacklem		nmp->nm_wsize = maxio;
200191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
201191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
202191783Srmacklem
203191783Srmacklem	/*
204191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
205191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
206191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
207191783Srmacklem	 * space.
208191783Srmacklem	 */
209191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
210191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
211191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
212191783Srmacklem	return (iosize);
213191783Srmacklem}
214191783Srmacklem
215191783Srmacklemstatic void
216191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
217191783Srmacklem{
218191783Srmacklem
219191783Srmacklem	args->version = NFS_ARGSVERSION;
220191783Srmacklem	args->addr = oargs->addr;
221191783Srmacklem	args->addrlen = oargs->addrlen;
222191783Srmacklem	args->sotype = oargs->sotype;
223191783Srmacklem	args->proto = oargs->proto;
224191783Srmacklem	args->fh = oargs->fh;
225191783Srmacklem	args->fhsize = oargs->fhsize;
226191783Srmacklem	args->flags = oargs->flags;
227191783Srmacklem	args->wsize = oargs->wsize;
228191783Srmacklem	args->rsize = oargs->rsize;
229191783Srmacklem	args->readdirsize = oargs->readdirsize;
230191783Srmacklem	args->timeo = oargs->timeo;
231191783Srmacklem	args->retrans = oargs->retrans;
232191783Srmacklem	args->readahead = oargs->readahead;
233191783Srmacklem	args->hostname = oargs->hostname;
234191783Srmacklem}
235191783Srmacklem
236191783Srmacklemstatic void
237191783Srmacklemnfs_convert_diskless(void)
238191783Srmacklem{
239191783Srmacklem
240221040Srmacklem	bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif,
241221040Srmacklem		sizeof(struct ifaliasreq));
242221040Srmacklem	bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway,
243221040Srmacklem		sizeof(struct sockaddr_in));
244221040Srmacklem	nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args);
245221040Srmacklem	if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
246221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_MYFH;
247221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH);
248191783Srmacklem	} else {
249221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_V2FH;
250221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH);
251191783Srmacklem	}
252221040Srmacklem	bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr,
253221040Srmacklem		sizeof(struct sockaddr_in));
254221040Srmacklem	bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN);
255221040Srmacklem	nfsv3_diskless.root_time = nfs_diskless.root_time;
256221040Srmacklem	bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam,
257221040Srmacklem		MAXHOSTNAMELEN);
258221040Srmacklem	nfs_diskless_valid = 3;
259191783Srmacklem}
260191783Srmacklem
261191783Srmacklem/*
262191783Srmacklem * nfs statfs call
263191783Srmacklem */
264191783Srmacklemstatic int
265191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
266191783Srmacklem{
267191783Srmacklem	struct vnode *vp;
268191990Sattilio	struct thread *td;
269191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
270191783Srmacklem	struct nfsvattr nfsva;
271191783Srmacklem	struct nfsfsinfo fs;
272191783Srmacklem	struct nfsstatfs sb;
273191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
274191783Srmacklem	struct nfsnode *np;
275191783Srmacklem
276191990Sattilio	td = curthread;
277191990Sattilio
278191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
279191783Srmacklem	if (error)
280191783Srmacklem		return (error);
281220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
282191783Srmacklem	if (error) {
283191783Srmacklem		vfs_unbusy(mp);
284191783Srmacklem		return (error);
285191783Srmacklem	}
286191783Srmacklem	vp = NFSTOV(np);
287191783Srmacklem	mtx_lock(&nmp->nm_mtx);
288191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
289191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
290191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
291191783Srmacklem		    &attrflag, NULL);
292191783Srmacklem		if (!error)
293191783Srmacklem			gotfsinfo = 1;
294191783Srmacklem	} else
295191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
296191783Srmacklem	if (!error)
297191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
298191783Srmacklem		    &attrflag, NULL);
299191783Srmacklem	if (attrflag == 0) {
300191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
301191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
302191783Srmacklem		if (ret) {
303191783Srmacklem			/*
304191783Srmacklem			 * Just set default values to get things going.
305191783Srmacklem			 */
306191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
307191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
308191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
309191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
310191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
311191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
312191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
313191783Srmacklem			nfsva.na_vattr.va_gen = 1;
314191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
315191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
316191783Srmacklem		}
317191783Srmacklem	}
318191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
319191783Srmacklem	if (!error) {
320191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
321191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
322191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
323191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
324191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
325191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
326191783Srmacklem	    if (sbp != &mp->mnt_stat) {
327191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
328191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
329191783Srmacklem	    }
330191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
331191783Srmacklem	} else if (NFS_ISV4(vp)) {
332191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
333191783Srmacklem	}
334191783Srmacklem	vput(vp);
335191783Srmacklem	vfs_unbusy(mp);
336191783Srmacklem	return (error);
337191783Srmacklem}
338191783Srmacklem
339191783Srmacklem/*
340191783Srmacklem * nfs version 3 fsinfo rpc call
341191783Srmacklem */
342191783Srmacklemint
343191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
344191783Srmacklem    struct thread *td)
345191783Srmacklem{
346191783Srmacklem	struct nfsfsinfo fs;
347191783Srmacklem	struct nfsvattr nfsva;
348191783Srmacklem	int error, attrflag;
349191783Srmacklem
350191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
351191783Srmacklem	if (!error) {
352191783Srmacklem		if (attrflag)
353191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
354191783Srmacklem			    1);
355191783Srmacklem		mtx_lock(&nmp->nm_mtx);
356191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
357191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
358191783Srmacklem	}
359191783Srmacklem	return (error);
360191783Srmacklem}
361191783Srmacklem
362191783Srmacklem/*
363191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
364221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary
365191783Srmacklem * bootstrap.
366191783Srmacklem * It goes something like this:
367191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
368191783Srmacklem *   can talk to the server
369221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as
370191783Srmacklem *   a default gateway.
371191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
372191783Srmacklem *
373191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
374191783Srmacklem * structure, as well as other global NFS client variables here, as
375192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
376191783Srmacklem * client activity occurs.
377191783Srmacklem */
378221040Srmacklemstatic int
379221040Srmacklemnfs_mountroot(struct mount *mp)
380191783Srmacklem{
381192145Srmacklem	struct thread *td = curthread;
382221040Srmacklem	struct nfsv3_diskless *nd = &nfsv3_diskless;
383191783Srmacklem	struct socket *so;
384191783Srmacklem	struct vnode *vp;
385191783Srmacklem	struct ifreq ir;
386193066Sjamie	int error;
387191783Srmacklem	u_long l;
388191783Srmacklem	char buf[128];
389191783Srmacklem	char *cp;
390191783Srmacklem
391191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
392192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
393191783Srmacklem#elif defined(NFS_ROOT)
394191783Srmacklem	nfs_setup_diskless();
395191783Srmacklem#endif
396191783Srmacklem
397221040Srmacklem	if (nfs_diskless_valid == 0)
398191783Srmacklem		return (-1);
399221040Srmacklem	if (nfs_diskless_valid == 1)
400191783Srmacklem		nfs_convert_diskless();
401191783Srmacklem
402191783Srmacklem	/*
403191783Srmacklem	 * XXX splnet, so networks will receive...
404191783Srmacklem	 */
405191783Srmacklem	splnet();
406191783Srmacklem
407191783Srmacklem	/*
408191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
409191783Srmacklem	 * talk to the server.
410191783Srmacklem	 */
411191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
412191783Srmacklem	    td->td_ucred, td);
413191783Srmacklem	if (error)
414192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
415191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
416191783Srmacklem
417191783Srmacklem#if 0 /* XXX Bad idea */
418191783Srmacklem	/*
419191783Srmacklem	 * We might not have been told the right interface, so we pass
420191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
421191783Srmacklem	 * one of them configured.
422191783Srmacklem	 */
423191783Srmacklem
424191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
425191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
426191783Srmacklem		nd->myif.ifra_name[i] <= '9';
427191783Srmacklem		nd->myif.ifra_name[i] ++) {
428191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
429191783Srmacklem		if(!error)
430191783Srmacklem			break;
431191783Srmacklem	}
432191783Srmacklem#endif
433191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
434191783Srmacklem	if (error)
435192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
436191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
437191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
438191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
439191783Srmacklem		freeenv(cp);
440191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
441191783Srmacklem		if (error)
442192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
443191783Srmacklem	}
444191783Srmacklem	soclose(so);
445191783Srmacklem
446191783Srmacklem	/*
447191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
448191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
449191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
450191783Srmacklem	 * to avoid panicking inappropriately in that situation.
451191783Srmacklem	 */
452191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
453191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
454191783Srmacklem		struct sockaddr_in mask, sin;
455191783Srmacklem
456191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
457191783Srmacklem		sin = mask;
458191783Srmacklem		sin.sin_family = AF_INET;
459191783Srmacklem		sin.sin_len = sizeof(sin);
460192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
461218757Sbz		CURVNET_SET(TD_TO_VNET(td));
462232292Sbz		error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&sin,
463191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
464191783Srmacklem		    (struct sockaddr *)&mask,
465232292Sbz		    RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB);
466218757Sbz		CURVNET_RESTORE();
467191783Srmacklem		if (error)
468192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
469191783Srmacklem	}
470191783Srmacklem
471191783Srmacklem	/*
472191783Srmacklem	 * Create the rootfs mount point.
473191783Srmacklem	 */
474191783Srmacklem	nd->root_args.fh = nd->root_fh;
475191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
476191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
477191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
478191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
479191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
480191783Srmacklem	printf("NFS ROOT: %s\n", buf);
481192145Srmacklem	nd->root_args.hostname = buf;
482191783Srmacklem	if ((error = nfs_mountdiskless(buf,
483191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
484191783Srmacklem		return (error);
485191783Srmacklem	}
486191783Srmacklem
487191783Srmacklem	/*
488191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
489191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
490191783Srmacklem	 * mount the right /var based upon its preset value.
491191783Srmacklem	 */
492193066Sjamie	mtx_lock(&prison0.pr_mtx);
493194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
494194118Sjamie	    sizeof(prison0.pr_hostname));
495193066Sjamie	mtx_unlock(&prison0.pr_mtx);
496191783Srmacklem	inittodr(ntohl(nd->root_time));
497191783Srmacklem	return (0);
498191783Srmacklem}
499191783Srmacklem
500191783Srmacklem/*
501191783Srmacklem * Internal version of mount system call for diskless setup.
502191783Srmacklem */
503191783Srmacklemstatic int
504191783Srmacklemnfs_mountdiskless(char *path,
505191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
506191783Srmacklem    struct vnode **vpp, struct mount *mp)
507191783Srmacklem{
508191783Srmacklem	struct sockaddr *nam;
509221014Srmacklem	int dirlen, error;
510221014Srmacklem	char *dirpath;
511191783Srmacklem
512221014Srmacklem	/*
513221014Srmacklem	 * Find the directory path in "path", which also has the server's
514221014Srmacklem	 * name/ip address in it.
515221014Srmacklem	 */
516221014Srmacklem	dirpath = strchr(path, ':');
517221014Srmacklem	if (dirpath != NULL)
518221014Srmacklem		dirlen = strlen(++dirpath);
519221014Srmacklem	else
520221014Srmacklem		dirlen = 0;
521191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
522221014Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
523221014Srmacklem	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
524192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
525191783Srmacklem		return (error);
526191783Srmacklem	}
527191783Srmacklem	return (0);
528191783Srmacklem}
529191783Srmacklem
530191783Srmacklemstatic void
531192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
532192585Srmacklem{
533192585Srmacklem	if (!strcmp(sec, "krb5"))
534192585Srmacklem		*flagsp |= NFSMNT_KERB;
535192585Srmacklem	else if (!strcmp(sec, "krb5i"))
536192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
537192585Srmacklem	else if (!strcmp(sec, "krb5p"))
538192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
539192585Srmacklem}
540192585Srmacklem
541192585Srmacklemstatic void
542191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
543214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
544191783Srmacklem{
545191783Srmacklem	int s;
546191783Srmacklem	int adjsock;
547214048Srmacklem	char *p;
548191783Srmacklem
549191783Srmacklem	s = splnet();
550191783Srmacklem
551191783Srmacklem	/*
552191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
553191783Srmacklem	 * an update.  If this is not an update, then either the read-only
554191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
555191783Srmacklem	 * intentionally at some previous point.
556191783Srmacklem	 */
557191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
558191783Srmacklem		MNT_ILOCK(mp);
559191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
560191783Srmacklem		MNT_IUNLOCK(mp);
561191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
562191783Srmacklem		MNT_ILOCK(mp);
563191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
564191783Srmacklem		MNT_IUNLOCK(mp);
565191783Srmacklem	}
566191783Srmacklem
567191783Srmacklem	/*
568191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
569191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
570191783Srmacklem	 * and soft timeout behavior.
571191783Srmacklem	 */
572191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
573191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
574191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
575220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
576220739Srmacklem			nmp->nm_retry = INT_MAX;
577220739Srmacklem		else
578220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
579191783Srmacklem	}
580191783Srmacklem
581220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
582220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
583220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
584191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
585220739Srmacklem	}
586191783Srmacklem
587220739Srmacklem	/* Clear NFSMNT_RESVPORT for NFSv4, since it is not required. */
588220739Srmacklem	if ((argp->flags & NFSMNT_NFSV4) != 0) {
589220739Srmacklem		argp->flags &= ~NFSMNT_RESVPORT;
590220739Srmacklem		nmp->nm_flag &= ~NFSMNT_RESVPORT;
591220739Srmacklem	}
592220739Srmacklem
593220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
594220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
595220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
596191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
597220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
598191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
599191783Srmacklem
600191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
601191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
602191783Srmacklem	splx(s);
603191783Srmacklem
604191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
605191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
606191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
607191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
608191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
609191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
610191783Srmacklem	}
611191783Srmacklem
612191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
613191783Srmacklem		nmp->nm_retry = argp->retrans;
614191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
615191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
616191783Srmacklem	}
617191783Srmacklem
618191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
619191783Srmacklem		nmp->nm_wsize = argp->wsize;
620191783Srmacklem		/* Round down to multiple of blocksize */
621191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
622191783Srmacklem		if (nmp->nm_wsize <= 0)
623191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
624191783Srmacklem	}
625191783Srmacklem
626191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
627191783Srmacklem		nmp->nm_rsize = argp->rsize;
628191783Srmacklem		/* Round down to multiple of blocksize */
629191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
630191783Srmacklem		if (nmp->nm_rsize <= 0)
631191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
632191783Srmacklem	}
633191783Srmacklem
634191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
635191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
636191783Srmacklem	}
637191783Srmacklem
638191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
639191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
640191783Srmacklem	else
641191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
642191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
643191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
644191783Srmacklem	else
645191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
646191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
647191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
648191783Srmacklem	else
649191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
650191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
651191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
652191783Srmacklem	else
653191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
654191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
655191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
656191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
657191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
658191783Srmacklem
659191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
660191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
661191783Srmacklem			nmp->nm_readahead = argp->readahead;
662191783Srmacklem		else
663191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
664191783Srmacklem	}
665191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
666191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
667191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
668191783Srmacklem		else
669191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
670191783Srmacklem	}
671191783Srmacklem
672191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
673191783Srmacklem		    (nmp->nm_soproto != argp->proto));
674191783Srmacklem
675191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
676191783Srmacklem		int haslock = 0, error = 0;
677191783Srmacklem
678191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
679191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
680191783Srmacklem			if (!error)
681191783Srmacklem				haslock = 1;
682191783Srmacklem		}
683191783Srmacklem		if (!error) {
684191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
685191783Srmacklem		    if (haslock)
686191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
687191783Srmacklem		    nmp->nm_sotype = argp->sotype;
688191783Srmacklem		    nmp->nm_soproto = argp->proto;
689191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
690191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
691191783Srmacklem			    cred, td, 0)) {
692191783Srmacklem				printf("newnfs_args: retrying connect\n");
693207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
694191783Srmacklem			}
695191783Srmacklem		}
696191783Srmacklem	} else {
697191783Srmacklem		nmp->nm_sotype = argp->sotype;
698191783Srmacklem		nmp->nm_soproto = argp->proto;
699191783Srmacklem	}
700214048Srmacklem
701214048Srmacklem	if (hostname != NULL) {
702214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
703214048Srmacklem		    sizeof(nmp->nm_hostname));
704214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
705214048Srmacklem		if (p != NULL)
706214048Srmacklem			*p = '\0';
707214048Srmacklem	}
708191783Srmacklem}
709191783Srmacklem
710221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
711191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
712191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
713192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
714192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
715192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
716192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
717192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
718229604Sjhb    "negnametimeo", "nocto", "wcommitsize",
719191783Srmacklem    NULL };
720191783Srmacklem
721191783Srmacklem/*
722191783Srmacklem * VFS Operations.
723191783Srmacklem *
724191783Srmacklem * mount system call
725191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
726191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
727191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
728191783Srmacklem * an error after that means that I have to release the mbuf.
729191783Srmacklem */
730191783Srmacklem/* ARGSUSED */
731191783Srmacklemstatic int
732191990Sattilionfs_mount(struct mount *mp)
733191783Srmacklem{
734191783Srmacklem	struct nfs_args args = {
735191783Srmacklem	    .version = NFS_ARGSVERSION,
736191783Srmacklem	    .addr = NULL,
737191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
738191783Srmacklem	    .sotype = SOCK_STREAM,
739191783Srmacklem	    .proto = 0,
740191783Srmacklem	    .fh = NULL,
741191783Srmacklem	    .fhsize = 0,
742220739Srmacklem	    .flags = NFSMNT_RESVPORT,
743191783Srmacklem	    .wsize = NFS_WSIZE,
744191783Srmacklem	    .rsize = NFS_RSIZE,
745191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
746191783Srmacklem	    .timeo = 10,
747191783Srmacklem	    .retrans = NFS_RETRANS,
748191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
749191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
750191783Srmacklem	    .hostname = NULL,
751191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
752191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
753191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
754191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
755191783Srmacklem	};
756192585Srmacklem	int error = 0, ret, len;
757192585Srmacklem	struct sockaddr *nam = NULL;
758191783Srmacklem	struct vnode *vp;
759191990Sattilio	struct thread *td;
760191783Srmacklem	char hst[MNAMELEN];
761191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
762192585Srmacklem	char *opt, *name, *secname;
763203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
764221190Srmacklem	int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
765221205Srmacklem	size_t hstlen;
766191783Srmacklem
767221190Srmacklem	has_nfs_args_opt = 0;
768191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
769191783Srmacklem		error = EINVAL;
770191783Srmacklem		goto out;
771191783Srmacklem	}
772191783Srmacklem
773191990Sattilio	td = curthread;
774191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
775221040Srmacklem		error = nfs_mountroot(mp);
776191783Srmacklem		goto out;
777191783Srmacklem	}
778191783Srmacklem
779192585Srmacklem	nfscl_init();
780191783Srmacklem
781221190Srmacklem	/*
782221190Srmacklem	 * The old mount_nfs program passed the struct nfs_args
783221190Srmacklem	 * from userspace to kernel.  The new mount_nfs program
784221190Srmacklem	 * passes string options via nmount() from userspace to kernel
785221190Srmacklem	 * and we populate the struct nfs_args in the kernel.
786221190Srmacklem	 */
787221190Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) {
788221190Srmacklem		error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args,
789221190Srmacklem		    sizeof(args));
790221190Srmacklem		if (error != 0)
791221190Srmacklem			goto out;
792221190Srmacklem
793221190Srmacklem		if (args.version != NFS_ARGSVERSION) {
794221190Srmacklem			error = EPROGMISMATCH;
795221190Srmacklem			goto out;
796221190Srmacklem		}
797221190Srmacklem		has_nfs_args_opt = 1;
798221190Srmacklem	}
799221190Srmacklem
800192585Srmacklem	/* Handle the new style options. */
801192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
802192585Srmacklem		args.flags |= NFSMNT_NOCONN;
803192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
804192585Srmacklem		args.flags |= NFSMNT_NOCONN;
805192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
806192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
807192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
808192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
809192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
810192585Srmacklem		args.flags |= NFSMNT_INT;
811192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
812192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
813192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
814192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
815192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
816192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
817192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
818192585Srmacklem		args.flags |= NFSMNT_SOFT;
819192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
820192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
821192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
822192585Srmacklem		args.sotype = SOCK_DGRAM;
823192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
824192585Srmacklem		args.sotype = SOCK_DGRAM;
825192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
826192585Srmacklem		args.sotype = SOCK_STREAM;
827192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
828192585Srmacklem		args.flags |= NFSMNT_NFSV3;
829192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
830192585Srmacklem		args.flags |= NFSMNT_NFSV4;
831192585Srmacklem		args.sotype = SOCK_STREAM;
832191783Srmacklem	}
833192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
834192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
835221436Sru	if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
836221436Sru		args.flags |= NFSMNT_NOCTO;
837192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
838192585Srmacklem		if (opt == NULL) {
839192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
840192585Srmacklem			error = EINVAL;
841192585Srmacklem			goto out;
842192585Srmacklem		}
843192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
844192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
845192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
846192585Srmacklem			    opt);
847192585Srmacklem			error = EINVAL;
848192585Srmacklem			goto out;
849192585Srmacklem		}
850192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
851192585Srmacklem	}
852192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
853192585Srmacklem		if (opt == NULL) {
854192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
855192585Srmacklem			error = EINVAL;
856192585Srmacklem			goto out;
857192585Srmacklem		}
858192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
859192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
860192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
861192585Srmacklem			    opt);
862192585Srmacklem			error = EINVAL;
863192585Srmacklem			goto out;
864192585Srmacklem		}
865192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
866192585Srmacklem	}
867192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
868192585Srmacklem		if (opt == NULL) {
869192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
870192585Srmacklem			error = EINVAL;
871192585Srmacklem			goto out;
872192585Srmacklem		}
873192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
874192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
875192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
876192585Srmacklem			    opt);
877192585Srmacklem			error = EINVAL;
878192585Srmacklem			goto out;
879192585Srmacklem		}
880192585Srmacklem		args.flags |= NFSMNT_WSIZE;
881192585Srmacklem	}
882192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
883192585Srmacklem		if (opt == NULL) {
884192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
885192585Srmacklem			error = EINVAL;
886192585Srmacklem			goto out;
887192585Srmacklem		}
888192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
889192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
890192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
891192585Srmacklem			    opt);
892192585Srmacklem			error = EINVAL;
893192585Srmacklem			goto out;
894192585Srmacklem		}
895192585Srmacklem		args.flags |= NFSMNT_RSIZE;
896192585Srmacklem	}
897192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
898192585Srmacklem		if (opt == NULL) {
899192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
900192585Srmacklem			error = EINVAL;
901192585Srmacklem			goto out;
902192585Srmacklem		}
903192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
904192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
905192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
906192585Srmacklem			    opt);
907192585Srmacklem			error = EINVAL;
908192585Srmacklem			goto out;
909192585Srmacklem		}
910192585Srmacklem		args.flags |= NFSMNT_RETRANS;
911192585Srmacklem	}
912192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
913192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
914192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
915192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
916192585Srmacklem			    opt);
917192585Srmacklem			error = EINVAL;
918192585Srmacklem			goto out;
919192585Srmacklem		}
920192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
921192585Srmacklem	}
922192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
923192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
924192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
925192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
926192585Srmacklem			    opt);
927192585Srmacklem			error = EINVAL;
928192585Srmacklem			goto out;
929192585Srmacklem		}
930192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
931192585Srmacklem	}
932192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
933192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
934192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
935192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
936192585Srmacklem			    opt);
937192585Srmacklem			error = EINVAL;
938192585Srmacklem			goto out;
939192585Srmacklem		}
940192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
941192585Srmacklem	}
942192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
943192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
944192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
945192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
946192585Srmacklem			    opt);
947192585Srmacklem			error = EINVAL;
948192585Srmacklem			goto out;
949192585Srmacklem		}
950192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
951192585Srmacklem	}
952229604Sjhb	if (vfs_getopt(mp->mnt_optnew, "wcommitsize", (void **)&opt, NULL) == 0) {
953229604Sjhb		ret = sscanf(opt, "%d", &args.wcommitsize);
954229604Sjhb		if (ret != 1 || args.wcommitsize < 0) {
955229604Sjhb			vfs_mount_error(mp, "illegal wcommitsize: %s", opt);
956229604Sjhb			error = EINVAL;
957229604Sjhb			goto out;
958229604Sjhb		}
959229604Sjhb		args.flags |= NFSMNT_WCOMMITSIZE;
960229604Sjhb	}
961192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
962192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
963192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
964192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
965192585Srmacklem			    opt);
966192585Srmacklem			error = EINVAL;
967192585Srmacklem			goto out;
968192585Srmacklem		}
969192585Srmacklem		args.flags |= NFSMNT_TIMEO;
970192585Srmacklem	}
971203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
972203303Srmacklem	    == 0) {
973203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
974203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
975203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
976203303Srmacklem			    opt);
977203303Srmacklem			error = EINVAL;
978203303Srmacklem			goto out;
979203303Srmacklem		}
980203303Srmacklem	}
981192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
982192585Srmacklem		(void **) &secname, NULL) == 0)
983192585Srmacklem		nfs_sec_name(secname, &args.flags);
984191783Srmacklem
985191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
986191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
987191783Srmacklem
988191783Srmacklem		if (nmp == NULL) {
989191783Srmacklem			error = EIO;
990191783Srmacklem			goto out;
991191783Srmacklem		}
992231636Srmacklem
993191783Srmacklem		/*
994231636Srmacklem		 * If a change from TCP->UDP is done and there are thread(s)
995231636Srmacklem		 * that have I/O RPC(s) in progress with a tranfer size
996231636Srmacklem		 * greater than NFS_MAXDGRAMDATA, those thread(s) will be
997231636Srmacklem		 * hung, retrying the RPC(s) forever. Usually these threads
998231636Srmacklem		 * will be seen doing an uninterruptible sleep on wait channel
999231636Srmacklem		 * "newnfsreq" (truncated to "newnfsre" by procstat).
1000231636Srmacklem		 */
1001231636Srmacklem		if (args.sotype == SOCK_DGRAM && nmp->nm_sotype == SOCK_STREAM)
1002231636Srmacklem			tprintf(td->td_proc, LOG_WARNING,
1003231636Srmacklem	"Warning: mount -u that changes TCP->UDP can result in hung threads\n");
1004231636Srmacklem
1005231636Srmacklem		/*
1006191783Srmacklem		 * When doing an update, we can't change version,
1007191783Srmacklem		 * security, switch lockd strategies or change cookie
1008191783Srmacklem		 * translation
1009191783Srmacklem		 */
1010191783Srmacklem		args.flags = (args.flags &
1011191783Srmacklem		    ~(NFSMNT_NFSV3 |
1012191783Srmacklem		      NFSMNT_NFSV4 |
1013191783Srmacklem		      NFSMNT_KERB |
1014191783Srmacklem		      NFSMNT_INTEGRITY |
1015191783Srmacklem		      NFSMNT_PRIVACY |
1016191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
1017191783Srmacklem		    (nmp->nm_flag &
1018191783Srmacklem			(NFSMNT_NFSV3 |
1019191783Srmacklem			 NFSMNT_NFSV4 |
1020191783Srmacklem			 NFSMNT_KERB |
1021191783Srmacklem			 NFSMNT_INTEGRITY |
1022191783Srmacklem			 NFSMNT_PRIVACY |
1023191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
1024214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
1025191783Srmacklem		goto out;
1026191783Srmacklem	}
1027191783Srmacklem
1028191783Srmacklem	/*
1029191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
1030191783Srmacklem	 * or no-connection mode for those protocols that support
1031191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
1032191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
1033191783Srmacklem	 * to receive replies from a different IP then the request was
1034191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
1035191783Srmacklem	 * not 0.
1036191783Srmacklem	 */
1037191783Srmacklem	if (nfs_ip_paranoia == 0)
1038191783Srmacklem		args.flags |= NFSMNT_NOCONN;
1039192585Srmacklem
1040221190Srmacklem	if (has_nfs_args_opt != 0) {
1041221190Srmacklem		/*
1042221190Srmacklem		 * In the 'nfs_args' case, the pointers in the args
1043221190Srmacklem		 * structure are in userland - we copy them in here.
1044221190Srmacklem		 */
1045221190Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) {
1046192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
1047191783Srmacklem			error = EINVAL;
1048191783Srmacklem			goto out;
1049191783Srmacklem		}
1050221190Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1051221190Srmacklem		    args.fhsize);
1052221190Srmacklem		if (error != 0)
1053221190Srmacklem			goto out;
1054221205Srmacklem		error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen);
1055221190Srmacklem		if (error != 0)
1056221190Srmacklem			goto out;
1057221205Srmacklem		bzero(&hst[hstlen], MNAMELEN - hstlen);
1058221190Srmacklem		args.hostname = hst;
1059221190Srmacklem		/* sockargs() call must be after above copyin() calls */
1060221190Srmacklem		error = getsockaddr(&nam, (caddr_t)args.addr,
1061221190Srmacklem		    args.addrlen);
1062221190Srmacklem		if (error != 0)
1063221190Srmacklem			goto out;
1064191783Srmacklem	} else {
1065221190Srmacklem		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
1066221190Srmacklem		    &args.fhsize) == 0) {
1067221190Srmacklem			if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
1068221190Srmacklem				vfs_mount_error(mp, "Bad file handle");
1069221190Srmacklem				error = EINVAL;
1070221190Srmacklem				goto out;
1071221190Srmacklem			}
1072221190Srmacklem			bcopy(args.fh, nfh, args.fhsize);
1073221190Srmacklem		} else {
1074221190Srmacklem			args.fhsize = 0;
1075221190Srmacklem		}
1076221190Srmacklem		(void) vfs_getopt(mp->mnt_optnew, "hostname",
1077221190Srmacklem		    (void **)&args.hostname, &len);
1078221190Srmacklem		if (args.hostname == NULL) {
1079221190Srmacklem			vfs_mount_error(mp, "Invalid hostname");
1080221190Srmacklem			error = EINVAL;
1081221190Srmacklem			goto out;
1082221190Srmacklem		}
1083221190Srmacklem		bcopy(args.hostname, hst, MNAMELEN);
1084221190Srmacklem		hst[MNAMELEN - 1] = '\0';
1085192585Srmacklem	}
1086192585Srmacklem
1087192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1088192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1089192585Srmacklem	else
1090192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1091221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1092192585Srmacklem
1093192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1094192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1095192585Srmacklem	else
1096191783Srmacklem		krbname[0] = '\0';
1097221014Srmacklem	krbnamelen = strlen(krbname);
1098192585Srmacklem
1099192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1100192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1101192585Srmacklem	else
1102191783Srmacklem		dirpath[0] = '\0';
1103221014Srmacklem	dirlen = strlen(dirpath);
1104192585Srmacklem
1105222075Srmacklem	if (has_nfs_args_opt == 0) {
1106222075Srmacklem		if (vfs_getopt(mp->mnt_optnew, "addr",
1107222075Srmacklem		    (void **)&args.addr, &args.addrlen) == 0) {
1108222075Srmacklem			if (args.addrlen > SOCK_MAXADDRLEN) {
1109222075Srmacklem				error = ENAMETOOLONG;
1110222075Srmacklem				goto out;
1111222075Srmacklem			}
1112222075Srmacklem			nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1113222075Srmacklem			bcopy(args.addr, nam, args.addrlen);
1114222075Srmacklem			nam->sa_len = args.addrlen;
1115222075Srmacklem		} else {
1116222075Srmacklem			vfs_mount_error(mp, "No server address");
1117222075Srmacklem			error = EINVAL;
1118191783Srmacklem			goto out;
1119191783Srmacklem		}
1120191783Srmacklem	}
1121192585Srmacklem
1122191783Srmacklem	args.fh = nfh;
1123221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1124221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1125221014Srmacklem	    negnametimeo);
1126191783Srmacklemout:
1127191783Srmacklem	if (!error) {
1128191783Srmacklem		MNT_ILOCK(mp);
1129191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
1130191783Srmacklem		MNT_IUNLOCK(mp);
1131191783Srmacklem	}
1132191783Srmacklem	return (error);
1133191783Srmacklem}
1134191783Srmacklem
1135191783Srmacklem
1136191783Srmacklem/*
1137191783Srmacklem * VFS Operations.
1138191783Srmacklem *
1139191783Srmacklem * mount system call
1140191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1141191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1142191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1143191783Srmacklem * an error after that means that I have to release the mbuf.
1144191783Srmacklem */
1145191783Srmacklem/* ARGSUSED */
1146191783Srmacklemstatic int
1147230725Smckusicknfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
1148191783Srmacklem{
1149191783Srmacklem	int error;
1150191783Srmacklem	struct nfs_args args;
1151191783Srmacklem
1152191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1153191783Srmacklem	if (error)
1154191783Srmacklem		return error;
1155191783Srmacklem
1156191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1157191783Srmacklem
1158191783Srmacklem	error = kernel_mount(ma, flags);
1159191783Srmacklem	return (error);
1160191783Srmacklem}
1161191783Srmacklem
1162191783Srmacklem/*
1163191783Srmacklem * Common code for mount and mountroot
1164191783Srmacklem */
1165191783Srmacklemstatic int
1166191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1167221014Srmacklem    char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
1168221014Srmacklem    u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
1169221014Srmacklem    struct ucred *cred, struct thread *td, int negnametimeo)
1170191783Srmacklem{
1171191783Srmacklem	struct nfsmount *nmp;
1172191783Srmacklem	struct nfsnode *np;
1173195762Srmacklem	int error, trycnt, ret;
1174191783Srmacklem	struct nfsvattr nfsva;
1175191783Srmacklem	static u_int64_t clval = 0;
1176191783Srmacklem
1177191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1178191783Srmacklem		nmp = VFSTONFS(mp);
1179191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1180191783Srmacklem		FREE(nam, M_SONAME);
1181191783Srmacklem		return (0);
1182191783Srmacklem	} else {
1183191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1184221014Srmacklem		    krbnamelen + dirlen + srvkrbnamelen + 2,
1185221014Srmacklem		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
1186191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1187191783Srmacklem		if (clval == 0)
1188191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1189191783Srmacklem		nmp->nm_clval = clval++;
1190221014Srmacklem		nmp->nm_krbnamelen = krbnamelen;
1191221014Srmacklem		nmp->nm_dirpathlen = dirlen;
1192221014Srmacklem		nmp->nm_srvkrbnamelen = srvkrbnamelen;
1193192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1194191783Srmacklem			/*
1195192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1196192675Srmacklem			 * the nfsv4 state handling operations if there is
1197192675Srmacklem			 * no host based principal set. Use the uid of
1198192675Srmacklem			 * this user if not root, since they are doing the
1199192675Srmacklem			 * mount. I don't think setting this for root will
1200192675Srmacklem			 * work, since root normally does not have user
1201192675Srmacklem			 * credentials in a credentials cache.
1202191783Srmacklem			 */
1203192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1204191783Srmacklem		} else {
1205191783Srmacklem			/*
1206192675Srmacklem			 * Just set to -1, so it won't be used.
1207191783Srmacklem			 */
1208191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1209191783Srmacklem		}
1210191783Srmacklem
1211191783Srmacklem		/* Copy and null terminate all the names */
1212191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1213191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1214191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1215191783Srmacklem		}
1216191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1217191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1218191783Srmacklem			    nmp->nm_dirpathlen);
1219191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1220191783Srmacklem			    + 1] = '\0';
1221191783Srmacklem		}
1222191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1223191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1224191783Srmacklem			    nmp->nm_srvkrbnamelen);
1225191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1226191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1227191783Srmacklem		}
1228191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1229191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1230191783Srmacklem		mp->mnt_data = nmp;
1231214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1232216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1233191783Srmacklem	}
1234191783Srmacklem	vfs_getnewfsid(mp);
1235191783Srmacklem	nmp->nm_mountp = mp;
1236191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1237229172Srmacklem
1238229172Srmacklem	/*
1239229172Srmacklem	 * Since nfs_decode_args() might optionally set them, these need to
1240229172Srmacklem	 * set to defaults before the call, so that the optional settings
1241229172Srmacklem	 * aren't overwritten.
1242229172Srmacklem	 */
1243203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1244229172Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1245229172Srmacklem	nmp->nm_retry = NFS_RETRANS;
1246229172Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1247229263Srmacklem	if (desiredvnodes >= 11000)
1248229263Srmacklem		nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1249229263Srmacklem	else
1250229263Srmacklem		nmp->nm_wcommitsize = hibufspace / 10;
1251191783Srmacklem
1252214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1253192585Srmacklem
1254191783Srmacklem	/*
1255191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1256191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1257191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1258191783Srmacklem	 *
1259191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1260191783Srmacklem	 * that we can handle until we find out otherwise.
1261191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1262191783Srmacklem	 * buffer cache using signed(!) block numbers.
1263191783Srmacklem	 */
1264191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1265191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1266191783Srmacklem	else
1267221537Srmacklem		nmp->nm_maxfilesize = OFF_MAX;
1268191783Srmacklem
1269191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1270191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1271191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1272191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1273191783Srmacklem	}
1274191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1275191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1276191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1277191783Srmacklem		nmp->nm_tprintf_delay = 0;
1278191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1279191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1280191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1281191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1282191783Srmacklem	if (nmp->nm_fhsize > 0)
1283191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1284191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1285191783Srmacklem	nmp->nm_nam = nam;
1286191783Srmacklem	/* Set up the sockets and per-host congestion */
1287191783Srmacklem	nmp->nm_sotype = argp->sotype;
1288191783Srmacklem	nmp->nm_soproto = argp->proto;
1289191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1290191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1291191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1292191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1293191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1294191783Srmacklem	else
1295191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1296191783Srmacklem
1297191783Srmacklem
1298191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1299191783Srmacklem		goto bad;
1300191783Srmacklem
1301191783Srmacklem	/*
1302191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1303191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1304191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1305191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1306191783Srmacklem	 * this problem, because one can identify root inodes by their
1307191783Srmacklem	 * number == ROOTINO (2).
1308191783Srmacklem	 */
1309191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1310191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1311191783Srmacklem		/*
1312191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1313191783Srmacklem		 * path needs to be looked up.
1314191783Srmacklem		 */
1315191783Srmacklem		trycnt = 3;
1316191783Srmacklem		do {
1317191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1318191783Srmacklem			    cred, td);
1319191783Srmacklem			if (error)
1320207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1321191783Srmacklem		} while (error && --trycnt > 0);
1322191783Srmacklem		if (error) {
1323191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1324191783Srmacklem			goto bad;
1325191783Srmacklem		}
1326191783Srmacklem	}
1327191783Srmacklem	if (nmp->nm_fhsize > 0) {
1328195762Srmacklem		/*
1329195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1330195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1331195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1332195762Srmacklem		 */
1333195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1334220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1335220732Srmacklem		    LK_EXCLUSIVE);
1336191783Srmacklem		if (error)
1337191783Srmacklem			goto bad;
1338191783Srmacklem		*vpp = NFSTOV(np);
1339191783Srmacklem
1340191783Srmacklem		/*
1341191783Srmacklem		 * Get file attributes and transfer parameters for the
1342191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1343191783Srmacklem		 * (*vpp)->v_type with the correct value.
1344191783Srmacklem		 */
1345191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1346191783Srmacklem		    cred, td, &nfsva, NULL);
1347191783Srmacklem		if (ret) {
1348191783Srmacklem			/*
1349191783Srmacklem			 * Just set default values to get things going.
1350191783Srmacklem			 */
1351191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1352191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1353191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1354191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1355191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1356191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1357191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1358191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1359191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1360191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1361191783Srmacklem		}
1362191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1363191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1364191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1365191783Srmacklem
1366222233Srmacklem		/* Mark if the mount point supports NFSv4 ACLs. */
1367222233Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0 && nfsrv_useacl != 0 &&
1368222233Srmacklem		    ret == 0 &&
1369222233Srmacklem		    NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL)) {
1370222233Srmacklem			MNT_ILOCK(mp);
1371222233Srmacklem			mp->mnt_flag |= MNT_NFS4ACLS;
1372222233Srmacklem			MNT_IUNLOCK(mp);
1373222233Srmacklem		}
1374222233Srmacklem
1375191783Srmacklem		/*
1376191783Srmacklem		 * Lose the lock but keep the ref.
1377191783Srmacklem		 */
1378224082Szack		NFSVOPUNLOCK(*vpp, 0);
1379191783Srmacklem		return (0);
1380191783Srmacklem	}
1381191783Srmacklem	error = EIO;
1382191783Srmacklem
1383191783Srmacklembad:
1384191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1385191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1386191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1387191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1388191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1389191783Srmacklem	FREE(nam, M_SONAME);
1390191783Srmacklem	return (error);
1391191783Srmacklem}
1392191783Srmacklem
1393191783Srmacklem/*
1394191783Srmacklem * unmount system call
1395191783Srmacklem */
1396191783Srmacklemstatic int
1397191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1398191783Srmacklem{
1399191990Sattilio	struct thread *td;
1400191783Srmacklem	struct nfsmount *nmp;
1401191783Srmacklem	int error, flags = 0, trycnt = 0;
1402191783Srmacklem
1403191990Sattilio	td = curthread;
1404191990Sattilio
1405191783Srmacklem	if (mntflags & MNT_FORCE)
1406191783Srmacklem		flags |= FORCECLOSE;
1407191783Srmacklem	nmp = VFSTONFS(mp);
1408191783Srmacklem	/*
1409191783Srmacklem	 * Goes something like this..
1410191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1411191783Srmacklem	 * - Close the socket
1412191783Srmacklem	 * - Free up the data structures
1413191783Srmacklem	 */
1414191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1415191783Srmacklem	if (mntflags & MNT_FORCE) {
1416191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1417191783Srmacklem		if (error)
1418191783Srmacklem			goto out;
1419191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1420191783Srmacklem		nfscl_umount(nmp, td);
1421191783Srmacklem	}
1422191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1423191783Srmacklem	do {
1424191783Srmacklem		error = vflush(mp, 1, flags, td);
1425191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1426207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1427191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1428191783Srmacklem	if (error)
1429191783Srmacklem		goto out;
1430191783Srmacklem
1431191783Srmacklem	/*
1432191783Srmacklem	 * We are now committed to the unmount.
1433191783Srmacklem	 */
1434191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1435191783Srmacklem		nfscl_umount(nmp, td);
1436191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1437191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1438191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1439191783Srmacklem
1440191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1441191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1442191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1443191783Srmacklemout:
1444191783Srmacklem	return (error);
1445191783Srmacklem}
1446191783Srmacklem
1447191783Srmacklem/*
1448191783Srmacklem * Return root of a filesystem
1449191783Srmacklem */
1450191783Srmacklemstatic int
1451191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1452191783Srmacklem{
1453191783Srmacklem	struct vnode *vp;
1454191783Srmacklem	struct nfsmount *nmp;
1455191783Srmacklem	struct nfsnode *np;
1456191783Srmacklem	int error;
1457191783Srmacklem
1458191783Srmacklem	nmp = VFSTONFS(mp);
1459220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1460191783Srmacklem	if (error)
1461191783Srmacklem		return error;
1462191783Srmacklem	vp = NFSTOV(np);
1463191783Srmacklem	/*
1464191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1465191783Srmacklem	 */
1466191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1467191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1468191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1469191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1470191783Srmacklem	} else
1471191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1472191783Srmacklem	if (vp->v_type == VNON)
1473191783Srmacklem	    vp->v_type = VDIR;
1474191783Srmacklem	vp->v_vflag |= VV_ROOT;
1475191783Srmacklem	*vpp = vp;
1476191783Srmacklem	return (0);
1477191783Srmacklem}
1478191783Srmacklem
1479191783Srmacklem/*
1480191783Srmacklem * Flush out the buffer cache
1481191783Srmacklem */
1482191783Srmacklem/* ARGSUSED */
1483191783Srmacklemstatic int
1484191990Sattilionfs_sync(struct mount *mp, int waitfor)
1485191783Srmacklem{
1486191783Srmacklem	struct vnode *vp, *mvp;
1487191990Sattilio	struct thread *td;
1488191783Srmacklem	int error, allerror = 0;
1489191783Srmacklem
1490191990Sattilio	td = curthread;
1491191990Sattilio
1492222329Srmacklem	MNT_ILOCK(mp);
1493191783Srmacklem	/*
1494222329Srmacklem	 * If a forced dismount is in progress, return from here so that
1495222329Srmacklem	 * the umount(2) syscall doesn't get stuck in VFS_SYNC() before
1496222329Srmacklem	 * calling VFS_UNMOUNT().
1497222329Srmacklem	 */
1498222329Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1499222329Srmacklem		MNT_IUNLOCK(mp);
1500222329Srmacklem		return (EBADF);
1501222329Srmacklem	}
1502222329Srmacklem
1503222329Srmacklem	/*
1504191783Srmacklem	 * Force stale buffer cache information to be flushed.
1505191783Srmacklem	 */
1506191783Srmacklemloop:
1507191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1508191783Srmacklem		VI_LOCK(vp);
1509191783Srmacklem		MNT_IUNLOCK(mp);
1510191783Srmacklem		/* XXX Racy bv_cnt check. */
1511224083Szack		if (NFSVOPISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1512191783Srmacklem		    waitfor == MNT_LAZY) {
1513191783Srmacklem			VI_UNLOCK(vp);
1514191783Srmacklem			MNT_ILOCK(mp);
1515191783Srmacklem			continue;
1516191783Srmacklem		}
1517191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1518191783Srmacklem			MNT_ILOCK(mp);
1519191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1520191783Srmacklem			goto loop;
1521191783Srmacklem		}
1522191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1523191783Srmacklem		if (error)
1524191783Srmacklem			allerror = error;
1525224082Szack		NFSVOPUNLOCK(vp, 0);
1526191783Srmacklem		vrele(vp);
1527191783Srmacklem
1528191783Srmacklem		MNT_ILOCK(mp);
1529191783Srmacklem	}
1530191783Srmacklem	MNT_IUNLOCK(mp);
1531191783Srmacklem	return (allerror);
1532191783Srmacklem}
1533191783Srmacklem
1534191783Srmacklemstatic int
1535191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1536191783Srmacklem{
1537191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1538191783Srmacklem	struct vfsquery vq;
1539191783Srmacklem	int error;
1540191783Srmacklem
1541191783Srmacklem	bzero(&vq, sizeof(vq));
1542191783Srmacklem	switch (op) {
1543191783Srmacklem#if 0
1544191783Srmacklem	case VFS_CTL_NOLOCKS:
1545191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1546191783Srmacklem 		if (req->oldptr != NULL) {
1547191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1548191783Srmacklem 			if (error)
1549191783Srmacklem 				return (error);
1550191783Srmacklem 		}
1551191783Srmacklem 		if (req->newptr != NULL) {
1552191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1553191783Srmacklem 			if (error)
1554191783Srmacklem 				return (error);
1555191783Srmacklem			if (val)
1556191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1557191783Srmacklem			else
1558191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1559191783Srmacklem 		}
1560191783Srmacklem		break;
1561191783Srmacklem#endif
1562191783Srmacklem	case VFS_CTL_QUERY:
1563191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1564191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1565191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1566191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1567191783Srmacklem#if 0
1568191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1569191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1570191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1571191783Srmacklem#endif
1572191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1573191783Srmacklem		break;
1574191783Srmacklem 	case VFS_CTL_TIMEO:
1575191783Srmacklem 		if (req->oldptr != NULL) {
1576191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1577191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1578191783Srmacklem 			if (error)
1579191783Srmacklem 				return (error);
1580191783Srmacklem 		}
1581191783Srmacklem 		if (req->newptr != NULL) {
1582191783Srmacklem			error = vfs_suser(mp, req->td);
1583191783Srmacklem			if (error)
1584191783Srmacklem				return (error);
1585191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1586191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1587191783Srmacklem 			if (error)
1588191783Srmacklem 				return (error);
1589191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1590191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1591191783Srmacklem 		}
1592191783Srmacklem		break;
1593191783Srmacklem	default:
1594191783Srmacklem		return (ENOTSUP);
1595191783Srmacklem	}
1596191783Srmacklem	return (0);
1597191783Srmacklem}
1598191783Srmacklem
1599214048Srmacklem/*
1600214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1601214048Srmacklem */
1602214048Srmacklemstatic void
1603214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1604216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1605216931Srmacklem    struct timeval *timeop)
1606214048Srmacklem{
1607214048Srmacklem	struct nfsmount *nmp;
1608214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1609214048Srmacklem
1610214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1611214048Srmacklem	if (fhlenp != NULL)
1612214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1613214048Srmacklem	if (fhp != NULL)
1614214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1615214048Srmacklem	if (sp != NULL)
1616214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1617214048Srmacklem	if (is_v3p != NULL)
1618214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1619214048Srmacklem	if (sizep != NULL)
1620214048Srmacklem		*sizep = np->n_size;
1621216931Srmacklem	if (timeop != NULL) {
1622216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1623216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1624216931Srmacklem	}
1625214048Srmacklem}
1626214048Srmacklem
1627