nfs_clvfsops.c revision 233326
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 233326 2012-03-22 21:07:54Z jhb $");
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 *,
107233326Sjhb		    struct thread *, int, 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,
523233326Sjhb	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO,
524233326Sjhb	    NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
525192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
526191783Srmacklem		return (error);
527191783Srmacklem	}
528191783Srmacklem	return (0);
529191783Srmacklem}
530191783Srmacklem
531191783Srmacklemstatic void
532192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
533192585Srmacklem{
534192585Srmacklem	if (!strcmp(sec, "krb5"))
535192585Srmacklem		*flagsp |= NFSMNT_KERB;
536192585Srmacklem	else if (!strcmp(sec, "krb5i"))
537192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
538192585Srmacklem	else if (!strcmp(sec, "krb5p"))
539192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
540192585Srmacklem}
541192585Srmacklem
542192585Srmacklemstatic void
543191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
544214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
545191783Srmacklem{
546191783Srmacklem	int s;
547191783Srmacklem	int adjsock;
548214048Srmacklem	char *p;
549191783Srmacklem
550191783Srmacklem	s = splnet();
551191783Srmacklem
552191783Srmacklem	/*
553191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
554191783Srmacklem	 * an update.  If this is not an update, then either the read-only
555191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
556191783Srmacklem	 * intentionally at some previous point.
557191783Srmacklem	 */
558191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
559191783Srmacklem		MNT_ILOCK(mp);
560191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
561191783Srmacklem		MNT_IUNLOCK(mp);
562191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
563191783Srmacklem		MNT_ILOCK(mp);
564191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
565191783Srmacklem		MNT_IUNLOCK(mp);
566191783Srmacklem	}
567191783Srmacklem
568191783Srmacklem	/*
569191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
570191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
571191783Srmacklem	 * and soft timeout behavior.
572191783Srmacklem	 */
573191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
574191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
575191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
576220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
577220739Srmacklem			nmp->nm_retry = INT_MAX;
578220739Srmacklem		else
579220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
580191783Srmacklem	}
581191783Srmacklem
582220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
583220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
584220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
585191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
586220739Srmacklem	}
587191783Srmacklem
588220739Srmacklem	/* Clear NFSMNT_RESVPORT for NFSv4, since it is not required. */
589220739Srmacklem	if ((argp->flags & NFSMNT_NFSV4) != 0) {
590220739Srmacklem		argp->flags &= ~NFSMNT_RESVPORT;
591220739Srmacklem		nmp->nm_flag &= ~NFSMNT_RESVPORT;
592220739Srmacklem	}
593220739Srmacklem
594220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
595220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
596220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
597191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
598220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
599191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
600191783Srmacklem
601191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
602191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
603191783Srmacklem	splx(s);
604191783Srmacklem
605191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
606191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
607191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
608191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
609191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
610191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
611191783Srmacklem	}
612191783Srmacklem
613191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
614191783Srmacklem		nmp->nm_retry = argp->retrans;
615191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
616191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
617191783Srmacklem	}
618191783Srmacklem
619191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
620191783Srmacklem		nmp->nm_wsize = argp->wsize;
621191783Srmacklem		/* Round down to multiple of blocksize */
622191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
623191783Srmacklem		if (nmp->nm_wsize <= 0)
624191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
625191783Srmacklem	}
626191783Srmacklem
627191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
628191783Srmacklem		nmp->nm_rsize = argp->rsize;
629191783Srmacklem		/* Round down to multiple of blocksize */
630191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
631191783Srmacklem		if (nmp->nm_rsize <= 0)
632191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
633191783Srmacklem	}
634191783Srmacklem
635191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
636191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
637191783Srmacklem	}
638191783Srmacklem
639191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
640191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
641191783Srmacklem	else
642191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
643191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
644191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
645191783Srmacklem	else
646191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
647191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
648191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
649191783Srmacklem	else
650191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
651191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
652191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
653191783Srmacklem	else
654191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
655191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
656191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
657191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
658191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
659191783Srmacklem
660191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
661191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
662191783Srmacklem			nmp->nm_readahead = argp->readahead;
663191783Srmacklem		else
664191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
665191783Srmacklem	}
666191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
667191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
668191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
669191783Srmacklem		else
670191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
671191783Srmacklem	}
672191783Srmacklem
673191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
674191783Srmacklem		    (nmp->nm_soproto != argp->proto));
675191783Srmacklem
676191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
677191783Srmacklem		int haslock = 0, error = 0;
678191783Srmacklem
679191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
680191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
681191783Srmacklem			if (!error)
682191783Srmacklem				haslock = 1;
683191783Srmacklem		}
684191783Srmacklem		if (!error) {
685191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
686191783Srmacklem		    if (haslock)
687191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
688191783Srmacklem		    nmp->nm_sotype = argp->sotype;
689191783Srmacklem		    nmp->nm_soproto = argp->proto;
690191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
691191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
692191783Srmacklem			    cred, td, 0)) {
693191783Srmacklem				printf("newnfs_args: retrying connect\n");
694207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
695191783Srmacklem			}
696191783Srmacklem		}
697191783Srmacklem	} else {
698191783Srmacklem		nmp->nm_sotype = argp->sotype;
699191783Srmacklem		nmp->nm_soproto = argp->proto;
700191783Srmacklem	}
701214048Srmacklem
702214048Srmacklem	if (hostname != NULL) {
703214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
704214048Srmacklem		    sizeof(nmp->nm_hostname));
705214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
706214048Srmacklem		if (p != NULL)
707214048Srmacklem			*p = '\0';
708214048Srmacklem	}
709191783Srmacklem}
710191783Srmacklem
711221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
712191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
713191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
714192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
715192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
716192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
717192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
718192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
719233326Sjhb    "nametimeo", "negnametimeo", "nocto", "wcommitsize",
720191783Srmacklem    NULL };
721191783Srmacklem
722191783Srmacklem/*
723191783Srmacklem * VFS Operations.
724191783Srmacklem *
725191783Srmacklem * mount system call
726191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
727191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
728191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
729191783Srmacklem * an error after that means that I have to release the mbuf.
730191783Srmacklem */
731191783Srmacklem/* ARGSUSED */
732191783Srmacklemstatic int
733191990Sattilionfs_mount(struct mount *mp)
734191783Srmacklem{
735191783Srmacklem	struct nfs_args args = {
736191783Srmacklem	    .version = NFS_ARGSVERSION,
737191783Srmacklem	    .addr = NULL,
738191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
739191783Srmacklem	    .sotype = SOCK_STREAM,
740191783Srmacklem	    .proto = 0,
741191783Srmacklem	    .fh = NULL,
742191783Srmacklem	    .fhsize = 0,
743220739Srmacklem	    .flags = NFSMNT_RESVPORT,
744191783Srmacklem	    .wsize = NFS_WSIZE,
745191783Srmacklem	    .rsize = NFS_RSIZE,
746191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
747191783Srmacklem	    .timeo = 10,
748191783Srmacklem	    .retrans = NFS_RETRANS,
749191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
750191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
751191783Srmacklem	    .hostname = NULL,
752191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
753191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
754191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
755191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
756191783Srmacklem	};
757192585Srmacklem	int error = 0, ret, len;
758192585Srmacklem	struct sockaddr *nam = NULL;
759191783Srmacklem	struct vnode *vp;
760191990Sattilio	struct thread *td;
761191783Srmacklem	char hst[MNAMELEN];
762191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
763192585Srmacklem	char *opt, *name, *secname;
764233326Sjhb	int nametimeo = NFS_DEFAULT_NAMETIMEO;
765203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
766221190Srmacklem	int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
767221205Srmacklem	size_t hstlen;
768191783Srmacklem
769221190Srmacklem	has_nfs_args_opt = 0;
770191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
771191783Srmacklem		error = EINVAL;
772191783Srmacklem		goto out;
773191783Srmacklem	}
774191783Srmacklem
775191990Sattilio	td = curthread;
776191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
777221040Srmacklem		error = nfs_mountroot(mp);
778191783Srmacklem		goto out;
779191783Srmacklem	}
780191783Srmacklem
781192585Srmacklem	nfscl_init();
782191783Srmacklem
783221190Srmacklem	/*
784221190Srmacklem	 * The old mount_nfs program passed the struct nfs_args
785221190Srmacklem	 * from userspace to kernel.  The new mount_nfs program
786221190Srmacklem	 * passes string options via nmount() from userspace to kernel
787221190Srmacklem	 * and we populate the struct nfs_args in the kernel.
788221190Srmacklem	 */
789221190Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) {
790221190Srmacklem		error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args,
791221190Srmacklem		    sizeof(args));
792221190Srmacklem		if (error != 0)
793221190Srmacklem			goto out;
794221190Srmacklem
795221190Srmacklem		if (args.version != NFS_ARGSVERSION) {
796221190Srmacklem			error = EPROGMISMATCH;
797221190Srmacklem			goto out;
798221190Srmacklem		}
799221190Srmacklem		has_nfs_args_opt = 1;
800221190Srmacklem	}
801221190Srmacklem
802192585Srmacklem	/* Handle the new style options. */
803192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
804192585Srmacklem		args.flags |= NFSMNT_NOCONN;
805192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
806192585Srmacklem		args.flags |= NFSMNT_NOCONN;
807192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
808192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
809192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
810192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
811192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
812192585Srmacklem		args.flags |= NFSMNT_INT;
813192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
814192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
815192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
816192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
817192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
818192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
819192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
820192585Srmacklem		args.flags |= NFSMNT_SOFT;
821192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
822192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
823192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
824192585Srmacklem		args.sotype = SOCK_DGRAM;
825192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
826192585Srmacklem		args.sotype = SOCK_DGRAM;
827192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
828192585Srmacklem		args.sotype = SOCK_STREAM;
829192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
830192585Srmacklem		args.flags |= NFSMNT_NFSV3;
831192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
832192585Srmacklem		args.flags |= NFSMNT_NFSV4;
833192585Srmacklem		args.sotype = SOCK_STREAM;
834191783Srmacklem	}
835192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
836192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
837221436Sru	if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
838221436Sru		args.flags |= NFSMNT_NOCTO;
839192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
840192585Srmacklem		if (opt == NULL) {
841192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
842192585Srmacklem			error = EINVAL;
843192585Srmacklem			goto out;
844192585Srmacklem		}
845192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
846192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
847192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
848192585Srmacklem			    opt);
849192585Srmacklem			error = EINVAL;
850192585Srmacklem			goto out;
851192585Srmacklem		}
852192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
853192585Srmacklem	}
854192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
855192585Srmacklem		if (opt == NULL) {
856192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
857192585Srmacklem			error = EINVAL;
858192585Srmacklem			goto out;
859192585Srmacklem		}
860192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
861192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
862192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
863192585Srmacklem			    opt);
864192585Srmacklem			error = EINVAL;
865192585Srmacklem			goto out;
866192585Srmacklem		}
867192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
868192585Srmacklem	}
869192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
870192585Srmacklem		if (opt == NULL) {
871192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
872192585Srmacklem			error = EINVAL;
873192585Srmacklem			goto out;
874192585Srmacklem		}
875192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
876192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
877192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
878192585Srmacklem			    opt);
879192585Srmacklem			error = EINVAL;
880192585Srmacklem			goto out;
881192585Srmacklem		}
882192585Srmacklem		args.flags |= NFSMNT_WSIZE;
883192585Srmacklem	}
884192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
885192585Srmacklem		if (opt == NULL) {
886192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
887192585Srmacklem			error = EINVAL;
888192585Srmacklem			goto out;
889192585Srmacklem		}
890192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
891192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
892192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
893192585Srmacklem			    opt);
894192585Srmacklem			error = EINVAL;
895192585Srmacklem			goto out;
896192585Srmacklem		}
897192585Srmacklem		args.flags |= NFSMNT_RSIZE;
898192585Srmacklem	}
899192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
900192585Srmacklem		if (opt == NULL) {
901192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
902192585Srmacklem			error = EINVAL;
903192585Srmacklem			goto out;
904192585Srmacklem		}
905192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
906192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
907192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
908192585Srmacklem			    opt);
909192585Srmacklem			error = EINVAL;
910192585Srmacklem			goto out;
911192585Srmacklem		}
912192585Srmacklem		args.flags |= NFSMNT_RETRANS;
913192585Srmacklem	}
914192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
915192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
916192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
917192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
918192585Srmacklem			    opt);
919192585Srmacklem			error = EINVAL;
920192585Srmacklem			goto out;
921192585Srmacklem		}
922192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
923192585Srmacklem	}
924192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
925192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
926192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
927192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
928192585Srmacklem			    opt);
929192585Srmacklem			error = EINVAL;
930192585Srmacklem			goto out;
931192585Srmacklem		}
932192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
933192585Srmacklem	}
934192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
935192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
936192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
937192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
938192585Srmacklem			    opt);
939192585Srmacklem			error = EINVAL;
940192585Srmacklem			goto out;
941192585Srmacklem		}
942192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
943192585Srmacklem	}
944192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
945192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
946192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
947192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
948192585Srmacklem			    opt);
949192585Srmacklem			error = EINVAL;
950192585Srmacklem			goto out;
951192585Srmacklem		}
952192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
953192585Srmacklem	}
954229604Sjhb	if (vfs_getopt(mp->mnt_optnew, "wcommitsize", (void **)&opt, NULL) == 0) {
955229604Sjhb		ret = sscanf(opt, "%d", &args.wcommitsize);
956229604Sjhb		if (ret != 1 || args.wcommitsize < 0) {
957229604Sjhb			vfs_mount_error(mp, "illegal wcommitsize: %s", opt);
958229604Sjhb			error = EINVAL;
959229604Sjhb			goto out;
960229604Sjhb		}
961229604Sjhb		args.flags |= NFSMNT_WCOMMITSIZE;
962229604Sjhb	}
963192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
964192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
965192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
966192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
967192585Srmacklem			    opt);
968192585Srmacklem			error = EINVAL;
969192585Srmacklem			goto out;
970192585Srmacklem		}
971192585Srmacklem		args.flags |= NFSMNT_TIMEO;
972192585Srmacklem	}
973233326Sjhb	if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) {
974233326Sjhb		ret = sscanf(opt, "%d", &nametimeo);
975233326Sjhb		if (ret != 1 || nametimeo < 0) {
976233326Sjhb			vfs_mount_error(mp, "illegal nametimeo: %s", opt);
977233326Sjhb			error = EINVAL;
978233326Sjhb			goto out;
979233326Sjhb		}
980233326Sjhb	}
981203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
982203303Srmacklem	    == 0) {
983203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
984203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
985203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
986203303Srmacklem			    opt);
987203303Srmacklem			error = EINVAL;
988203303Srmacklem			goto out;
989203303Srmacklem		}
990203303Srmacklem	}
991192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
992192585Srmacklem		(void **) &secname, NULL) == 0)
993192585Srmacklem		nfs_sec_name(secname, &args.flags);
994191783Srmacklem
995191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
996191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
997191783Srmacklem
998191783Srmacklem		if (nmp == NULL) {
999191783Srmacklem			error = EIO;
1000191783Srmacklem			goto out;
1001191783Srmacklem		}
1002231636Srmacklem
1003191783Srmacklem		/*
1004231636Srmacklem		 * If a change from TCP->UDP is done and there are thread(s)
1005231636Srmacklem		 * that have I/O RPC(s) in progress with a tranfer size
1006231636Srmacklem		 * greater than NFS_MAXDGRAMDATA, those thread(s) will be
1007231636Srmacklem		 * hung, retrying the RPC(s) forever. Usually these threads
1008231636Srmacklem		 * will be seen doing an uninterruptible sleep on wait channel
1009231636Srmacklem		 * "newnfsreq" (truncated to "newnfsre" by procstat).
1010231636Srmacklem		 */
1011231636Srmacklem		if (args.sotype == SOCK_DGRAM && nmp->nm_sotype == SOCK_STREAM)
1012231636Srmacklem			tprintf(td->td_proc, LOG_WARNING,
1013231636Srmacklem	"Warning: mount -u that changes TCP->UDP can result in hung threads\n");
1014231636Srmacklem
1015231636Srmacklem		/*
1016191783Srmacklem		 * When doing an update, we can't change version,
1017191783Srmacklem		 * security, switch lockd strategies or change cookie
1018191783Srmacklem		 * translation
1019191783Srmacklem		 */
1020191783Srmacklem		args.flags = (args.flags &
1021191783Srmacklem		    ~(NFSMNT_NFSV3 |
1022191783Srmacklem		      NFSMNT_NFSV4 |
1023191783Srmacklem		      NFSMNT_KERB |
1024191783Srmacklem		      NFSMNT_INTEGRITY |
1025191783Srmacklem		      NFSMNT_PRIVACY |
1026191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
1027191783Srmacklem		    (nmp->nm_flag &
1028191783Srmacklem			(NFSMNT_NFSV3 |
1029191783Srmacklem			 NFSMNT_NFSV4 |
1030191783Srmacklem			 NFSMNT_KERB |
1031191783Srmacklem			 NFSMNT_INTEGRITY |
1032191783Srmacklem			 NFSMNT_PRIVACY |
1033191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
1034214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
1035191783Srmacklem		goto out;
1036191783Srmacklem	}
1037191783Srmacklem
1038191783Srmacklem	/*
1039191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
1040191783Srmacklem	 * or no-connection mode for those protocols that support
1041191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
1042191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
1043191783Srmacklem	 * to receive replies from a different IP then the request was
1044191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
1045191783Srmacklem	 * not 0.
1046191783Srmacklem	 */
1047191783Srmacklem	if (nfs_ip_paranoia == 0)
1048191783Srmacklem		args.flags |= NFSMNT_NOCONN;
1049192585Srmacklem
1050221190Srmacklem	if (has_nfs_args_opt != 0) {
1051221190Srmacklem		/*
1052221190Srmacklem		 * In the 'nfs_args' case, the pointers in the args
1053221190Srmacklem		 * structure are in userland - we copy them in here.
1054221190Srmacklem		 */
1055221190Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) {
1056192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
1057191783Srmacklem			error = EINVAL;
1058191783Srmacklem			goto out;
1059191783Srmacklem		}
1060221190Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1061221190Srmacklem		    args.fhsize);
1062221190Srmacklem		if (error != 0)
1063221190Srmacklem			goto out;
1064221205Srmacklem		error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen);
1065221190Srmacklem		if (error != 0)
1066221190Srmacklem			goto out;
1067221205Srmacklem		bzero(&hst[hstlen], MNAMELEN - hstlen);
1068221190Srmacklem		args.hostname = hst;
1069221190Srmacklem		/* sockargs() call must be after above copyin() calls */
1070221190Srmacklem		error = getsockaddr(&nam, (caddr_t)args.addr,
1071221190Srmacklem		    args.addrlen);
1072221190Srmacklem		if (error != 0)
1073221190Srmacklem			goto out;
1074191783Srmacklem	} else {
1075221190Srmacklem		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
1076221190Srmacklem		    &args.fhsize) == 0) {
1077221190Srmacklem			if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
1078221190Srmacklem				vfs_mount_error(mp, "Bad file handle");
1079221190Srmacklem				error = EINVAL;
1080221190Srmacklem				goto out;
1081221190Srmacklem			}
1082221190Srmacklem			bcopy(args.fh, nfh, args.fhsize);
1083221190Srmacklem		} else {
1084221190Srmacklem			args.fhsize = 0;
1085221190Srmacklem		}
1086221190Srmacklem		(void) vfs_getopt(mp->mnt_optnew, "hostname",
1087221190Srmacklem		    (void **)&args.hostname, &len);
1088221190Srmacklem		if (args.hostname == NULL) {
1089221190Srmacklem			vfs_mount_error(mp, "Invalid hostname");
1090221190Srmacklem			error = EINVAL;
1091221190Srmacklem			goto out;
1092221190Srmacklem		}
1093221190Srmacklem		bcopy(args.hostname, hst, MNAMELEN);
1094221190Srmacklem		hst[MNAMELEN - 1] = '\0';
1095192585Srmacklem	}
1096192585Srmacklem
1097192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1098192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1099192585Srmacklem	else
1100192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1101221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1102192585Srmacklem
1103192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1104192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1105192585Srmacklem	else
1106191783Srmacklem		krbname[0] = '\0';
1107221014Srmacklem	krbnamelen = strlen(krbname);
1108192585Srmacklem
1109192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1110192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1111192585Srmacklem	else
1112191783Srmacklem		dirpath[0] = '\0';
1113221014Srmacklem	dirlen = strlen(dirpath);
1114192585Srmacklem
1115222075Srmacklem	if (has_nfs_args_opt == 0) {
1116222075Srmacklem		if (vfs_getopt(mp->mnt_optnew, "addr",
1117222075Srmacklem		    (void **)&args.addr, &args.addrlen) == 0) {
1118222075Srmacklem			if (args.addrlen > SOCK_MAXADDRLEN) {
1119222075Srmacklem				error = ENAMETOOLONG;
1120222075Srmacklem				goto out;
1121222075Srmacklem			}
1122222075Srmacklem			nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1123222075Srmacklem			bcopy(args.addr, nam, args.addrlen);
1124222075Srmacklem			nam->sa_len = args.addrlen;
1125222075Srmacklem		} else {
1126222075Srmacklem			vfs_mount_error(mp, "No server address");
1127222075Srmacklem			error = EINVAL;
1128191783Srmacklem			goto out;
1129191783Srmacklem		}
1130191783Srmacklem	}
1131192585Srmacklem
1132191783Srmacklem	args.fh = nfh;
1133221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1134221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1135233326Sjhb	    nametimeo, negnametimeo);
1136191783Srmacklemout:
1137191783Srmacklem	if (!error) {
1138191783Srmacklem		MNT_ILOCK(mp);
1139191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
1140191783Srmacklem		MNT_IUNLOCK(mp);
1141191783Srmacklem	}
1142191783Srmacklem	return (error);
1143191783Srmacklem}
1144191783Srmacklem
1145191783Srmacklem
1146191783Srmacklem/*
1147191783Srmacklem * VFS Operations.
1148191783Srmacklem *
1149191783Srmacklem * mount system call
1150191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1151191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1152191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1153191783Srmacklem * an error after that means that I have to release the mbuf.
1154191783Srmacklem */
1155191783Srmacklem/* ARGSUSED */
1156191783Srmacklemstatic int
1157230725Smckusicknfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
1158191783Srmacklem{
1159191783Srmacklem	int error;
1160191783Srmacklem	struct nfs_args args;
1161191783Srmacklem
1162191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1163191783Srmacklem	if (error)
1164191783Srmacklem		return error;
1165191783Srmacklem
1166191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1167191783Srmacklem
1168191783Srmacklem	error = kernel_mount(ma, flags);
1169191783Srmacklem	return (error);
1170191783Srmacklem}
1171191783Srmacklem
1172191783Srmacklem/*
1173191783Srmacklem * Common code for mount and mountroot
1174191783Srmacklem */
1175191783Srmacklemstatic int
1176191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1177221014Srmacklem    char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
1178221014Srmacklem    u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
1179233326Sjhb    struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo)
1180191783Srmacklem{
1181191783Srmacklem	struct nfsmount *nmp;
1182191783Srmacklem	struct nfsnode *np;
1183195762Srmacklem	int error, trycnt, ret;
1184191783Srmacklem	struct nfsvattr nfsva;
1185191783Srmacklem	static u_int64_t clval = 0;
1186191783Srmacklem
1187191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1188191783Srmacklem		nmp = VFSTONFS(mp);
1189191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1190191783Srmacklem		FREE(nam, M_SONAME);
1191191783Srmacklem		return (0);
1192191783Srmacklem	} else {
1193191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1194221014Srmacklem		    krbnamelen + dirlen + srvkrbnamelen + 2,
1195221014Srmacklem		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
1196191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1197191783Srmacklem		if (clval == 0)
1198191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1199191783Srmacklem		nmp->nm_clval = clval++;
1200221014Srmacklem		nmp->nm_krbnamelen = krbnamelen;
1201221014Srmacklem		nmp->nm_dirpathlen = dirlen;
1202221014Srmacklem		nmp->nm_srvkrbnamelen = srvkrbnamelen;
1203192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1204191783Srmacklem			/*
1205192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1206192675Srmacklem			 * the nfsv4 state handling operations if there is
1207192675Srmacklem			 * no host based principal set. Use the uid of
1208192675Srmacklem			 * this user if not root, since they are doing the
1209192675Srmacklem			 * mount. I don't think setting this for root will
1210192675Srmacklem			 * work, since root normally does not have user
1211192675Srmacklem			 * credentials in a credentials cache.
1212191783Srmacklem			 */
1213192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1214191783Srmacklem		} else {
1215191783Srmacklem			/*
1216192675Srmacklem			 * Just set to -1, so it won't be used.
1217191783Srmacklem			 */
1218191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1219191783Srmacklem		}
1220191783Srmacklem
1221191783Srmacklem		/* Copy and null terminate all the names */
1222191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1223191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1224191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1225191783Srmacklem		}
1226191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1227191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1228191783Srmacklem			    nmp->nm_dirpathlen);
1229191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1230191783Srmacklem			    + 1] = '\0';
1231191783Srmacklem		}
1232191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1233191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1234191783Srmacklem			    nmp->nm_srvkrbnamelen);
1235191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1236191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1237191783Srmacklem		}
1238191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1239191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1240191783Srmacklem		mp->mnt_data = nmp;
1241214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1242216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1243191783Srmacklem	}
1244191783Srmacklem	vfs_getnewfsid(mp);
1245191783Srmacklem	nmp->nm_mountp = mp;
1246233326Sjhb	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1247229172Srmacklem
1248229172Srmacklem	/*
1249233326Sjhb	 * Since nfs_decode_args() might optionally set them, these
1250233326Sjhb	 * need to be set to defaults before the call, so that the
1251233326Sjhb	 * optional settings aren't overwritten.
1252229172Srmacklem	 */
1253233326Sjhb	nmp->nm_nametimeo = nametimeo;
1254203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1255229172Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1256229172Srmacklem	nmp->nm_retry = NFS_RETRANS;
1257229172Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1258229263Srmacklem	if (desiredvnodes >= 11000)
1259229263Srmacklem		nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1260229263Srmacklem	else
1261229263Srmacklem		nmp->nm_wcommitsize = hibufspace / 10;
1262191783Srmacklem
1263214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1264192585Srmacklem
1265191783Srmacklem	/*
1266191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1267191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1268191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1269191783Srmacklem	 *
1270191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1271191783Srmacklem	 * that we can handle until we find out otherwise.
1272191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1273191783Srmacklem	 * buffer cache using signed(!) block numbers.
1274191783Srmacklem	 */
1275191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1276191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1277191783Srmacklem	else
1278221537Srmacklem		nmp->nm_maxfilesize = OFF_MAX;
1279191783Srmacklem
1280191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1281191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1282191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1283191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1284191783Srmacklem	}
1285191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1286191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1287191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1288191783Srmacklem		nmp->nm_tprintf_delay = 0;
1289191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1290191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1291191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1292191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1293191783Srmacklem	if (nmp->nm_fhsize > 0)
1294191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1295191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1296191783Srmacklem	nmp->nm_nam = nam;
1297191783Srmacklem	/* Set up the sockets and per-host congestion */
1298191783Srmacklem	nmp->nm_sotype = argp->sotype;
1299191783Srmacklem	nmp->nm_soproto = argp->proto;
1300191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1301191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1302191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1303191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1304191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1305191783Srmacklem	else
1306191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1307191783Srmacklem
1308191783Srmacklem
1309191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1310191783Srmacklem		goto bad;
1311191783Srmacklem
1312191783Srmacklem	/*
1313191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1314191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1315191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1316191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1317191783Srmacklem	 * this problem, because one can identify root inodes by their
1318191783Srmacklem	 * number == ROOTINO (2).
1319191783Srmacklem	 */
1320191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1321191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1322191783Srmacklem		/*
1323191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1324191783Srmacklem		 * path needs to be looked up.
1325191783Srmacklem		 */
1326191783Srmacklem		trycnt = 3;
1327191783Srmacklem		do {
1328191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1329191783Srmacklem			    cred, td);
1330191783Srmacklem			if (error)
1331207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1332191783Srmacklem		} while (error && --trycnt > 0);
1333191783Srmacklem		if (error) {
1334191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1335191783Srmacklem			goto bad;
1336191783Srmacklem		}
1337191783Srmacklem	}
1338191783Srmacklem	if (nmp->nm_fhsize > 0) {
1339195762Srmacklem		/*
1340195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1341195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1342195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1343195762Srmacklem		 */
1344195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1345220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1346220732Srmacklem		    LK_EXCLUSIVE);
1347191783Srmacklem		if (error)
1348191783Srmacklem			goto bad;
1349191783Srmacklem		*vpp = NFSTOV(np);
1350191783Srmacklem
1351191783Srmacklem		/*
1352191783Srmacklem		 * Get file attributes and transfer parameters for the
1353191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1354191783Srmacklem		 * (*vpp)->v_type with the correct value.
1355191783Srmacklem		 */
1356191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1357191783Srmacklem		    cred, td, &nfsva, NULL);
1358191783Srmacklem		if (ret) {
1359191783Srmacklem			/*
1360191783Srmacklem			 * Just set default values to get things going.
1361191783Srmacklem			 */
1362191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1363191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1364191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1365191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1366191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1367191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1368191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1369191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1370191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1371191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1372191783Srmacklem		}
1373191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1374191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1375191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1376191783Srmacklem
1377222233Srmacklem		/* Mark if the mount point supports NFSv4 ACLs. */
1378222233Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0 && nfsrv_useacl != 0 &&
1379222233Srmacklem		    ret == 0 &&
1380222233Srmacklem		    NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL)) {
1381222233Srmacklem			MNT_ILOCK(mp);
1382222233Srmacklem			mp->mnt_flag |= MNT_NFS4ACLS;
1383222233Srmacklem			MNT_IUNLOCK(mp);
1384222233Srmacklem		}
1385222233Srmacklem
1386191783Srmacklem		/*
1387191783Srmacklem		 * Lose the lock but keep the ref.
1388191783Srmacklem		 */
1389224082Szack		NFSVOPUNLOCK(*vpp, 0);
1390191783Srmacklem		return (0);
1391191783Srmacklem	}
1392191783Srmacklem	error = EIO;
1393191783Srmacklem
1394191783Srmacklembad:
1395191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1396191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1397191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1398191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1399191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1400191783Srmacklem	FREE(nam, M_SONAME);
1401191783Srmacklem	return (error);
1402191783Srmacklem}
1403191783Srmacklem
1404191783Srmacklem/*
1405191783Srmacklem * unmount system call
1406191783Srmacklem */
1407191783Srmacklemstatic int
1408191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1409191783Srmacklem{
1410191990Sattilio	struct thread *td;
1411191783Srmacklem	struct nfsmount *nmp;
1412191783Srmacklem	int error, flags = 0, trycnt = 0;
1413191783Srmacklem
1414191990Sattilio	td = curthread;
1415191990Sattilio
1416191783Srmacklem	if (mntflags & MNT_FORCE)
1417191783Srmacklem		flags |= FORCECLOSE;
1418191783Srmacklem	nmp = VFSTONFS(mp);
1419191783Srmacklem	/*
1420191783Srmacklem	 * Goes something like this..
1421191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1422191783Srmacklem	 * - Close the socket
1423191783Srmacklem	 * - Free up the data structures
1424191783Srmacklem	 */
1425191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1426191783Srmacklem	if (mntflags & MNT_FORCE) {
1427191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1428191783Srmacklem		if (error)
1429191783Srmacklem			goto out;
1430191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1431191783Srmacklem		nfscl_umount(nmp, td);
1432191783Srmacklem	}
1433191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1434191783Srmacklem	do {
1435191783Srmacklem		error = vflush(mp, 1, flags, td);
1436191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1437207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1438191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1439191783Srmacklem	if (error)
1440191783Srmacklem		goto out;
1441191783Srmacklem
1442191783Srmacklem	/*
1443191783Srmacklem	 * We are now committed to the unmount.
1444191783Srmacklem	 */
1445191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1446191783Srmacklem		nfscl_umount(nmp, td);
1447191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1448191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1449191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1450191783Srmacklem
1451191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1452191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1453191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1454191783Srmacklemout:
1455191783Srmacklem	return (error);
1456191783Srmacklem}
1457191783Srmacklem
1458191783Srmacklem/*
1459191783Srmacklem * Return root of a filesystem
1460191783Srmacklem */
1461191783Srmacklemstatic int
1462191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1463191783Srmacklem{
1464191783Srmacklem	struct vnode *vp;
1465191783Srmacklem	struct nfsmount *nmp;
1466191783Srmacklem	struct nfsnode *np;
1467191783Srmacklem	int error;
1468191783Srmacklem
1469191783Srmacklem	nmp = VFSTONFS(mp);
1470220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1471191783Srmacklem	if (error)
1472191783Srmacklem		return error;
1473191783Srmacklem	vp = NFSTOV(np);
1474191783Srmacklem	/*
1475191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1476191783Srmacklem	 */
1477191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1478191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1479191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1480191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1481191783Srmacklem	} else
1482191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1483191783Srmacklem	if (vp->v_type == VNON)
1484191783Srmacklem	    vp->v_type = VDIR;
1485191783Srmacklem	vp->v_vflag |= VV_ROOT;
1486191783Srmacklem	*vpp = vp;
1487191783Srmacklem	return (0);
1488191783Srmacklem}
1489191783Srmacklem
1490191783Srmacklem/*
1491191783Srmacklem * Flush out the buffer cache
1492191783Srmacklem */
1493191783Srmacklem/* ARGSUSED */
1494191783Srmacklemstatic int
1495191990Sattilionfs_sync(struct mount *mp, int waitfor)
1496191783Srmacklem{
1497191783Srmacklem	struct vnode *vp, *mvp;
1498191990Sattilio	struct thread *td;
1499191783Srmacklem	int error, allerror = 0;
1500191783Srmacklem
1501191990Sattilio	td = curthread;
1502191990Sattilio
1503222329Srmacklem	MNT_ILOCK(mp);
1504191783Srmacklem	/*
1505222329Srmacklem	 * If a forced dismount is in progress, return from here so that
1506222329Srmacklem	 * the umount(2) syscall doesn't get stuck in VFS_SYNC() before
1507222329Srmacklem	 * calling VFS_UNMOUNT().
1508222329Srmacklem	 */
1509222329Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1510222329Srmacklem		MNT_IUNLOCK(mp);
1511222329Srmacklem		return (EBADF);
1512222329Srmacklem	}
1513222329Srmacklem
1514222329Srmacklem	/*
1515191783Srmacklem	 * Force stale buffer cache information to be flushed.
1516191783Srmacklem	 */
1517191783Srmacklemloop:
1518191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1519191783Srmacklem		VI_LOCK(vp);
1520191783Srmacklem		MNT_IUNLOCK(mp);
1521191783Srmacklem		/* XXX Racy bv_cnt check. */
1522224083Szack		if (NFSVOPISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1523191783Srmacklem		    waitfor == MNT_LAZY) {
1524191783Srmacklem			VI_UNLOCK(vp);
1525191783Srmacklem			MNT_ILOCK(mp);
1526191783Srmacklem			continue;
1527191783Srmacklem		}
1528191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1529191783Srmacklem			MNT_ILOCK(mp);
1530191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1531191783Srmacklem			goto loop;
1532191783Srmacklem		}
1533191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1534191783Srmacklem		if (error)
1535191783Srmacklem			allerror = error;
1536224082Szack		NFSVOPUNLOCK(vp, 0);
1537191783Srmacklem		vrele(vp);
1538191783Srmacklem
1539191783Srmacklem		MNT_ILOCK(mp);
1540191783Srmacklem	}
1541191783Srmacklem	MNT_IUNLOCK(mp);
1542191783Srmacklem	return (allerror);
1543191783Srmacklem}
1544191783Srmacklem
1545191783Srmacklemstatic int
1546191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1547191783Srmacklem{
1548191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1549191783Srmacklem	struct vfsquery vq;
1550191783Srmacklem	int error;
1551191783Srmacklem
1552191783Srmacklem	bzero(&vq, sizeof(vq));
1553191783Srmacklem	switch (op) {
1554191783Srmacklem#if 0
1555191783Srmacklem	case VFS_CTL_NOLOCKS:
1556191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1557191783Srmacklem 		if (req->oldptr != NULL) {
1558191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1559191783Srmacklem 			if (error)
1560191783Srmacklem 				return (error);
1561191783Srmacklem 		}
1562191783Srmacklem 		if (req->newptr != NULL) {
1563191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1564191783Srmacklem 			if (error)
1565191783Srmacklem 				return (error);
1566191783Srmacklem			if (val)
1567191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1568191783Srmacklem			else
1569191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1570191783Srmacklem 		}
1571191783Srmacklem		break;
1572191783Srmacklem#endif
1573191783Srmacklem	case VFS_CTL_QUERY:
1574191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1575191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1576191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1577191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1578191783Srmacklem#if 0
1579191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1580191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1581191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1582191783Srmacklem#endif
1583191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1584191783Srmacklem		break;
1585191783Srmacklem 	case VFS_CTL_TIMEO:
1586191783Srmacklem 		if (req->oldptr != NULL) {
1587191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1588191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1589191783Srmacklem 			if (error)
1590191783Srmacklem 				return (error);
1591191783Srmacklem 		}
1592191783Srmacklem 		if (req->newptr != NULL) {
1593191783Srmacklem			error = vfs_suser(mp, req->td);
1594191783Srmacklem			if (error)
1595191783Srmacklem				return (error);
1596191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1597191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1598191783Srmacklem 			if (error)
1599191783Srmacklem 				return (error);
1600191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1601191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1602191783Srmacklem 		}
1603191783Srmacklem		break;
1604191783Srmacklem	default:
1605191783Srmacklem		return (ENOTSUP);
1606191783Srmacklem	}
1607191783Srmacklem	return (0);
1608191783Srmacklem}
1609191783Srmacklem
1610214048Srmacklem/*
1611214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1612214048Srmacklem */
1613214048Srmacklemstatic void
1614214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1615216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1616216931Srmacklem    struct timeval *timeop)
1617214048Srmacklem{
1618214048Srmacklem	struct nfsmount *nmp;
1619214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1620214048Srmacklem
1621214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1622214048Srmacklem	if (fhlenp != NULL)
1623214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1624214048Srmacklem	if (fhp != NULL)
1625214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1626214048Srmacklem	if (sp != NULL)
1627214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1628214048Srmacklem	if (is_v3p != NULL)
1629214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1630214048Srmacklem	if (sizep != NULL)
1631214048Srmacklem		*sizep = np->n_size;
1632216931Srmacklem	if (timeop != NULL) {
1633216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1634216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1635216931Srmacklem	}
1636214048Srmacklem}
1637214048Srmacklem
1638