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$");
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;
83250258Srmacklemextern enum nfsiod_state ncl_iodwant[NFS_MAXASYNCDAEMON];
84250258Srmacklemextern struct nfsmount *ncl_iodmount[NFS_MAXASYNCDAEMON];
85250258Srmacklemextern struct mtx ncl_iod_mutex;
86191783Srmacklem
87191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
88191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
89191783Srmacklem
90221973SrmacklemSYSCTL_DECL(_vfs_nfs);
91191783Srmacklemstatic int nfs_ip_paranoia = 1;
92221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
93191783Srmacklem    &nfs_ip_paranoia, 0, "");
94191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
95221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_INITIAL_DELAY,
96191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
97191783Srmacklem/* how long between console messages "nfs server foo not responding" */
98191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
99221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_DELAY,
100191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
101191783Srmacklem
102221040Srmacklemstatic int	nfs_mountroot(struct mount *);
103192585Srmacklemstatic void	nfs_sec_name(char *, int *);
104191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
105214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
106214048Srmacklem		    struct thread *);
107191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
108221014Srmacklem		    struct sockaddr *, char *, u_char *, int, u_char *, int,
109221014Srmacklem		    u_char *, int, struct vnode **, struct ucred *,
110233326Sjhb		    struct thread *, int, int);
111214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
112216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
113216931Srmacklem		    struct timeval *);
114191783Srmacklemstatic vfs_mount_t nfs_mount;
115191783Srmacklemstatic vfs_cmount_t nfs_cmount;
116191783Srmacklemstatic vfs_unmount_t nfs_unmount;
117191783Srmacklemstatic vfs_root_t nfs_root;
118191783Srmacklemstatic vfs_statfs_t nfs_statfs;
119191783Srmacklemstatic vfs_sync_t nfs_sync;
120191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
121191783Srmacklem
122191783Srmacklem/*
123191783Srmacklem * nfs vfs operations.
124191783Srmacklem */
125191783Srmacklemstatic struct vfsops nfs_vfsops = {
126191783Srmacklem	.vfs_init =		ncl_init,
127191783Srmacklem	.vfs_mount =		nfs_mount,
128191783Srmacklem	.vfs_cmount =		nfs_cmount,
129191783Srmacklem	.vfs_root =		nfs_root,
130191783Srmacklem	.vfs_statfs =		nfs_statfs,
131191783Srmacklem	.vfs_sync =		nfs_sync,
132191783Srmacklem	.vfs_uninit =		ncl_uninit,
133191783Srmacklem	.vfs_unmount =		nfs_unmount,
134191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
135191783Srmacklem};
136251147SjhbVFS_SET(nfs_vfsops, nfs, VFCF_NETWORK | VFCF_SBDRY);
137191783Srmacklem
138191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
139221139SrmacklemMODULE_VERSION(nfs, 1);
140221139SrmacklemMODULE_DEPEND(nfs, nfscommon, 1, 1, 1);
141221139SrmacklemMODULE_DEPEND(nfs, krpc, 1, 1, 1);
142221139SrmacklemMODULE_DEPEND(nfs, nfssvc, 1, 1, 1);
143221139SrmacklemMODULE_DEPEND(nfs, nfslock, 1, 1, 1);
144191783Srmacklem
145191783Srmacklem/*
146221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it
147221066Srmacklem * can be shared by both NFS clients. It is declared here so that it
148221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it
149221066Srmacklem * isn't used in that case.
150191783Srmacklem */
151221066Srmacklem#if !defined(NFS_ROOT) && !defined(NFSCLIENT)
152221066Srmacklemstruct nfs_diskless	nfs_diskless = { { { 0 } } };
153221066Srmacklemstruct nfsv3_diskless	nfsv3_diskless = { { { 0 } } };
154221066Srmacklemint			nfs_diskless_valid = 0;
155221066Srmacklem#endif
156221066Srmacklem
157221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
158221040Srmacklem    &nfs_diskless_valid, 0,
159192145Srmacklem    "Has the diskless struct been filled correctly");
160191783Srmacklem
161221973SrmacklemSYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
162221040Srmacklem    nfsv3_diskless.root_hostnam, 0, "Path to nfs root");
163191783Srmacklem
164221973SrmacklemSYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
165221040Srmacklem    &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr),
166192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
167191783Srmacklem
168191783Srmacklem
169191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
170191783Srmacklemstatic int	nfs_mountdiskless(char *,
171191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
172191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
173191783Srmacklemstatic void	nfs_convert_diskless(void);
174191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
175191783Srmacklem		    struct onfs_args *oargs);
176191783Srmacklem
177191783Srmacklemint
178191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
179191783Srmacklem{
180191783Srmacklem	int iosize, maxio;
181191783Srmacklem
182191783Srmacklem	/* First, set the upper limit for iosize */
183191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
184191783Srmacklem		maxio = NFS_MAXBSIZE;
185191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
186191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
187191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
188191783Srmacklem		else
189191783Srmacklem			maxio = NFS_MAXBSIZE;
190191783Srmacklem	} else {
191191783Srmacklem		maxio = NFS_V2MAXDATA;
192191783Srmacklem	}
193191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
194191783Srmacklem		nmp->nm_rsize = maxio;
195191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
196191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
197191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
198191783Srmacklem		nmp->nm_readdirsize = maxio;
199191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
200191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
201191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
202191783Srmacklem		nmp->nm_wsize = maxio;
203191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
204191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
205191783Srmacklem
206191783Srmacklem	/*
207191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
208191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
209191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
210191783Srmacklem	 * space.
211191783Srmacklem	 */
212191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
213191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
214191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
215191783Srmacklem	return (iosize);
216191783Srmacklem}
217191783Srmacklem
218191783Srmacklemstatic void
219191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
220191783Srmacklem{
221191783Srmacklem
222191783Srmacklem	args->version = NFS_ARGSVERSION;
223191783Srmacklem	args->addr = oargs->addr;
224191783Srmacklem	args->addrlen = oargs->addrlen;
225191783Srmacklem	args->sotype = oargs->sotype;
226191783Srmacklem	args->proto = oargs->proto;
227191783Srmacklem	args->fh = oargs->fh;
228191783Srmacklem	args->fhsize = oargs->fhsize;
229191783Srmacklem	args->flags = oargs->flags;
230191783Srmacklem	args->wsize = oargs->wsize;
231191783Srmacklem	args->rsize = oargs->rsize;
232191783Srmacklem	args->readdirsize = oargs->readdirsize;
233191783Srmacklem	args->timeo = oargs->timeo;
234191783Srmacklem	args->retrans = oargs->retrans;
235191783Srmacklem	args->readahead = oargs->readahead;
236191783Srmacklem	args->hostname = oargs->hostname;
237191783Srmacklem}
238191783Srmacklem
239191783Srmacklemstatic void
240191783Srmacklemnfs_convert_diskless(void)
241191783Srmacklem{
242191783Srmacklem
243221040Srmacklem	bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif,
244221040Srmacklem		sizeof(struct ifaliasreq));
245221040Srmacklem	bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway,
246221040Srmacklem		sizeof(struct sockaddr_in));
247221040Srmacklem	nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args);
248221040Srmacklem	if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
249221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_MYFH;
250221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH);
251191783Srmacklem	} else {
252221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_V2FH;
253221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH);
254191783Srmacklem	}
255221040Srmacklem	bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr,
256221040Srmacklem		sizeof(struct sockaddr_in));
257221040Srmacklem	bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN);
258221040Srmacklem	nfsv3_diskless.root_time = nfs_diskless.root_time;
259221040Srmacklem	bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam,
260221040Srmacklem		MAXHOSTNAMELEN);
261221040Srmacklem	nfs_diskless_valid = 3;
262191783Srmacklem}
263191783Srmacklem
264191783Srmacklem/*
265191783Srmacklem * nfs statfs call
266191783Srmacklem */
267191783Srmacklemstatic int
268191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
269191783Srmacklem{
270191783Srmacklem	struct vnode *vp;
271191990Sattilio	struct thread *td;
272191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
273191783Srmacklem	struct nfsvattr nfsva;
274191783Srmacklem	struct nfsfsinfo fs;
275191783Srmacklem	struct nfsstatfs sb;
276191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
277191783Srmacklem	struct nfsnode *np;
278191783Srmacklem
279191990Sattilio	td = curthread;
280191990Sattilio
281191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
282191783Srmacklem	if (error)
283191783Srmacklem		return (error);
284220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
285191783Srmacklem	if (error) {
286191783Srmacklem		vfs_unbusy(mp);
287191783Srmacklem		return (error);
288191783Srmacklem	}
289191783Srmacklem	vp = NFSTOV(np);
290191783Srmacklem	mtx_lock(&nmp->nm_mtx);
291191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
292191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
293191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
294191783Srmacklem		    &attrflag, NULL);
295191783Srmacklem		if (!error)
296191783Srmacklem			gotfsinfo = 1;
297191783Srmacklem	} else
298191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
299191783Srmacklem	if (!error)
300191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
301191783Srmacklem		    &attrflag, NULL);
302191783Srmacklem	if (attrflag == 0) {
303191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
304191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
305191783Srmacklem		if (ret) {
306191783Srmacklem			/*
307191783Srmacklem			 * Just set default values to get things going.
308191783Srmacklem			 */
309191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
310191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
311191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
312191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
313191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
314191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
315191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
316191783Srmacklem			nfsva.na_vattr.va_gen = 1;
317191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
318191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
319191783Srmacklem		}
320191783Srmacklem	}
321191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
322191783Srmacklem	if (!error) {
323191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
324191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
325191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
326191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
327191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
328191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
329191783Srmacklem	    if (sbp != &mp->mnt_stat) {
330191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
331191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
332191783Srmacklem	    }
333191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
334191783Srmacklem	} else if (NFS_ISV4(vp)) {
335191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
336191783Srmacklem	}
337191783Srmacklem	vput(vp);
338191783Srmacklem	vfs_unbusy(mp);
339191783Srmacklem	return (error);
340191783Srmacklem}
341191783Srmacklem
342191783Srmacklem/*
343191783Srmacklem * nfs version 3 fsinfo rpc call
344191783Srmacklem */
345191783Srmacklemint
346191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
347191783Srmacklem    struct thread *td)
348191783Srmacklem{
349191783Srmacklem	struct nfsfsinfo fs;
350191783Srmacklem	struct nfsvattr nfsva;
351191783Srmacklem	int error, attrflag;
352191783Srmacklem
353191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
354191783Srmacklem	if (!error) {
355191783Srmacklem		if (attrflag)
356191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
357191783Srmacklem			    1);
358191783Srmacklem		mtx_lock(&nmp->nm_mtx);
359191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
360191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
361191783Srmacklem	}
362191783Srmacklem	return (error);
363191783Srmacklem}
364191783Srmacklem
365191783Srmacklem/*
366191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
367221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary
368191783Srmacklem * bootstrap.
369191783Srmacklem * It goes something like this:
370191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
371191783Srmacklem *   can talk to the server
372221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as
373191783Srmacklem *   a default gateway.
374191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
375191783Srmacklem *
376191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
377191783Srmacklem * structure, as well as other global NFS client variables here, as
378192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
379191783Srmacklem * client activity occurs.
380191783Srmacklem */
381221040Srmacklemstatic int
382221040Srmacklemnfs_mountroot(struct mount *mp)
383191783Srmacklem{
384192145Srmacklem	struct thread *td = curthread;
385221040Srmacklem	struct nfsv3_diskless *nd = &nfsv3_diskless;
386191783Srmacklem	struct socket *so;
387191783Srmacklem	struct vnode *vp;
388191783Srmacklem	struct ifreq ir;
389193066Sjamie	int error;
390191783Srmacklem	u_long l;
391191783Srmacklem	char buf[128];
392191783Srmacklem	char *cp;
393191783Srmacklem
394191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
395192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
396191783Srmacklem#elif defined(NFS_ROOT)
397191783Srmacklem	nfs_setup_diskless();
398191783Srmacklem#endif
399191783Srmacklem
400221040Srmacklem	if (nfs_diskless_valid == 0)
401191783Srmacklem		return (-1);
402221040Srmacklem	if (nfs_diskless_valid == 1)
403191783Srmacklem		nfs_convert_diskless();
404191783Srmacklem
405191783Srmacklem	/*
406191783Srmacklem	 * XXX splnet, so networks will receive...
407191783Srmacklem	 */
408191783Srmacklem	splnet();
409191783Srmacklem
410191783Srmacklem	/*
411191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
412191783Srmacklem	 * talk to the server.
413191783Srmacklem	 */
414191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
415191783Srmacklem	    td->td_ucred, td);
416191783Srmacklem	if (error)
417192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
418191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
419191783Srmacklem
420191783Srmacklem#if 0 /* XXX Bad idea */
421191783Srmacklem	/*
422191783Srmacklem	 * We might not have been told the right interface, so we pass
423191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
424191783Srmacklem	 * one of them configured.
425191783Srmacklem	 */
426191783Srmacklem
427191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
428191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
429191783Srmacklem		nd->myif.ifra_name[i] <= '9';
430191783Srmacklem		nd->myif.ifra_name[i] ++) {
431191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
432191783Srmacklem		if(!error)
433191783Srmacklem			break;
434191783Srmacklem	}
435191783Srmacklem#endif
436191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
437191783Srmacklem	if (error)
438192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
439191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
440191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
441191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
442191783Srmacklem		freeenv(cp);
443191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
444191783Srmacklem		if (error)
445192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
446191783Srmacklem	}
447191783Srmacklem	soclose(so);
448191783Srmacklem
449191783Srmacklem	/*
450191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
451191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
452191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
453191783Srmacklem	 * to avoid panicking inappropriately in that situation.
454191783Srmacklem	 */
455191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
456191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
457191783Srmacklem		struct sockaddr_in mask, sin;
458191783Srmacklem
459191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
460191783Srmacklem		sin = mask;
461191783Srmacklem		sin.sin_family = AF_INET;
462191783Srmacklem		sin.sin_len = sizeof(sin);
463192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
464218757Sbz		CURVNET_SET(TD_TO_VNET(td));
465232292Sbz		error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&sin,
466191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
467191783Srmacklem		    (struct sockaddr *)&mask,
468232292Sbz		    RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB);
469218757Sbz		CURVNET_RESTORE();
470191783Srmacklem		if (error)
471192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
472191783Srmacklem	}
473191783Srmacklem
474191783Srmacklem	/*
475191783Srmacklem	 * Create the rootfs mount point.
476191783Srmacklem	 */
477191783Srmacklem	nd->root_args.fh = nd->root_fh;
478191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
479191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
480191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
481191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
482191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
483191783Srmacklem	printf("NFS ROOT: %s\n", buf);
484192145Srmacklem	nd->root_args.hostname = buf;
485191783Srmacklem	if ((error = nfs_mountdiskless(buf,
486191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
487191783Srmacklem		return (error);
488191783Srmacklem	}
489191783Srmacklem
490191783Srmacklem	/*
491191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
492191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
493191783Srmacklem	 * mount the right /var based upon its preset value.
494191783Srmacklem	 */
495193066Sjamie	mtx_lock(&prison0.pr_mtx);
496194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
497194118Sjamie	    sizeof(prison0.pr_hostname));
498193066Sjamie	mtx_unlock(&prison0.pr_mtx);
499191783Srmacklem	inittodr(ntohl(nd->root_time));
500191783Srmacklem	return (0);
501191783Srmacklem}
502191783Srmacklem
503191783Srmacklem/*
504191783Srmacklem * Internal version of mount system call for diskless setup.
505191783Srmacklem */
506191783Srmacklemstatic int
507191783Srmacklemnfs_mountdiskless(char *path,
508191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
509191783Srmacklem    struct vnode **vpp, struct mount *mp)
510191783Srmacklem{
511191783Srmacklem	struct sockaddr *nam;
512221014Srmacklem	int dirlen, error;
513221014Srmacklem	char *dirpath;
514191783Srmacklem
515221014Srmacklem	/*
516221014Srmacklem	 * Find the directory path in "path", which also has the server's
517221014Srmacklem	 * name/ip address in it.
518221014Srmacklem	 */
519221014Srmacklem	dirpath = strchr(path, ':');
520221014Srmacklem	if (dirpath != NULL)
521221014Srmacklem		dirlen = strlen(++dirpath);
522221014Srmacklem	else
523221014Srmacklem		dirlen = 0;
524191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
525221014Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
526233326Sjhb	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO,
527233326Sjhb	    NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
528192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
529191783Srmacklem		return (error);
530191783Srmacklem	}
531191783Srmacklem	return (0);
532191783Srmacklem}
533191783Srmacklem
534191783Srmacklemstatic void
535192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
536192585Srmacklem{
537192585Srmacklem	if (!strcmp(sec, "krb5"))
538192585Srmacklem		*flagsp |= NFSMNT_KERB;
539192585Srmacklem	else if (!strcmp(sec, "krb5i"))
540192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
541192585Srmacklem	else if (!strcmp(sec, "krb5p"))
542192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
543192585Srmacklem}
544192585Srmacklem
545192585Srmacklemstatic void
546191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
547214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
548191783Srmacklem{
549191783Srmacklem	int s;
550191783Srmacklem	int adjsock;
551214048Srmacklem	char *p;
552191783Srmacklem
553191783Srmacklem	s = splnet();
554191783Srmacklem
555191783Srmacklem	/*
556191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
557191783Srmacklem	 * an update.  If this is not an update, then either the read-only
558191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
559191783Srmacklem	 * intentionally at some previous point.
560191783Srmacklem	 */
561191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
562191783Srmacklem		MNT_ILOCK(mp);
563191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
564191783Srmacklem		MNT_IUNLOCK(mp);
565191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
566191783Srmacklem		MNT_ILOCK(mp);
567191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
568191783Srmacklem		MNT_IUNLOCK(mp);
569191783Srmacklem	}
570191783Srmacklem
571191783Srmacklem	/*
572191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
573191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
574191783Srmacklem	 * and soft timeout behavior.
575191783Srmacklem	 */
576191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
577191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
578191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
579220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
580220739Srmacklem			nmp->nm_retry = INT_MAX;
581220739Srmacklem		else
582220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
583191783Srmacklem	}
584191783Srmacklem
585220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
586220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
587220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
588191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
589220739Srmacklem	}
590191783Srmacklem
591220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
592220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
593220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
594191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
595220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
596191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
597191783Srmacklem
598191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
599191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
600191783Srmacklem	splx(s);
601191783Srmacklem
602191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
603191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
604191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
605191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
606191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
607191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
608191783Srmacklem	}
609191783Srmacklem
610191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
611191783Srmacklem		nmp->nm_retry = argp->retrans;
612191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
613191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
614191783Srmacklem	}
615191783Srmacklem
616191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
617191783Srmacklem		nmp->nm_wsize = argp->wsize;
618191783Srmacklem		/* Round down to multiple of blocksize */
619191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
620191783Srmacklem		if (nmp->nm_wsize <= 0)
621191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
622191783Srmacklem	}
623191783Srmacklem
624191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
625191783Srmacklem		nmp->nm_rsize = argp->rsize;
626191783Srmacklem		/* Round down to multiple of blocksize */
627191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
628191783Srmacklem		if (nmp->nm_rsize <= 0)
629191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
630191783Srmacklem	}
631191783Srmacklem
632191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
633191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
634191783Srmacklem	}
635191783Srmacklem
636191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
637191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
638191783Srmacklem	else
639191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
640191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
641191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
642191783Srmacklem	else
643191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
644191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
645191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
646191783Srmacklem	else
647191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
648191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
649191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
650191783Srmacklem	else
651191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
652191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
653191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
654191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
655191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
656191783Srmacklem
657191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
658191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
659191783Srmacklem			nmp->nm_readahead = argp->readahead;
660191783Srmacklem		else
661191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
662191783Srmacklem	}
663191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
664191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
665191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
666191783Srmacklem		else
667191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
668191783Srmacklem	}
669191783Srmacklem
670191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
671191783Srmacklem		    (nmp->nm_soproto != argp->proto));
672191783Srmacklem
673191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
674191783Srmacklem		int haslock = 0, error = 0;
675191783Srmacklem
676191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
677191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
678191783Srmacklem			if (!error)
679191783Srmacklem				haslock = 1;
680191783Srmacklem		}
681191783Srmacklem		if (!error) {
682191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
683191783Srmacklem		    if (haslock)
684191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
685191783Srmacklem		    nmp->nm_sotype = argp->sotype;
686191783Srmacklem		    nmp->nm_soproto = argp->proto;
687191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
688191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
689191783Srmacklem			    cred, td, 0)) {
690191783Srmacklem				printf("newnfs_args: retrying connect\n");
691207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
692191783Srmacklem			}
693191783Srmacklem		}
694191783Srmacklem	} else {
695191783Srmacklem		nmp->nm_sotype = argp->sotype;
696191783Srmacklem		nmp->nm_soproto = argp->proto;
697191783Srmacklem	}
698214048Srmacklem
699214048Srmacklem	if (hostname != NULL) {
700214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
701214048Srmacklem		    sizeof(nmp->nm_hostname));
702214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
703214048Srmacklem		if (p != NULL)
704214048Srmacklem			*p = '\0';
705214048Srmacklem	}
706191783Srmacklem}
707191783Srmacklem
708221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
709191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
710191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
711192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
712192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
713192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
714192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
715192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
716260170Srmacklem    "nametimeo", "negnametimeo", "nocto", "noncontigwr", "wcommitsize",
717191783Srmacklem    NULL };
718191783Srmacklem
719191783Srmacklem/*
720191783Srmacklem * VFS Operations.
721191783Srmacklem *
722191783Srmacklem * mount system call
723191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
724191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
725191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
726191783Srmacklem * an error after that means that I have to release the mbuf.
727191783Srmacklem */
728191783Srmacklem/* ARGSUSED */
729191783Srmacklemstatic int
730191990Sattilionfs_mount(struct mount *mp)
731191783Srmacklem{
732191783Srmacklem	struct nfs_args args = {
733191783Srmacklem	    .version = NFS_ARGSVERSION,
734191783Srmacklem	    .addr = NULL,
735191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
736191783Srmacklem	    .sotype = SOCK_STREAM,
737191783Srmacklem	    .proto = 0,
738191783Srmacklem	    .fh = NULL,
739191783Srmacklem	    .fhsize = 0,
740220739Srmacklem	    .flags = NFSMNT_RESVPORT,
741191783Srmacklem	    .wsize = NFS_WSIZE,
742191783Srmacklem	    .rsize = NFS_RSIZE,
743191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
744191783Srmacklem	    .timeo = 10,
745191783Srmacklem	    .retrans = NFS_RETRANS,
746191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
747191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
748191783Srmacklem	    .hostname = NULL,
749191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
750191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
751191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
752191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
753191783Srmacklem	};
754192585Srmacklem	int error = 0, ret, len;
755192585Srmacklem	struct sockaddr *nam = NULL;
756191783Srmacklem	struct vnode *vp;
757191990Sattilio	struct thread *td;
758191783Srmacklem	char hst[MNAMELEN];
759191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
760192585Srmacklem	char *opt, *name, *secname;
761233326Sjhb	int nametimeo = NFS_DEFAULT_NAMETIMEO;
762203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
763221190Srmacklem	int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
764221205Srmacklem	size_t hstlen;
765191783Srmacklem
766221190Srmacklem	has_nfs_args_opt = 0;
767191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
768191783Srmacklem		error = EINVAL;
769191783Srmacklem		goto out;
770191783Srmacklem	}
771191783Srmacklem
772191990Sattilio	td = curthread;
773191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
774221040Srmacklem		error = nfs_mountroot(mp);
775191783Srmacklem		goto out;
776191783Srmacklem	}
777191783Srmacklem
778192585Srmacklem	nfscl_init();
779191783Srmacklem
780221190Srmacklem	/*
781221190Srmacklem	 * The old mount_nfs program passed the struct nfs_args
782221190Srmacklem	 * from userspace to kernel.  The new mount_nfs program
783221190Srmacklem	 * passes string options via nmount() from userspace to kernel
784221190Srmacklem	 * and we populate the struct nfs_args in the kernel.
785221190Srmacklem	 */
786221190Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) {
787221190Srmacklem		error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args,
788221190Srmacklem		    sizeof(args));
789221190Srmacklem		if (error != 0)
790221190Srmacklem			goto out;
791221190Srmacklem
792221190Srmacklem		if (args.version != NFS_ARGSVERSION) {
793221190Srmacklem			error = EPROGMISMATCH;
794221190Srmacklem			goto out;
795221190Srmacklem		}
796221190Srmacklem		has_nfs_args_opt = 1;
797221190Srmacklem	}
798221190Srmacklem
799192585Srmacklem	/* Handle the new style options. */
800192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
801192585Srmacklem		args.flags |= NFSMNT_NOCONN;
802192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
803192585Srmacklem		args.flags |= NFSMNT_NOCONN;
804192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
805192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
806192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
807192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
808192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
809192585Srmacklem		args.flags |= NFSMNT_INT;
810192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
811192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
812192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
813192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
814192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
815192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
816192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
817192585Srmacklem		args.flags |= NFSMNT_SOFT;
818192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
819192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
820192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
821192585Srmacklem		args.sotype = SOCK_DGRAM;
822192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
823192585Srmacklem		args.sotype = SOCK_DGRAM;
824192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
825192585Srmacklem		args.sotype = SOCK_STREAM;
826192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
827192585Srmacklem		args.flags |= NFSMNT_NFSV3;
828192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
829192585Srmacklem		args.flags |= NFSMNT_NFSV4;
830192585Srmacklem		args.sotype = SOCK_STREAM;
831191783Srmacklem	}
832192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
833192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
834221436Sru	if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
835221436Sru		args.flags |= NFSMNT_NOCTO;
836260170Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noncontigwr", NULL, NULL) == 0)
837260170Srmacklem		args.flags |= NFSMNT_NONCONTIGWR;
838192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
839192585Srmacklem		if (opt == NULL) {
840192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
841192585Srmacklem			error = EINVAL;
842192585Srmacklem			goto out;
843192585Srmacklem		}
844192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
845192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
846192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
847192585Srmacklem			    opt);
848192585Srmacklem			error = EINVAL;
849192585Srmacklem			goto out;
850192585Srmacklem		}
851192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
852192585Srmacklem	}
853192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
854192585Srmacklem		if (opt == NULL) {
855192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
856192585Srmacklem			error = EINVAL;
857192585Srmacklem			goto out;
858192585Srmacklem		}
859192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
860192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
861192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
862192585Srmacklem			    opt);
863192585Srmacklem			error = EINVAL;
864192585Srmacklem			goto out;
865192585Srmacklem		}
866192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
867192585Srmacklem	}
868192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
869192585Srmacklem		if (opt == NULL) {
870192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
871192585Srmacklem			error = EINVAL;
872192585Srmacklem			goto out;
873192585Srmacklem		}
874192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
875192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
876192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
877192585Srmacklem			    opt);
878192585Srmacklem			error = EINVAL;
879192585Srmacklem			goto out;
880192585Srmacklem		}
881192585Srmacklem		args.flags |= NFSMNT_WSIZE;
882192585Srmacklem	}
883192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
884192585Srmacklem		if (opt == NULL) {
885192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
886192585Srmacklem			error = EINVAL;
887192585Srmacklem			goto out;
888192585Srmacklem		}
889192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
890192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
891192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
892192585Srmacklem			    opt);
893192585Srmacklem			error = EINVAL;
894192585Srmacklem			goto out;
895192585Srmacklem		}
896192585Srmacklem		args.flags |= NFSMNT_RSIZE;
897192585Srmacklem	}
898192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
899192585Srmacklem		if (opt == NULL) {
900192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
901192585Srmacklem			error = EINVAL;
902192585Srmacklem			goto out;
903192585Srmacklem		}
904192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
905192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
906192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
907192585Srmacklem			    opt);
908192585Srmacklem			error = EINVAL;
909192585Srmacklem			goto out;
910192585Srmacklem		}
911192585Srmacklem		args.flags |= NFSMNT_RETRANS;
912192585Srmacklem	}
913192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
914192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
915192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
916192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
917192585Srmacklem			    opt);
918192585Srmacklem			error = EINVAL;
919192585Srmacklem			goto out;
920192585Srmacklem		}
921192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
922192585Srmacklem	}
923192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
924192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
925192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
926192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
927192585Srmacklem			    opt);
928192585Srmacklem			error = EINVAL;
929192585Srmacklem			goto out;
930192585Srmacklem		}
931192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
932192585Srmacklem	}
933192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
934192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
935192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
936192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
937192585Srmacklem			    opt);
938192585Srmacklem			error = EINVAL;
939192585Srmacklem			goto out;
940192585Srmacklem		}
941192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
942192585Srmacklem	}
943192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
944192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
945192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
946192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
947192585Srmacklem			    opt);
948192585Srmacklem			error = EINVAL;
949192585Srmacklem			goto out;
950192585Srmacklem		}
951192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
952192585Srmacklem	}
953229604Sjhb	if (vfs_getopt(mp->mnt_optnew, "wcommitsize", (void **)&opt, NULL) == 0) {
954229604Sjhb		ret = sscanf(opt, "%d", &args.wcommitsize);
955229604Sjhb		if (ret != 1 || args.wcommitsize < 0) {
956229604Sjhb			vfs_mount_error(mp, "illegal wcommitsize: %s", opt);
957229604Sjhb			error = EINVAL;
958229604Sjhb			goto out;
959229604Sjhb		}
960229604Sjhb		args.flags |= NFSMNT_WCOMMITSIZE;
961229604Sjhb	}
962192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
963192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
964192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
965192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
966192585Srmacklem			    opt);
967192585Srmacklem			error = EINVAL;
968192585Srmacklem			goto out;
969192585Srmacklem		}
970192585Srmacklem		args.flags |= NFSMNT_TIMEO;
971192585Srmacklem	}
972233326Sjhb	if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) {
973233326Sjhb		ret = sscanf(opt, "%d", &nametimeo);
974233326Sjhb		if (ret != 1 || nametimeo < 0) {
975233326Sjhb			vfs_mount_error(mp, "illegal nametimeo: %s", opt);
976233326Sjhb			error = EINVAL;
977233326Sjhb			goto out;
978233326Sjhb		}
979233326Sjhb	}
980203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
981203303Srmacklem	    == 0) {
982203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
983203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
984203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
985203303Srmacklem			    opt);
986203303Srmacklem			error = EINVAL;
987203303Srmacklem			goto out;
988203303Srmacklem		}
989203303Srmacklem	}
990192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
991192585Srmacklem		(void **) &secname, NULL) == 0)
992192585Srmacklem		nfs_sec_name(secname, &args.flags);
993191783Srmacklem
994191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
995191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
996191783Srmacklem
997191783Srmacklem		if (nmp == NULL) {
998191783Srmacklem			error = EIO;
999191783Srmacklem			goto out;
1000191783Srmacklem		}
1001231636Srmacklem
1002191783Srmacklem		/*
1003231636Srmacklem		 * If a change from TCP->UDP is done and there are thread(s)
1004231636Srmacklem		 * that have I/O RPC(s) in progress with a tranfer size
1005231636Srmacklem		 * greater than NFS_MAXDGRAMDATA, those thread(s) will be
1006231636Srmacklem		 * hung, retrying the RPC(s) forever. Usually these threads
1007231636Srmacklem		 * will be seen doing an uninterruptible sleep on wait channel
1008231636Srmacklem		 * "newnfsreq" (truncated to "newnfsre" by procstat).
1009231636Srmacklem		 */
1010231636Srmacklem		if (args.sotype == SOCK_DGRAM && nmp->nm_sotype == SOCK_STREAM)
1011231636Srmacklem			tprintf(td->td_proc, LOG_WARNING,
1012231636Srmacklem	"Warning: mount -u that changes TCP->UDP can result in hung threads\n");
1013231636Srmacklem
1014231636Srmacklem		/*
1015191783Srmacklem		 * When doing an update, we can't change version,
1016191783Srmacklem		 * security, switch lockd strategies or change cookie
1017191783Srmacklem		 * translation
1018191783Srmacklem		 */
1019191783Srmacklem		args.flags = (args.flags &
1020191783Srmacklem		    ~(NFSMNT_NFSV3 |
1021191783Srmacklem		      NFSMNT_NFSV4 |
1022191783Srmacklem		      NFSMNT_KERB |
1023191783Srmacklem		      NFSMNT_INTEGRITY |
1024191783Srmacklem		      NFSMNT_PRIVACY |
1025191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
1026191783Srmacklem		    (nmp->nm_flag &
1027191783Srmacklem			(NFSMNT_NFSV3 |
1028191783Srmacklem			 NFSMNT_NFSV4 |
1029191783Srmacklem			 NFSMNT_KERB |
1030191783Srmacklem			 NFSMNT_INTEGRITY |
1031191783Srmacklem			 NFSMNT_PRIVACY |
1032191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
1033214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
1034191783Srmacklem		goto out;
1035191783Srmacklem	}
1036191783Srmacklem
1037191783Srmacklem	/*
1038191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
1039191783Srmacklem	 * or no-connection mode for those protocols that support
1040191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
1041191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
1042191783Srmacklem	 * to receive replies from a different IP then the request was
1043191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
1044191783Srmacklem	 * not 0.
1045191783Srmacklem	 */
1046191783Srmacklem	if (nfs_ip_paranoia == 0)
1047191783Srmacklem		args.flags |= NFSMNT_NOCONN;
1048192585Srmacklem
1049221190Srmacklem	if (has_nfs_args_opt != 0) {
1050221190Srmacklem		/*
1051221190Srmacklem		 * In the 'nfs_args' case, the pointers in the args
1052221190Srmacklem		 * structure are in userland - we copy them in here.
1053221190Srmacklem		 */
1054221190Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) {
1055192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
1056191783Srmacklem			error = EINVAL;
1057191783Srmacklem			goto out;
1058191783Srmacklem		}
1059221190Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1060221190Srmacklem		    args.fhsize);
1061221190Srmacklem		if (error != 0)
1062221190Srmacklem			goto out;
1063221205Srmacklem		error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen);
1064221190Srmacklem		if (error != 0)
1065221190Srmacklem			goto out;
1066221205Srmacklem		bzero(&hst[hstlen], MNAMELEN - hstlen);
1067221190Srmacklem		args.hostname = hst;
1068221190Srmacklem		/* sockargs() call must be after above copyin() calls */
1069221190Srmacklem		error = getsockaddr(&nam, (caddr_t)args.addr,
1070221190Srmacklem		    args.addrlen);
1071221190Srmacklem		if (error != 0)
1072221190Srmacklem			goto out;
1073191783Srmacklem	} else {
1074221190Srmacklem		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
1075221190Srmacklem		    &args.fhsize) == 0) {
1076221190Srmacklem			if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
1077221190Srmacklem				vfs_mount_error(mp, "Bad file handle");
1078221190Srmacklem				error = EINVAL;
1079221190Srmacklem				goto out;
1080221190Srmacklem			}
1081221190Srmacklem			bcopy(args.fh, nfh, args.fhsize);
1082221190Srmacklem		} else {
1083221190Srmacklem			args.fhsize = 0;
1084221190Srmacklem		}
1085221190Srmacklem		(void) vfs_getopt(mp->mnt_optnew, "hostname",
1086221190Srmacklem		    (void **)&args.hostname, &len);
1087221190Srmacklem		if (args.hostname == NULL) {
1088221190Srmacklem			vfs_mount_error(mp, "Invalid hostname");
1089221190Srmacklem			error = EINVAL;
1090221190Srmacklem			goto out;
1091221190Srmacklem		}
1092221190Srmacklem		bcopy(args.hostname, hst, MNAMELEN);
1093221190Srmacklem		hst[MNAMELEN - 1] = '\0';
1094192585Srmacklem	}
1095192585Srmacklem
1096192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1097192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1098192585Srmacklem	else
1099192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1100221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1101192585Srmacklem
1102192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1103192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1104192585Srmacklem	else
1105191783Srmacklem		krbname[0] = '\0';
1106221014Srmacklem	krbnamelen = strlen(krbname);
1107192585Srmacklem
1108192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1109192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1110192585Srmacklem	else
1111191783Srmacklem		dirpath[0] = '\0';
1112221014Srmacklem	dirlen = strlen(dirpath);
1113192585Srmacklem
1114222075Srmacklem	if (has_nfs_args_opt == 0) {
1115222075Srmacklem		if (vfs_getopt(mp->mnt_optnew, "addr",
1116222075Srmacklem		    (void **)&args.addr, &args.addrlen) == 0) {
1117222075Srmacklem			if (args.addrlen > SOCK_MAXADDRLEN) {
1118222075Srmacklem				error = ENAMETOOLONG;
1119222075Srmacklem				goto out;
1120222075Srmacklem			}
1121222075Srmacklem			nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1122222075Srmacklem			bcopy(args.addr, nam, args.addrlen);
1123222075Srmacklem			nam->sa_len = args.addrlen;
1124222075Srmacklem		} else {
1125222075Srmacklem			vfs_mount_error(mp, "No server address");
1126222075Srmacklem			error = EINVAL;
1127191783Srmacklem			goto out;
1128191783Srmacklem		}
1129191783Srmacklem	}
1130192585Srmacklem
1131191783Srmacklem	args.fh = nfh;
1132221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1133221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1134233326Sjhb	    nametimeo, negnametimeo);
1135191783Srmacklemout:
1136191783Srmacklem	if (!error) {
1137191783Srmacklem		MNT_ILOCK(mp);
1138239852Skib		mp->mnt_kern_flag |= MNTK_MPSAFE | MNTK_LOOKUP_SHARED |
1139239852Skib		    MNTK_NO_IOPF;
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;
1412250258Srmacklem	int error, flags = 0, i, 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);
1447250258Srmacklem	/* Make sure no nfsiods are assigned to this mount. */
1448250258Srmacklem	mtx_lock(&ncl_iod_mutex);
1449250258Srmacklem	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
1450250258Srmacklem		if (ncl_iodmount[i] == nmp) {
1451250258Srmacklem			ncl_iodwant[i] = NFSIOD_AVAILABLE;
1452250258Srmacklem			ncl_iodmount[i] = NULL;
1453250258Srmacklem		}
1454250258Srmacklem	mtx_unlock(&ncl_iod_mutex);
1455191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1456191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1457191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1458191783Srmacklem
1459191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1460191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1461191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1462191783Srmacklemout:
1463191783Srmacklem	return (error);
1464191783Srmacklem}
1465191783Srmacklem
1466191783Srmacklem/*
1467191783Srmacklem * Return root of a filesystem
1468191783Srmacklem */
1469191783Srmacklemstatic int
1470191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1471191783Srmacklem{
1472191783Srmacklem	struct vnode *vp;
1473191783Srmacklem	struct nfsmount *nmp;
1474191783Srmacklem	struct nfsnode *np;
1475191783Srmacklem	int error;
1476191783Srmacklem
1477191783Srmacklem	nmp = VFSTONFS(mp);
1478220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1479191783Srmacklem	if (error)
1480191783Srmacklem		return error;
1481191783Srmacklem	vp = NFSTOV(np);
1482191783Srmacklem	/*
1483191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1484191783Srmacklem	 */
1485191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1486191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1487191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1488191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1489191783Srmacklem	} else
1490191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1491191783Srmacklem	if (vp->v_type == VNON)
1492191783Srmacklem	    vp->v_type = VDIR;
1493191783Srmacklem	vp->v_vflag |= VV_ROOT;
1494191783Srmacklem	*vpp = vp;
1495191783Srmacklem	return (0);
1496191783Srmacklem}
1497191783Srmacklem
1498191783Srmacklem/*
1499191783Srmacklem * Flush out the buffer cache
1500191783Srmacklem */
1501191783Srmacklem/* ARGSUSED */
1502191783Srmacklemstatic int
1503191990Sattilionfs_sync(struct mount *mp, int waitfor)
1504191783Srmacklem{
1505191783Srmacklem	struct vnode *vp, *mvp;
1506191990Sattilio	struct thread *td;
1507191783Srmacklem	int error, allerror = 0;
1508191783Srmacklem
1509191990Sattilio	td = curthread;
1510191990Sattilio
1511222329Srmacklem	MNT_ILOCK(mp);
1512191783Srmacklem	/*
1513222329Srmacklem	 * If a forced dismount is in progress, return from here so that
1514222329Srmacklem	 * the umount(2) syscall doesn't get stuck in VFS_SYNC() before
1515222329Srmacklem	 * calling VFS_UNMOUNT().
1516222329Srmacklem	 */
1517222329Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1518222329Srmacklem		MNT_IUNLOCK(mp);
1519222329Srmacklem		return (EBADF);
1520222329Srmacklem	}
1521235626Smckusick	MNT_IUNLOCK(mp);
1522222329Srmacklem
1523222329Srmacklem	/*
1524191783Srmacklem	 * Force stale buffer cache information to be flushed.
1525191783Srmacklem	 */
1526191783Srmacklemloop:
1527235626Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
1528191783Srmacklem		/* XXX Racy bv_cnt check. */
1529224083Szack		if (NFSVOPISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1530191783Srmacklem		    waitfor == MNT_LAZY) {
1531191783Srmacklem			VI_UNLOCK(vp);
1532191783Srmacklem			continue;
1533191783Srmacklem		}
1534191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1535235626Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
1536191783Srmacklem			goto loop;
1537191783Srmacklem		}
1538191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1539191783Srmacklem		if (error)
1540191783Srmacklem			allerror = error;
1541224082Szack		NFSVOPUNLOCK(vp, 0);
1542191783Srmacklem		vrele(vp);
1543191783Srmacklem	}
1544191783Srmacklem	return (allerror);
1545191783Srmacklem}
1546191783Srmacklem
1547191783Srmacklemstatic int
1548191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1549191783Srmacklem{
1550191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1551191783Srmacklem	struct vfsquery vq;
1552191783Srmacklem	int error;
1553191783Srmacklem
1554191783Srmacklem	bzero(&vq, sizeof(vq));
1555191783Srmacklem	switch (op) {
1556191783Srmacklem#if 0
1557191783Srmacklem	case VFS_CTL_NOLOCKS:
1558191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1559191783Srmacklem 		if (req->oldptr != NULL) {
1560191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1561191783Srmacklem 			if (error)
1562191783Srmacklem 				return (error);
1563191783Srmacklem 		}
1564191783Srmacklem 		if (req->newptr != NULL) {
1565191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1566191783Srmacklem 			if (error)
1567191783Srmacklem 				return (error);
1568191783Srmacklem			if (val)
1569191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1570191783Srmacklem			else
1571191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1572191783Srmacklem 		}
1573191783Srmacklem		break;
1574191783Srmacklem#endif
1575191783Srmacklem	case VFS_CTL_QUERY:
1576191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1577191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1578191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1579191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1580191783Srmacklem#if 0
1581191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1582191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1583191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1584191783Srmacklem#endif
1585191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1586191783Srmacklem		break;
1587191783Srmacklem 	case VFS_CTL_TIMEO:
1588191783Srmacklem 		if (req->oldptr != NULL) {
1589191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1590191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1591191783Srmacklem 			if (error)
1592191783Srmacklem 				return (error);
1593191783Srmacklem 		}
1594191783Srmacklem 		if (req->newptr != NULL) {
1595191783Srmacklem			error = vfs_suser(mp, req->td);
1596191783Srmacklem			if (error)
1597191783Srmacklem				return (error);
1598191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1599191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1600191783Srmacklem 			if (error)
1601191783Srmacklem 				return (error);
1602191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1603191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1604191783Srmacklem 		}
1605191783Srmacklem		break;
1606191783Srmacklem	default:
1607191783Srmacklem		return (ENOTSUP);
1608191783Srmacklem	}
1609191783Srmacklem	return (0);
1610191783Srmacklem}
1611191783Srmacklem
1612214048Srmacklem/*
1613214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1614214048Srmacklem */
1615214048Srmacklemstatic void
1616214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1617216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1618216931Srmacklem    struct timeval *timeop)
1619214048Srmacklem{
1620214048Srmacklem	struct nfsmount *nmp;
1621214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1622214048Srmacklem
1623214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1624214048Srmacklem	if (fhlenp != NULL)
1625214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1626214048Srmacklem	if (fhp != NULL)
1627214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1628214048Srmacklem	if (sp != NULL)
1629214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1630214048Srmacklem	if (is_v3p != NULL)
1631214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1632214048Srmacklem	if (sizep != NULL)
1633214048Srmacklem		*sizep = np->n_size;
1634216931Srmacklem	if (timeop != NULL) {
1635216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1636216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1637216931Srmacklem	}
1638214048Srmacklem}
1639214048Srmacklem
1640244289Srmacklem/*
1641244289Srmacklem * This function prints out an option name, based on the conditional
1642244289Srmacklem * argument.
1643244289Srmacklem */
1644244289Srmacklemstatic __inline void nfscl_printopt(struct nfsmount *nmp, int testval,
1645244289Srmacklem    char *opt, char **buf, size_t *blen)
1646244289Srmacklem{
1647244289Srmacklem	int len;
1648244289Srmacklem
1649244289Srmacklem	if (testval != 0 && *blen > strlen(opt)) {
1650244289Srmacklem		len = snprintf(*buf, *blen, "%s", opt);
1651244289Srmacklem		if (len != strlen(opt))
1652244289Srmacklem			printf("EEK!!\n");
1653244289Srmacklem		*buf += len;
1654244289Srmacklem		*blen -= len;
1655244289Srmacklem	}
1656244289Srmacklem}
1657244289Srmacklem
1658244289Srmacklem/*
1659244289Srmacklem * This function printf out an options integer value.
1660244289Srmacklem */
1661244289Srmacklemstatic __inline void nfscl_printoptval(struct nfsmount *nmp, int optval,
1662244289Srmacklem    char *opt, char **buf, size_t *blen)
1663244289Srmacklem{
1664244289Srmacklem	int len;
1665244289Srmacklem
1666244289Srmacklem	if (*blen > strlen(opt) + 1) {
1667244289Srmacklem		/* Could result in truncated output string. */
1668244289Srmacklem		len = snprintf(*buf, *blen, "%s=%d", opt, optval);
1669244289Srmacklem		if (len < *blen) {
1670244289Srmacklem			*buf += len;
1671244289Srmacklem			*blen -= len;
1672244289Srmacklem		}
1673244289Srmacklem	}
1674244289Srmacklem}
1675244289Srmacklem
1676244289Srmacklem/*
1677244289Srmacklem * Load the option flags and values into the buffer.
1678244289Srmacklem */
1679244289Srmacklemvoid nfscl_retopts(struct nfsmount *nmp, char *buffer, size_t buflen)
1680244289Srmacklem{
1681244289Srmacklem	char *buf;
1682244289Srmacklem	size_t blen;
1683244289Srmacklem
1684244289Srmacklem	buf = buffer;
1685244289Srmacklem	blen = buflen;
1686244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV4) != 0, "nfsv4", &buf,
1687244289Srmacklem	    &blen);
1688244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV3) != 0, "nfsv3", &buf,
1689244289Srmacklem	    &blen);
1690244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0,
1691244289Srmacklem	    "nfsv2", &buf, &blen);
1692244289Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype == SOCK_STREAM, ",tcp", &buf, &blen);
1693244289Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype != SOCK_STREAM, ",udp", &buf, &blen);
1694244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RESVPORT) != 0, ",resvport",
1695244289Srmacklem	    &buf, &blen);
1696244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCONN) != 0, ",noconn",
1697244289Srmacklem	    &buf, &blen);
1698244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) == 0, ",hard", &buf,
1699244289Srmacklem	    &blen);
1700244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) != 0, ",soft", &buf,
1701244289Srmacklem	    &blen);
1702244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_INT) != 0, ",intr", &buf,
1703244289Srmacklem	    &blen);
1704244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) == 0, ",cto", &buf,
1705244289Srmacklem	    &blen);
1706244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) != 0, ",nocto", &buf,
1707244289Srmacklem	    &blen);
1708260170Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NONCONTIGWR) != 0,
1709260170Srmacklem	    ",noncontigwr", &buf, &blen);
1710244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1711244289Srmacklem	    0, ",lockd", &buf, &blen);
1712244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1713244289Srmacklem	    NFSMNT_NOLOCKD, ",nolockd", &buf, &blen);
1714244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RDIRPLUS) != 0, ",rdirplus",
1715244289Srmacklem	    &buf, &blen);
1716244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_KERB) == 0, ",sec=sys",
1717244289Srmacklem	    &buf, &blen);
1718244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1719244289Srmacklem	    NFSMNT_PRIVACY)) == NFSMNT_KERB, ",sec=krb5", &buf, &blen);
1720244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1721244289Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_INTEGRITY), ",sec=krb5i",
1722244289Srmacklem	    &buf, &blen);
1723244289Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1724244289Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_PRIVACY), ",sec=krb5p",
1725244289Srmacklem	    &buf, &blen);
1726244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmin, ",acdirmin", &buf, &blen);
1727244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmax, ",acdirmax", &buf, &blen);
1728244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmin, ",acregmin", &buf, &blen);
1729244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmax, ",acregmax", &buf, &blen);
1730244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_nametimeo, ",nametimeo", &buf, &blen);
1731244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_negnametimeo, ",negnametimeo", &buf,
1732244289Srmacklem	    &blen);
1733244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_rsize, ",rsize", &buf, &blen);
1734244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_wsize, ",wsize", &buf, &blen);
1735244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_readdirsize, ",readdirsize", &buf,
1736244289Srmacklem	    &blen);
1737244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_readahead, ",readahead", &buf, &blen);
1738244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_wcommitsize, ",wcommitsize", &buf,
1739244289Srmacklem	    &blen);
1740244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_timeo, ",timeout", &buf, &blen);
1741244289Srmacklem	nfscl_printoptval(nmp, nmp->nm_retry, ",retrans", &buf, &blen);
1742244289Srmacklem}
1743244289Srmacklem
1744