nfs_clvfsops.c revision 220732
1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993, 1995
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem *	from nfs_vfsops.c	8.12 (Berkeley) 5/20/95
33191783Srmacklem */
34191783Srmacklem
35191783Srmacklem#include <sys/cdefs.h>
36191783Srmacklem__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clvfsops.c 220732 2011-04-16 23:20:21Z rmacklem $");
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>
49191783Srmacklem#include <sys/lock.h>
50191783Srmacklem#include <sys/malloc.h>
51191783Srmacklem#include <sys/mbuf.h>
52191783Srmacklem#include <sys/module.h>
53191783Srmacklem#include <sys/mount.h>
54191783Srmacklem#include <sys/proc.h>
55191783Srmacklem#include <sys/socket.h>
56191783Srmacklem#include <sys/socketvar.h>
57191783Srmacklem#include <sys/sockio.h>
58191783Srmacklem#include <sys/sysctl.h>
59191783Srmacklem#include <sys/vnode.h>
60191783Srmacklem#include <sys/signalvar.h>
61191783Srmacklem
62191783Srmacklem#include <vm/vm.h>
63191783Srmacklem#include <vm/vm_extern.h>
64191783Srmacklem#include <vm/uma.h>
65191783Srmacklem
66191783Srmacklem#include <net/if.h>
67191783Srmacklem#include <net/route.h>
68191783Srmacklem#include <netinet/in.h>
69191783Srmacklem
70191783Srmacklem#include <fs/nfs/nfsport.h>
71191783Srmacklem#include <fs/nfsclient/nfsnode.h>
72191783Srmacklem#include <fs/nfsclient/nfsmount.h>
73191783Srmacklem#include <fs/nfsclient/nfs.h>
74191783Srmacklem#include <fs/nfsclient/nfsdiskless.h>
75191783Srmacklem
76219028SnetchildFEATURE(nfscl, "NFSv4 client");
77219028Snetchild
78191783Srmacklemextern int nfscl_ticks;
79191783Srmacklemextern struct timeval nfsboottime;
80191783Srmacklemextern struct nfsstats	newnfsstats;
81191783Srmacklem
82191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
83191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
84191783Srmacklem
85191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
86191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW,
87191783Srmacklem	&newnfsstats, nfsstats, "S,nfsstats");
88191783Srmacklemstatic int nfs_ip_paranoia = 1;
89191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
90191783Srmacklem    &nfs_ip_paranoia, 0, "");
91191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
92191783SrmacklemSYSCTL_INT(_vfs_newnfs, 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;
96191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY,
97191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
98191783Srmacklem
99192585Srmacklemstatic void	nfs_sec_name(char *, int *);
100191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
101214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
102214048Srmacklem		    struct thread *);
103191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
104191783Srmacklem		    struct sockaddr *, char *, u_char *, u_char *, u_char *,
105203303Srmacklem		    struct vnode **, struct ucred *, struct thread *, int);
106214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
107216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
108216931Srmacklem		    struct timeval *);
109191783Srmacklemstatic vfs_mount_t nfs_mount;
110191783Srmacklemstatic vfs_cmount_t nfs_cmount;
111191783Srmacklemstatic vfs_unmount_t nfs_unmount;
112191783Srmacklemstatic vfs_root_t nfs_root;
113191783Srmacklemstatic vfs_statfs_t nfs_statfs;
114191783Srmacklemstatic vfs_sync_t nfs_sync;
115191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
116191783Srmacklem
117191783Srmacklem/*
118191783Srmacklem * nfs vfs operations.
119191783Srmacklem */
120191783Srmacklemstatic struct vfsops nfs_vfsops = {
121191783Srmacklem	.vfs_init =		ncl_init,
122191783Srmacklem	.vfs_mount =		nfs_mount,
123191783Srmacklem	.vfs_cmount =		nfs_cmount,
124191783Srmacklem	.vfs_root =		nfs_root,
125191783Srmacklem	.vfs_statfs =		nfs_statfs,
126191783Srmacklem	.vfs_sync =		nfs_sync,
127191783Srmacklem	.vfs_uninit =		ncl_uninit,
128191783Srmacklem	.vfs_unmount =		nfs_unmount,
129191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
130191783Srmacklem};
131191783SrmacklemVFS_SET(nfs_vfsops, newnfs, VFCF_NETWORK);
132191783Srmacklem
133191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
134191783SrmacklemMODULE_VERSION(newnfs, 1);
135191783Srmacklem
136191783Srmacklem/*
137191783Srmacklem * This structure must be filled in by a primary bootstrap or bootstrap
138191783Srmacklem * server for a diskless/dataless machine. It is initialized below just
139191783Srmacklem * to ensure that it is allocated to initialized data (.data not .bss).
140191783Srmacklem */
141191783Srmacklemstruct nfs_diskless newnfs_diskless = { { { 0 } } };
142191783Srmacklemstruct nfsv3_diskless newnfsv3_diskless = { { { 0 } } };
143191783Srmacklemint newnfs_diskless_valid = 0;
144191783Srmacklem
145191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
146192145Srmacklem    &newnfs_diskless_valid, 0,
147192145Srmacklem    "Has the diskless struct been filled correctly");
148191783Srmacklem
149191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
150192145Srmacklem    newnfsv3_diskless.root_hostnam, 0, "Path to nfs root");
151191783Srmacklem
152191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
153192145Srmacklem    &newnfsv3_diskless.root_saddr, sizeof newnfsv3_diskless.root_saddr,
154192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
155191783Srmacklem
156191783Srmacklem
157191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
158191783Srmacklemstatic int	nfs_mountdiskless(char *,
159191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
160191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
161191783Srmacklemstatic void	nfs_convert_diskless(void);
162191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
163191783Srmacklem		    struct onfs_args *oargs);
164191783Srmacklem
165191783Srmacklemint
166191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
167191783Srmacklem{
168191783Srmacklem	int iosize, maxio;
169191783Srmacklem
170191783Srmacklem	/* First, set the upper limit for iosize */
171191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
172191783Srmacklem		maxio = NFS_MAXBSIZE;
173191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
174191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
175191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
176191783Srmacklem		else
177191783Srmacklem			maxio = NFS_MAXBSIZE;
178191783Srmacklem	} else {
179191783Srmacklem		maxio = NFS_V2MAXDATA;
180191783Srmacklem	}
181191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
182191783Srmacklem		nmp->nm_rsize = maxio;
183191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
184191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
185191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
186191783Srmacklem		nmp->nm_readdirsize = maxio;
187191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
188191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
189191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
190191783Srmacklem		nmp->nm_wsize = maxio;
191191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
192191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
193191783Srmacklem
194191783Srmacklem	/*
195191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
196191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
197191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
198191783Srmacklem	 * space.
199191783Srmacklem	 */
200191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
201191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
202191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
203191783Srmacklem	return (iosize);
204191783Srmacklem}
205191783Srmacklem
206191783Srmacklemstatic void
207191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
208191783Srmacklem{
209191783Srmacklem
210191783Srmacklem	args->version = NFS_ARGSVERSION;
211191783Srmacklem	args->addr = oargs->addr;
212191783Srmacklem	args->addrlen = oargs->addrlen;
213191783Srmacklem	args->sotype = oargs->sotype;
214191783Srmacklem	args->proto = oargs->proto;
215191783Srmacklem	args->fh = oargs->fh;
216191783Srmacklem	args->fhsize = oargs->fhsize;
217191783Srmacklem	args->flags = oargs->flags;
218191783Srmacklem	args->wsize = oargs->wsize;
219191783Srmacklem	args->rsize = oargs->rsize;
220191783Srmacklem	args->readdirsize = oargs->readdirsize;
221191783Srmacklem	args->timeo = oargs->timeo;
222191783Srmacklem	args->retrans = oargs->retrans;
223191783Srmacklem	args->readahead = oargs->readahead;
224191783Srmacklem	args->hostname = oargs->hostname;
225191783Srmacklem}
226191783Srmacklem
227191783Srmacklemstatic void
228191783Srmacklemnfs_convert_diskless(void)
229191783Srmacklem{
230191783Srmacklem
231191783Srmacklem	bcopy(&newnfs_diskless.myif, &newnfsv3_diskless.myif,
232192145Srmacklem	    sizeof (struct ifaliasreq));
233191783Srmacklem	bcopy(&newnfs_diskless.mygateway, &newnfsv3_diskless.mygateway,
234192145Srmacklem	    sizeof (struct sockaddr_in));
235192145Srmacklem	nfs_convert_oargs(&newnfsv3_diskless.root_args,
236192145Srmacklem	    &newnfs_diskless.root_args);
237191783Srmacklem	if (newnfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
238191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_MYFH;
239192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
240192145Srmacklem		    NFSX_MYFH);
241191783Srmacklem	} else {
242191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_V2FH;
243192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
244192145Srmacklem		    NFSX_V2FH);
245191783Srmacklem	}
246191783Srmacklem	bcopy(&newnfs_diskless.root_saddr,&newnfsv3_diskless.root_saddr,
247192145Srmacklem	    sizeof(struct sockaddr_in));
248192145Srmacklem	bcopy(newnfs_diskless.root_hostnam, newnfsv3_diskless.root_hostnam,
249192145Srmacklem	    MNAMELEN);
250191783Srmacklem	newnfsv3_diskless.root_time = newnfs_diskless.root_time;
251191783Srmacklem	bcopy(newnfs_diskless.my_hostnam, newnfsv3_diskless.my_hostnam,
252192145Srmacklem	    MAXHOSTNAMELEN);
253191783Srmacklem	newnfs_diskless_valid = 3;
254191783Srmacklem}
255191783Srmacklem
256191783Srmacklem/*
257191783Srmacklem * nfs statfs call
258191783Srmacklem */
259191783Srmacklemstatic int
260191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
261191783Srmacklem{
262191783Srmacklem	struct vnode *vp;
263191990Sattilio	struct thread *td;
264191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
265191783Srmacklem	struct nfsvattr nfsva;
266191783Srmacklem	struct nfsfsinfo fs;
267191783Srmacklem	struct nfsstatfs sb;
268191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
269191783Srmacklem	struct nfsnode *np;
270191783Srmacklem
271191990Sattilio	td = curthread;
272191990Sattilio
273191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
274191783Srmacklem	if (error)
275191783Srmacklem		return (error);
276220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
277191783Srmacklem	if (error) {
278191783Srmacklem		vfs_unbusy(mp);
279191783Srmacklem		return (error);
280191783Srmacklem	}
281191783Srmacklem	vp = NFSTOV(np);
282191783Srmacklem	mtx_lock(&nmp->nm_mtx);
283191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
284191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
285191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
286191783Srmacklem		    &attrflag, NULL);
287191783Srmacklem		if (!error)
288191783Srmacklem			gotfsinfo = 1;
289191783Srmacklem	} else
290191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
291191783Srmacklem	if (!error)
292191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
293191783Srmacklem		    &attrflag, NULL);
294191783Srmacklem	if (attrflag == 0) {
295191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
296191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
297191783Srmacklem		if (ret) {
298191783Srmacklem			/*
299191783Srmacklem			 * Just set default values to get things going.
300191783Srmacklem			 */
301191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
302191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
303191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
304191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
305191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
306191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
307191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
308191783Srmacklem			nfsva.na_vattr.va_gen = 1;
309191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
310191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
311191783Srmacklem		}
312191783Srmacklem	}
313191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
314191783Srmacklem	if (!error) {
315191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
316191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
317191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
318191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
319191783Srmacklem	    sbp->f_flags = nmp->nm_flag;
320191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
321191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
322191783Srmacklem	    if (sbp != &mp->mnt_stat) {
323191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
324191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
325191783Srmacklem	    }
326191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
327191783Srmacklem	} else if (NFS_ISV4(vp)) {
328191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
329191783Srmacklem	}
330191783Srmacklem	vput(vp);
331191783Srmacklem	vfs_unbusy(mp);
332191783Srmacklem	return (error);
333191783Srmacklem}
334191783Srmacklem
335191783Srmacklem/*
336191783Srmacklem * nfs version 3 fsinfo rpc call
337191783Srmacklem */
338191783Srmacklemint
339191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
340191783Srmacklem    struct thread *td)
341191783Srmacklem{
342191783Srmacklem	struct nfsfsinfo fs;
343191783Srmacklem	struct nfsvattr nfsva;
344191783Srmacklem	int error, attrflag;
345191783Srmacklem
346191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
347191783Srmacklem	if (!error) {
348191783Srmacklem		if (attrflag)
349191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
350191783Srmacklem			    1);
351191783Srmacklem		mtx_lock(&nmp->nm_mtx);
352191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
353191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
354191783Srmacklem	}
355191783Srmacklem	return (error);
356191783Srmacklem}
357191783Srmacklem
358191783Srmacklem/*
359191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
360191783Srmacklem * newnfs_diskless structure that has been filled in properly by some primary
361191783Srmacklem * bootstrap.
362191783Srmacklem * It goes something like this:
363191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
364191783Srmacklem *   can talk to the server
365191783Srmacklem * - If newnfs_diskless.mygateway is filled in, use that address as
366191783Srmacklem *   a default gateway.
367191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
368191783Srmacklem *
369191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
370191783Srmacklem * structure, as well as other global NFS client variables here, as
371192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
372191783Srmacklem * client activity occurs.
373191783Srmacklem */
374191783Srmacklemint
375192145Srmacklemncl_mountroot(struct mount *mp)
376191783Srmacklem{
377192145Srmacklem	struct thread *td = curthread;
378191783Srmacklem	struct nfsv3_diskless *nd = &newnfsv3_diskless;
379191783Srmacklem	struct socket *so;
380191783Srmacklem	struct vnode *vp;
381191783Srmacklem	struct ifreq ir;
382193066Sjamie	int error;
383191783Srmacklem	u_long l;
384191783Srmacklem	char buf[128];
385191783Srmacklem	char *cp;
386191783Srmacklem
387191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
388192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
389191783Srmacklem#elif defined(NFS_ROOT)
390191783Srmacklem	nfs_setup_diskless();
391191783Srmacklem#endif
392191783Srmacklem
393191783Srmacklem	if (newnfs_diskless_valid == 0)
394191783Srmacklem		return (-1);
395191783Srmacklem	if (newnfs_diskless_valid == 1)
396191783Srmacklem		nfs_convert_diskless();
397191783Srmacklem
398191783Srmacklem	/*
399191783Srmacklem	 * XXX splnet, so networks will receive...
400191783Srmacklem	 */
401191783Srmacklem	splnet();
402191783Srmacklem
403191783Srmacklem	/*
404191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
405191783Srmacklem	 * talk to the server.
406191783Srmacklem	 */
407191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
408191783Srmacklem	    td->td_ucred, td);
409191783Srmacklem	if (error)
410192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
411191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
412191783Srmacklem
413191783Srmacklem#if 0 /* XXX Bad idea */
414191783Srmacklem	/*
415191783Srmacklem	 * We might not have been told the right interface, so we pass
416191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
417191783Srmacklem	 * one of them configured.
418191783Srmacklem	 */
419191783Srmacklem
420191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
421191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
422191783Srmacklem		nd->myif.ifra_name[i] <= '9';
423191783Srmacklem		nd->myif.ifra_name[i] ++) {
424191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
425191783Srmacklem		if(!error)
426191783Srmacklem			break;
427191783Srmacklem	}
428191783Srmacklem#endif
429191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
430191783Srmacklem	if (error)
431192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
432191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
433191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
434191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
435191783Srmacklem		freeenv(cp);
436191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
437191783Srmacklem		if (error)
438192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
439191783Srmacklem	}
440191783Srmacklem	soclose(so);
441191783Srmacklem
442191783Srmacklem	/*
443191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
444191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
445191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
446191783Srmacklem	 * to avoid panicking inappropriately in that situation.
447191783Srmacklem	 */
448191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
449191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
450191783Srmacklem		struct sockaddr_in mask, sin;
451191783Srmacklem
452191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
453191783Srmacklem		sin = mask;
454191783Srmacklem		sin.sin_family = AF_INET;
455191783Srmacklem		sin.sin_len = sizeof(sin);
456192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
457218757Sbz		CURVNET_SET(TD_TO_VNET(td));
458191783Srmacklem		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
459191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
460191783Srmacklem		    (struct sockaddr *)&mask,
461191783Srmacklem		    RTF_UP | RTF_GATEWAY, NULL);
462218757Sbz		CURVNET_RESTORE();
463191783Srmacklem		if (error)
464192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
465191783Srmacklem	}
466191783Srmacklem
467191783Srmacklem	/*
468191783Srmacklem	 * Create the rootfs mount point.
469191783Srmacklem	 */
470191783Srmacklem	nd->root_args.fh = nd->root_fh;
471191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
472191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
473191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
474191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
475191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
476191783Srmacklem	printf("NFS ROOT: %s\n", buf);
477192145Srmacklem	nd->root_args.hostname = buf;
478191783Srmacklem	if ((error = nfs_mountdiskless(buf,
479191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
480191783Srmacklem		return (error);
481191783Srmacklem	}
482191783Srmacklem
483191783Srmacklem	/*
484191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
485191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
486191783Srmacklem	 * mount the right /var based upon its preset value.
487191783Srmacklem	 */
488193066Sjamie	mtx_lock(&prison0.pr_mtx);
489194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
490194118Sjamie	    sizeof(prison0.pr_hostname));
491193066Sjamie	mtx_unlock(&prison0.pr_mtx);
492191783Srmacklem	inittodr(ntohl(nd->root_time));
493191783Srmacklem	return (0);
494191783Srmacklem}
495191783Srmacklem
496191783Srmacklem/*
497191783Srmacklem * Internal version of mount system call for diskless setup.
498191783Srmacklem */
499191783Srmacklemstatic int
500191783Srmacklemnfs_mountdiskless(char *path,
501191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
502191783Srmacklem    struct vnode **vpp, struct mount *mp)
503191783Srmacklem{
504191783Srmacklem	struct sockaddr *nam;
505191783Srmacklem	int error;
506191783Srmacklem
507191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
508191783Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, NULL, NULL, vpp,
509203303Srmacklem	    td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
510192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
511191783Srmacklem		return (error);
512191783Srmacklem	}
513191783Srmacklem	return (0);
514191783Srmacklem}
515191783Srmacklem
516191783Srmacklemstatic void
517192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
518192585Srmacklem{
519192585Srmacklem	if (!strcmp(sec, "krb5"))
520192585Srmacklem		*flagsp |= NFSMNT_KERB;
521192585Srmacklem	else if (!strcmp(sec, "krb5i"))
522192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
523192585Srmacklem	else if (!strcmp(sec, "krb5p"))
524192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
525192585Srmacklem}
526192585Srmacklem
527192585Srmacklemstatic void
528191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
529214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
530191783Srmacklem{
531191783Srmacklem	int s;
532191783Srmacklem	int adjsock;
533214048Srmacklem	char *p;
534191783Srmacklem
535191783Srmacklem	s = splnet();
536191783Srmacklem
537191783Srmacklem	/*
538191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
539191783Srmacklem	 * an update.  If this is not an update, then either the read-only
540191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
541191783Srmacklem	 * intentionally at some previous point.
542191783Srmacklem	 */
543191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
544191783Srmacklem		MNT_ILOCK(mp);
545191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
546191783Srmacklem		MNT_IUNLOCK(mp);
547191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
548191783Srmacklem		MNT_ILOCK(mp);
549191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
550191783Srmacklem		MNT_IUNLOCK(mp);
551191783Srmacklem	}
552191783Srmacklem
553191783Srmacklem	/*
554191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
555191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
556191783Srmacklem	 * and soft timeout behavior.
557191783Srmacklem	 */
558191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
559191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
560191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
561191783Srmacklem	}
562191783Srmacklem
563191783Srmacklem	/* Also clear RDIRPLUS if not NFSv3, it crashes some servers */
564191783Srmacklem	if ((argp->flags & NFSMNT_NFSV3) == 0)
565191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
566191783Srmacklem
567191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
568191783Srmacklem	adjsock = ((nmp->nm_flag & NFSMNT_NOCONN) !=
569191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
570191783Srmacklem
571191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
572191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
573191783Srmacklem	splx(s);
574191783Srmacklem
575191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
576191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
577191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
578191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
579191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
580191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
581191783Srmacklem	}
582191783Srmacklem
583191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
584191783Srmacklem		nmp->nm_retry = argp->retrans;
585191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
586191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
587191783Srmacklem	}
588191783Srmacklem
589191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
590191783Srmacklem		nmp->nm_wsize = argp->wsize;
591191783Srmacklem		/* Round down to multiple of blocksize */
592191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
593191783Srmacklem		if (nmp->nm_wsize <= 0)
594191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
595191783Srmacklem	}
596191783Srmacklem
597191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
598191783Srmacklem		nmp->nm_rsize = argp->rsize;
599191783Srmacklem		/* Round down to multiple of blocksize */
600191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
601191783Srmacklem		if (nmp->nm_rsize <= 0)
602191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
603191783Srmacklem	}
604191783Srmacklem
605191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
606191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
607191783Srmacklem	}
608191783Srmacklem
609191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
610191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
611191783Srmacklem	else
612191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
613191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
614191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
615191783Srmacklem	else
616191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
617191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
618191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
619191783Srmacklem	else
620191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
621191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
622191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
623191783Srmacklem	else
624191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
625191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
626191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
627191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
628191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
629191783Srmacklem
630191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
631191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
632191783Srmacklem			nmp->nm_readahead = argp->readahead;
633191783Srmacklem		else
634191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
635191783Srmacklem	}
636191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
637191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
638191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
639191783Srmacklem		else
640191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
641191783Srmacklem	}
642191783Srmacklem
643191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
644191783Srmacklem		    (nmp->nm_soproto != argp->proto));
645191783Srmacklem
646191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
647191783Srmacklem		int haslock = 0, error = 0;
648191783Srmacklem
649191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
650191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
651191783Srmacklem			if (!error)
652191783Srmacklem				haslock = 1;
653191783Srmacklem		}
654191783Srmacklem		if (!error) {
655191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
656191783Srmacklem		    if (haslock)
657191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
658191783Srmacklem		    nmp->nm_sotype = argp->sotype;
659191783Srmacklem		    nmp->nm_soproto = argp->proto;
660191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
661191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
662191783Srmacklem			    cred, td, 0)) {
663191783Srmacklem				printf("newnfs_args: retrying connect\n");
664207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
665191783Srmacklem			}
666191783Srmacklem		}
667191783Srmacklem	} else {
668191783Srmacklem		nmp->nm_sotype = argp->sotype;
669191783Srmacklem		nmp->nm_soproto = argp->proto;
670191783Srmacklem	}
671214048Srmacklem
672214048Srmacklem	if (hostname != NULL) {
673214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
674214048Srmacklem		    sizeof(nmp->nm_hostname));
675214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
676214048Srmacklem		if (p != NULL)
677214048Srmacklem			*p = '\0';
678214048Srmacklem	}
679191783Srmacklem}
680191783Srmacklem
681192585Srmacklemstatic const char *nfs_opts[] = { "from",
682191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
683191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
684192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
685192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
686192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
687192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
688192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
689203303Srmacklem    "negnametimeo",
690191783Srmacklem    NULL };
691191783Srmacklem
692191783Srmacklem/*
693191783Srmacklem * VFS Operations.
694191783Srmacklem *
695191783Srmacklem * mount system call
696191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
697191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
698191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
699191783Srmacklem * an error after that means that I have to release the mbuf.
700191783Srmacklem */
701191783Srmacklem/* ARGSUSED */
702191783Srmacklemstatic int
703191990Sattilionfs_mount(struct mount *mp)
704191783Srmacklem{
705191783Srmacklem	struct nfs_args args = {
706191783Srmacklem	    .version = NFS_ARGSVERSION,
707191783Srmacklem	    .addr = NULL,
708191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
709191783Srmacklem	    .sotype = SOCK_STREAM,
710191783Srmacklem	    .proto = 0,
711191783Srmacklem	    .fh = NULL,
712191783Srmacklem	    .fhsize = 0,
713191783Srmacklem	    .flags = 0,
714191783Srmacklem	    .wsize = NFS_WSIZE,
715191783Srmacklem	    .rsize = NFS_RSIZE,
716191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
717191783Srmacklem	    .timeo = 10,
718191783Srmacklem	    .retrans = NFS_RETRANS,
719191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
720191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
721191783Srmacklem	    .hostname = NULL,
722191783Srmacklem	    /* args version 4 */
723191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
724191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
725191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
726191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
727191783Srmacklem	    .dirlen = 0,
728191783Srmacklem	    .krbnamelen = 0,
729192585Srmacklem	    .srvkrbnamelen = 0,
730191783Srmacklem	};
731192585Srmacklem	int error = 0, ret, len;
732192585Srmacklem	struct sockaddr *nam = NULL;
733191783Srmacklem	struct vnode *vp;
734191990Sattilio	struct thread *td;
735191783Srmacklem	char hst[MNAMELEN];
736191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
737192585Srmacklem	char *opt, *name, *secname;
738203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
739191783Srmacklem
740191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
741191783Srmacklem		error = EINVAL;
742191783Srmacklem		goto out;
743191783Srmacklem	}
744191783Srmacklem
745191990Sattilio	td = curthread;
746191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
747192145Srmacklem		error = ncl_mountroot(mp);
748191783Srmacklem		goto out;
749191783Srmacklem	}
750191783Srmacklem
751192585Srmacklem	nfscl_init();
752191783Srmacklem
753192585Srmacklem	/* Handle the new style options. */
754192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
755192585Srmacklem		args.flags |= NFSMNT_NOCONN;
756192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
757192585Srmacklem		args.flags |= NFSMNT_NOCONN;
758192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
759192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
760192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
761192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
762192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
763192585Srmacklem		args.flags |= NFSMNT_INT;
764192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
765192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
766192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
767192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
768192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
769192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
770192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
771192585Srmacklem		args.flags |= NFSMNT_SOFT;
772192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
773192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
774192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
775192585Srmacklem		args.sotype = SOCK_DGRAM;
776192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
777192585Srmacklem		args.sotype = SOCK_DGRAM;
778192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
779192585Srmacklem		args.sotype = SOCK_STREAM;
780192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
781192585Srmacklem		args.flags |= NFSMNT_NFSV3;
782192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
783192585Srmacklem		args.flags |= NFSMNT_NFSV4;
784192585Srmacklem		args.sotype = SOCK_STREAM;
785191783Srmacklem	}
786192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
787192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
788192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
789192585Srmacklem		if (opt == NULL) {
790192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
791192585Srmacklem			error = EINVAL;
792192585Srmacklem			goto out;
793192585Srmacklem		}
794192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
795192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
796192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
797192585Srmacklem			    opt);
798192585Srmacklem			error = EINVAL;
799192585Srmacklem			goto out;
800192585Srmacklem		}
801192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
802192585Srmacklem	}
803192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
804192585Srmacklem		if (opt == NULL) {
805192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
806192585Srmacklem			error = EINVAL;
807192585Srmacklem			goto out;
808192585Srmacklem		}
809192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
810192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
811192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
812192585Srmacklem			    opt);
813192585Srmacklem			error = EINVAL;
814192585Srmacklem			goto out;
815192585Srmacklem		}
816192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
817192585Srmacklem	}
818192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
819192585Srmacklem		if (opt == NULL) {
820192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
821192585Srmacklem			error = EINVAL;
822192585Srmacklem			goto out;
823192585Srmacklem		}
824192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
825192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
826192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
827192585Srmacklem			    opt);
828192585Srmacklem			error = EINVAL;
829192585Srmacklem			goto out;
830192585Srmacklem		}
831192585Srmacklem		args.flags |= NFSMNT_WSIZE;
832192585Srmacklem	}
833192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
834192585Srmacklem		if (opt == NULL) {
835192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
836192585Srmacklem			error = EINVAL;
837192585Srmacklem			goto out;
838192585Srmacklem		}
839192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
840192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
841192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
842192585Srmacklem			    opt);
843192585Srmacklem			error = EINVAL;
844192585Srmacklem			goto out;
845192585Srmacklem		}
846192585Srmacklem		args.flags |= NFSMNT_RSIZE;
847192585Srmacklem	}
848192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
849192585Srmacklem		if (opt == NULL) {
850192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
851192585Srmacklem			error = EINVAL;
852192585Srmacklem			goto out;
853192585Srmacklem		}
854192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
855192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
856192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
857192585Srmacklem			    opt);
858192585Srmacklem			error = EINVAL;
859192585Srmacklem			goto out;
860192585Srmacklem		}
861192585Srmacklem		args.flags |= NFSMNT_RETRANS;
862192585Srmacklem	}
863192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
864192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
865192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
866192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
867192585Srmacklem			    opt);
868192585Srmacklem			error = EINVAL;
869192585Srmacklem			goto out;
870192585Srmacklem		}
871192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
872192585Srmacklem	}
873192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
874192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
875192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
876192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
877192585Srmacklem			    opt);
878192585Srmacklem			error = EINVAL;
879192585Srmacklem			goto out;
880192585Srmacklem		}
881192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
882192585Srmacklem	}
883192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
884192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
885192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
886192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
887192585Srmacklem			    opt);
888192585Srmacklem			error = EINVAL;
889192585Srmacklem			goto out;
890192585Srmacklem		}
891192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
892192585Srmacklem	}
893192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
894192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
895192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
896192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
897192585Srmacklem			    opt);
898192585Srmacklem			error = EINVAL;
899192585Srmacklem			goto out;
900192585Srmacklem		}
901192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
902192585Srmacklem	}
903192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
904192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
905192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
906192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
907192585Srmacklem			    opt);
908192585Srmacklem			error = EINVAL;
909192585Srmacklem			goto out;
910192585Srmacklem		}
911192585Srmacklem		args.flags |= NFSMNT_TIMEO;
912192585Srmacklem	}
913203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
914203303Srmacklem	    == 0) {
915203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
916203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
917203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
918203303Srmacklem			    opt);
919203303Srmacklem			error = EINVAL;
920203303Srmacklem			goto out;
921203303Srmacklem		}
922203303Srmacklem	}
923192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
924192585Srmacklem		(void **) &secname, NULL) == 0)
925192585Srmacklem		nfs_sec_name(secname, &args.flags);
926191783Srmacklem
927191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
928191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
929191783Srmacklem
930191783Srmacklem		if (nmp == NULL) {
931191783Srmacklem			error = EIO;
932191783Srmacklem			goto out;
933191783Srmacklem		}
934191783Srmacklem		/*
935191783Srmacklem		 * When doing an update, we can't change version,
936191783Srmacklem		 * security, switch lockd strategies or change cookie
937191783Srmacklem		 * translation
938191783Srmacklem		 */
939191783Srmacklem		args.flags = (args.flags &
940191783Srmacklem		    ~(NFSMNT_NFSV3 |
941191783Srmacklem		      NFSMNT_NFSV4 |
942191783Srmacklem		      NFSMNT_KERB |
943191783Srmacklem		      NFSMNT_INTEGRITY |
944191783Srmacklem		      NFSMNT_PRIVACY |
945191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
946191783Srmacklem		    (nmp->nm_flag &
947191783Srmacklem			(NFSMNT_NFSV3 |
948191783Srmacklem			 NFSMNT_NFSV4 |
949191783Srmacklem			 NFSMNT_KERB |
950191783Srmacklem			 NFSMNT_INTEGRITY |
951191783Srmacklem			 NFSMNT_PRIVACY |
952191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
953214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
954191783Srmacklem		goto out;
955191783Srmacklem	}
956191783Srmacklem
957191783Srmacklem	/*
958191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
959191783Srmacklem	 * or no-connection mode for those protocols that support
960191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
961191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
962191783Srmacklem	 * to receive replies from a different IP then the request was
963191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
964191783Srmacklem	 * not 0.
965191783Srmacklem	 */
966191783Srmacklem	if (nfs_ip_paranoia == 0)
967191783Srmacklem		args.flags |= NFSMNT_NOCONN;
968192585Srmacklem
969192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
970192585Srmacklem	    &args.fhsize) == 0) {
971208234Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
972192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
973191783Srmacklem			error = EINVAL;
974191783Srmacklem			goto out;
975191783Srmacklem		}
976192585Srmacklem		bcopy(args.fh, nfh, args.fhsize);
977191783Srmacklem	} else {
978192585Srmacklem		args.fhsize = 0;
979192585Srmacklem	}
980192585Srmacklem
981192585Srmacklem	(void) vfs_getopt(mp->mnt_optnew, "hostname", (void **)&args.hostname,
982192585Srmacklem	    &len);
983192585Srmacklem	if (args.hostname == NULL) {
984192585Srmacklem		vfs_mount_error(mp, "Invalid hostname");
985192585Srmacklem		error = EINVAL;
986192585Srmacklem		goto out;
987192585Srmacklem	}
988192585Srmacklem	bcopy(args.hostname, hst, MNAMELEN);
989192585Srmacklem	hst[MNAMELEN - 1] = '\0';
990192585Srmacklem
991192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
992192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
993192585Srmacklem	else
994192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
995192585Srmacklem	args.srvkrbnamelen = strlen(srvkrbname);
996192585Srmacklem
997192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
998192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
999192585Srmacklem	else
1000191783Srmacklem		krbname[0] = '\0';
1001192585Srmacklem	args.krbnamelen = strlen(krbname);
1002192585Srmacklem
1003192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1004192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1005192585Srmacklem	else
1006191783Srmacklem		dirpath[0] = '\0';
1007192585Srmacklem	args.dirlen = strlen(dirpath);
1008192585Srmacklem
1009192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "addr", (void **)&args.addr,
1010192585Srmacklem	    &args.addrlen) == 0) {
1011192585Srmacklem		if (args.addrlen > SOCK_MAXADDRLEN) {
1012192585Srmacklem			error = ENAMETOOLONG;
1013191783Srmacklem			goto out;
1014191783Srmacklem		}
1015192585Srmacklem		nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1016192585Srmacklem		bcopy(args.addr, nam, args.addrlen);
1017192585Srmacklem		nam->sa_len = args.addrlen;
1018191783Srmacklem	}
1019192585Srmacklem
1020191783Srmacklem	args.fh = nfh;
1021191783Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, dirpath, srvkrbname,
1022203303Srmacklem	    &vp, td->td_ucred, td, negnametimeo);
1023191783Srmacklemout:
1024191783Srmacklem	if (!error) {
1025191783Srmacklem		MNT_ILOCK(mp);
1026191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
1027191783Srmacklem		MNT_IUNLOCK(mp);
1028191783Srmacklem	}
1029191783Srmacklem	return (error);
1030191783Srmacklem}
1031191783Srmacklem
1032191783Srmacklem
1033191783Srmacklem/*
1034191783Srmacklem * VFS Operations.
1035191783Srmacklem *
1036191783Srmacklem * mount system call
1037191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1038191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1039191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1040191783Srmacklem * an error after that means that I have to release the mbuf.
1041191783Srmacklem */
1042191783Srmacklem/* ARGSUSED */
1043191783Srmacklemstatic int
1044191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags)
1045191783Srmacklem{
1046191783Srmacklem	int error;
1047191783Srmacklem	struct nfs_args args;
1048191783Srmacklem
1049191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1050191783Srmacklem	if (error)
1051191783Srmacklem		return error;
1052191783Srmacklem
1053191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1054191783Srmacklem
1055191783Srmacklem	error = kernel_mount(ma, flags);
1056191783Srmacklem	return (error);
1057191783Srmacklem}
1058191783Srmacklem
1059191783Srmacklem/*
1060191783Srmacklem * Common code for mount and mountroot
1061191783Srmacklem */
1062191783Srmacklemstatic int
1063191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1064191783Srmacklem    char *hst, u_char *krbname, u_char *dirpath, u_char *srvkrbname,
1065203303Srmacklem    struct vnode **vpp, struct ucred *cred, struct thread *td,
1066203303Srmacklem    int negnametimeo)
1067191783Srmacklem{
1068191783Srmacklem	struct nfsmount *nmp;
1069191783Srmacklem	struct nfsnode *np;
1070195762Srmacklem	int error, trycnt, ret;
1071191783Srmacklem	struct nfsvattr nfsva;
1072191783Srmacklem	static u_int64_t clval = 0;
1073191783Srmacklem
1074191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1075191783Srmacklem		nmp = VFSTONFS(mp);
1076191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1077191783Srmacklem		FREE(nam, M_SONAME);
1078191783Srmacklem		return (0);
1079191783Srmacklem	} else {
1080191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1081191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2,
1082191783Srmacklem		    M_NEWNFSMNT, M_WAITOK);
1083191783Srmacklem		bzero((caddr_t)nmp, sizeof (struct nfsmount) +
1084191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2);
1085191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1086191783Srmacklem		if (clval == 0)
1087191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1088191783Srmacklem		nmp->nm_clval = clval++;
1089191783Srmacklem		nmp->nm_krbnamelen = argp->krbnamelen;
1090191783Srmacklem		nmp->nm_dirpathlen = argp->dirlen;
1091191783Srmacklem		nmp->nm_srvkrbnamelen = argp->srvkrbnamelen;
1092192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1093191783Srmacklem			/*
1094192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1095192675Srmacklem			 * the nfsv4 state handling operations if there is
1096192675Srmacklem			 * no host based principal set. Use the uid of
1097192675Srmacklem			 * this user if not root, since they are doing the
1098192675Srmacklem			 * mount. I don't think setting this for root will
1099192675Srmacklem			 * work, since root normally does not have user
1100192675Srmacklem			 * credentials in a credentials cache.
1101191783Srmacklem			 */
1102192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1103191783Srmacklem		} else {
1104191783Srmacklem			/*
1105192675Srmacklem			 * Just set to -1, so it won't be used.
1106191783Srmacklem			 */
1107191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1108191783Srmacklem		}
1109191783Srmacklem
1110191783Srmacklem		/* Copy and null terminate all the names */
1111191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1112191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1113191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1114191783Srmacklem		}
1115191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1116191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1117191783Srmacklem			    nmp->nm_dirpathlen);
1118191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1119191783Srmacklem			    + 1] = '\0';
1120191783Srmacklem		}
1121191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1122191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1123191783Srmacklem			    nmp->nm_srvkrbnamelen);
1124191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1125191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1126191783Srmacklem		}
1127191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1128191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1129191783Srmacklem		mp->mnt_data = nmp;
1130214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1131216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1132191783Srmacklem	}
1133191783Srmacklem	vfs_getnewfsid(mp);
1134191783Srmacklem	nmp->nm_mountp = mp;
1135191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1136203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1137191783Srmacklem
1138214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1139192585Srmacklem
1140191783Srmacklem	/*
1141191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1142191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1143191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1144191783Srmacklem	 *
1145191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1146191783Srmacklem	 * that we can handle until we find out otherwise.
1147191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1148191783Srmacklem	 * buffer cache using signed(!) block numbers.
1149191783Srmacklem	 */
1150191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1151191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1152191783Srmacklem	else
1153191783Srmacklem		nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1;
1154191783Srmacklem
1155191783Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1156191783Srmacklem	nmp->nm_retry = NFS_RETRANS;
1157191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1158191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1159191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1160191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1161191783Srmacklem	}
1162191783Srmacklem	nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1163191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1164191783Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1165191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1166191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1167191783Srmacklem		nmp->nm_tprintf_delay = 0;
1168191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1169191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1170191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1171191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1172191783Srmacklem	if (nmp->nm_fhsize > 0)
1173191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1174191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1175191783Srmacklem	nmp->nm_nam = nam;
1176191783Srmacklem	/* Set up the sockets and per-host congestion */
1177191783Srmacklem	nmp->nm_sotype = argp->sotype;
1178191783Srmacklem	nmp->nm_soproto = argp->proto;
1179191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1180191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1181191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1182191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1183191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1184191783Srmacklem	else
1185191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1186191783Srmacklem
1187191783Srmacklem
1188191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1189191783Srmacklem		goto bad;
1190191783Srmacklem
1191191783Srmacklem	/*
1192191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1193191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1194191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1195191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1196191783Srmacklem	 * this problem, because one can identify root inodes by their
1197191783Srmacklem	 * number == ROOTINO (2).
1198191783Srmacklem	 */
1199191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1200191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1201191783Srmacklem		/*
1202191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1203191783Srmacklem		 * path needs to be looked up.
1204191783Srmacklem		 */
1205191783Srmacklem		trycnt = 3;
1206191783Srmacklem		do {
1207191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1208191783Srmacklem			    cred, td);
1209191783Srmacklem			if (error)
1210207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1211191783Srmacklem		} while (error && --trycnt > 0);
1212191783Srmacklem		if (error) {
1213191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1214191783Srmacklem			goto bad;
1215191783Srmacklem		}
1216191783Srmacklem	}
1217191783Srmacklem	if (nmp->nm_fhsize > 0) {
1218195762Srmacklem		/*
1219195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1220195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1221195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1222195762Srmacklem		 */
1223195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1224220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1225220732Srmacklem		    LK_EXCLUSIVE);
1226191783Srmacklem		if (error)
1227191783Srmacklem			goto bad;
1228191783Srmacklem		*vpp = NFSTOV(np);
1229191783Srmacklem
1230191783Srmacklem		/*
1231191783Srmacklem		 * Get file attributes and transfer parameters for the
1232191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1233191783Srmacklem		 * (*vpp)->v_type with the correct value.
1234191783Srmacklem		 */
1235191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1236191783Srmacklem		    cred, td, &nfsva, NULL);
1237191783Srmacklem		if (ret) {
1238191783Srmacklem			/*
1239191783Srmacklem			 * Just set default values to get things going.
1240191783Srmacklem			 */
1241191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1242191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1243191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1244191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1245191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1246191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1247191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1248191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1249191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1250191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1251191783Srmacklem		}
1252191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1253191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1254191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1255191783Srmacklem
1256191783Srmacklem		/*
1257191783Srmacklem		 * Lose the lock but keep the ref.
1258191783Srmacklem		 */
1259191783Srmacklem		VOP_UNLOCK(*vpp, 0);
1260191783Srmacklem		return (0);
1261191783Srmacklem	}
1262191783Srmacklem	error = EIO;
1263191783Srmacklem
1264191783Srmacklembad:
1265191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1266191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1267191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1268191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1269191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1270191783Srmacklem	FREE(nam, M_SONAME);
1271191783Srmacklem	return (error);
1272191783Srmacklem}
1273191783Srmacklem
1274191783Srmacklem/*
1275191783Srmacklem * unmount system call
1276191783Srmacklem */
1277191783Srmacklemstatic int
1278191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1279191783Srmacklem{
1280191990Sattilio	struct thread *td;
1281191783Srmacklem	struct nfsmount *nmp;
1282191783Srmacklem	int error, flags = 0, trycnt = 0;
1283191783Srmacklem
1284191990Sattilio	td = curthread;
1285191990Sattilio
1286191783Srmacklem	if (mntflags & MNT_FORCE)
1287191783Srmacklem		flags |= FORCECLOSE;
1288191783Srmacklem	nmp = VFSTONFS(mp);
1289191783Srmacklem	/*
1290191783Srmacklem	 * Goes something like this..
1291191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1292191783Srmacklem	 * - Close the socket
1293191783Srmacklem	 * - Free up the data structures
1294191783Srmacklem	 */
1295191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1296191783Srmacklem	if (mntflags & MNT_FORCE) {
1297191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1298191783Srmacklem		if (error)
1299191783Srmacklem			goto out;
1300191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1301191783Srmacklem		nfscl_umount(nmp, td);
1302191783Srmacklem	}
1303191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1304191783Srmacklem	do {
1305191783Srmacklem		error = vflush(mp, 1, flags, td);
1306191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1307207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1308191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1309191783Srmacklem	if (error)
1310191783Srmacklem		goto out;
1311191783Srmacklem
1312191783Srmacklem	/*
1313191783Srmacklem	 * We are now committed to the unmount.
1314191783Srmacklem	 */
1315191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1316191783Srmacklem		nfscl_umount(nmp, td);
1317191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1318191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1319191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1320191783Srmacklem
1321191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1322191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1323191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1324191783Srmacklemout:
1325191783Srmacklem	return (error);
1326191783Srmacklem}
1327191783Srmacklem
1328191783Srmacklem/*
1329191783Srmacklem * Return root of a filesystem
1330191783Srmacklem */
1331191783Srmacklemstatic int
1332191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1333191783Srmacklem{
1334191783Srmacklem	struct vnode *vp;
1335191783Srmacklem	struct nfsmount *nmp;
1336191783Srmacklem	struct nfsnode *np;
1337191783Srmacklem	int error;
1338191783Srmacklem
1339191783Srmacklem	nmp = VFSTONFS(mp);
1340220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1341191783Srmacklem	if (error)
1342191783Srmacklem		return error;
1343191783Srmacklem	vp = NFSTOV(np);
1344191783Srmacklem	/*
1345191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1346191783Srmacklem	 */
1347191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1348191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1349191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1350191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1351191783Srmacklem	} else
1352191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1353191783Srmacklem	if (vp->v_type == VNON)
1354191783Srmacklem	    vp->v_type = VDIR;
1355191783Srmacklem	vp->v_vflag |= VV_ROOT;
1356191783Srmacklem	*vpp = vp;
1357191783Srmacklem	return (0);
1358191783Srmacklem}
1359191783Srmacklem
1360191783Srmacklem/*
1361191783Srmacklem * Flush out the buffer cache
1362191783Srmacklem */
1363191783Srmacklem/* ARGSUSED */
1364191783Srmacklemstatic int
1365191990Sattilionfs_sync(struct mount *mp, int waitfor)
1366191783Srmacklem{
1367191783Srmacklem	struct vnode *vp, *mvp;
1368191990Sattilio	struct thread *td;
1369191783Srmacklem	int error, allerror = 0;
1370191783Srmacklem
1371191990Sattilio	td = curthread;
1372191990Sattilio
1373191783Srmacklem	/*
1374191783Srmacklem	 * Force stale buffer cache information to be flushed.
1375191783Srmacklem	 */
1376191783Srmacklem	MNT_ILOCK(mp);
1377191783Srmacklemloop:
1378191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1379191783Srmacklem		VI_LOCK(vp);
1380191783Srmacklem		MNT_IUNLOCK(mp);
1381191783Srmacklem		/* XXX Racy bv_cnt check. */
1382191783Srmacklem		if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1383191783Srmacklem		    waitfor == MNT_LAZY) {
1384191783Srmacklem			VI_UNLOCK(vp);
1385191783Srmacklem			MNT_ILOCK(mp);
1386191783Srmacklem			continue;
1387191783Srmacklem		}
1388191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1389191783Srmacklem			MNT_ILOCK(mp);
1390191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1391191783Srmacklem			goto loop;
1392191783Srmacklem		}
1393191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1394191783Srmacklem		if (error)
1395191783Srmacklem			allerror = error;
1396191783Srmacklem		VOP_UNLOCK(vp, 0);
1397191783Srmacklem		vrele(vp);
1398191783Srmacklem
1399191783Srmacklem		MNT_ILOCK(mp);
1400191783Srmacklem	}
1401191783Srmacklem	MNT_IUNLOCK(mp);
1402191783Srmacklem	return (allerror);
1403191783Srmacklem}
1404191783Srmacklem
1405191783Srmacklemstatic int
1406191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1407191783Srmacklem{
1408191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1409191783Srmacklem	struct vfsquery vq;
1410191783Srmacklem	int error;
1411191783Srmacklem
1412191783Srmacklem	bzero(&vq, sizeof(vq));
1413191783Srmacklem	switch (op) {
1414191783Srmacklem#if 0
1415191783Srmacklem	case VFS_CTL_NOLOCKS:
1416191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1417191783Srmacklem 		if (req->oldptr != NULL) {
1418191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1419191783Srmacklem 			if (error)
1420191783Srmacklem 				return (error);
1421191783Srmacklem 		}
1422191783Srmacklem 		if (req->newptr != NULL) {
1423191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1424191783Srmacklem 			if (error)
1425191783Srmacklem 				return (error);
1426191783Srmacklem			if (val)
1427191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1428191783Srmacklem			else
1429191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1430191783Srmacklem 		}
1431191783Srmacklem		break;
1432191783Srmacklem#endif
1433191783Srmacklem	case VFS_CTL_QUERY:
1434191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1435191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1436191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1437191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1438191783Srmacklem#if 0
1439191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1440191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1441191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1442191783Srmacklem#endif
1443191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1444191783Srmacklem		break;
1445191783Srmacklem 	case VFS_CTL_TIMEO:
1446191783Srmacklem 		if (req->oldptr != NULL) {
1447191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1448191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1449191783Srmacklem 			if (error)
1450191783Srmacklem 				return (error);
1451191783Srmacklem 		}
1452191783Srmacklem 		if (req->newptr != NULL) {
1453191783Srmacklem			error = vfs_suser(mp, req->td);
1454191783Srmacklem			if (error)
1455191783Srmacklem				return (error);
1456191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1457191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1458191783Srmacklem 			if (error)
1459191783Srmacklem 				return (error);
1460191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1461191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1462191783Srmacklem 		}
1463191783Srmacklem		break;
1464191783Srmacklem	default:
1465191783Srmacklem		return (ENOTSUP);
1466191783Srmacklem	}
1467191783Srmacklem	return (0);
1468191783Srmacklem}
1469191783Srmacklem
1470214048Srmacklem/*
1471214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1472214048Srmacklem */
1473214048Srmacklemstatic void
1474214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1475216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1476216931Srmacklem    struct timeval *timeop)
1477214048Srmacklem{
1478214048Srmacklem	struct nfsmount *nmp;
1479214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1480214048Srmacklem
1481214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1482214048Srmacklem	if (fhlenp != NULL)
1483214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1484214048Srmacklem	if (fhp != NULL)
1485214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1486214048Srmacklem	if (sp != NULL)
1487214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1488214048Srmacklem	if (is_v3p != NULL)
1489214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1490214048Srmacklem	if (sizep != NULL)
1491214048Srmacklem		*sizep = np->n_size;
1492216931Srmacklem	if (timeop != NULL) {
1493216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1494216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1495216931Srmacklem	}
1496214048Srmacklem}
1497214048Srmacklem
1498