nfs_clvfsops.c revision 218757
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 218757 2011-02-16 21:29:13Z bz $");
37191783Srmacklem
38191783Srmacklem
39191783Srmacklem#include "opt_bootp.h"
40191783Srmacklem#include "opt_nfsroot.h"
41191783Srmacklem
42191783Srmacklem#include <sys/param.h>
43191783Srmacklem#include <sys/systm.h>
44191783Srmacklem#include <sys/kernel.h>
45191783Srmacklem#include <sys/bio.h>
46191783Srmacklem#include <sys/buf.h>
47191783Srmacklem#include <sys/clock.h>
48193066Sjamie#include <sys/jail.h>
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
76191783Srmacklemextern int nfscl_ticks;
77191783Srmacklemextern struct timeval nfsboottime;
78191783Srmacklemextern struct nfsstats	newnfsstats;
79191783Srmacklem
80191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
81191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
82191783Srmacklem
83191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
84191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW,
85191783Srmacklem	&newnfsstats, nfsstats, "S,nfsstats");
86191783Srmacklemstatic int nfs_ip_paranoia = 1;
87191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
88191783Srmacklem    &nfs_ip_paranoia, 0, "");
89191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
90191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_INITIAL_DELAY,
91191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
92191783Srmacklem/* how long between console messages "nfs server foo not responding" */
93191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
94191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY,
95191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
96191783Srmacklem
97192585Srmacklemstatic void	nfs_sec_name(char *, int *);
98191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
99214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
100214048Srmacklem		    struct thread *);
101191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
102191783Srmacklem		    struct sockaddr *, char *, u_char *, u_char *, u_char *,
103203303Srmacklem		    struct vnode **, struct ucred *, struct thread *, int);
104214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
105216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
106216931Srmacklem		    struct timeval *);
107191783Srmacklemstatic vfs_mount_t nfs_mount;
108191783Srmacklemstatic vfs_cmount_t nfs_cmount;
109191783Srmacklemstatic vfs_unmount_t nfs_unmount;
110191783Srmacklemstatic vfs_root_t nfs_root;
111191783Srmacklemstatic vfs_statfs_t nfs_statfs;
112191783Srmacklemstatic vfs_sync_t nfs_sync;
113191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
114191783Srmacklem
115191783Srmacklem/*
116191783Srmacklem * nfs vfs operations.
117191783Srmacklem */
118191783Srmacklemstatic struct vfsops nfs_vfsops = {
119191783Srmacklem	.vfs_init =		ncl_init,
120191783Srmacklem	.vfs_mount =		nfs_mount,
121191783Srmacklem	.vfs_cmount =		nfs_cmount,
122191783Srmacklem	.vfs_root =		nfs_root,
123191783Srmacklem	.vfs_statfs =		nfs_statfs,
124191783Srmacklem	.vfs_sync =		nfs_sync,
125191783Srmacklem	.vfs_uninit =		ncl_uninit,
126191783Srmacklem	.vfs_unmount =		nfs_unmount,
127191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
128191783Srmacklem};
129191783SrmacklemVFS_SET(nfs_vfsops, newnfs, VFCF_NETWORK);
130191783Srmacklem
131191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
132191783SrmacklemMODULE_VERSION(newnfs, 1);
133191783Srmacklem
134191783Srmacklem/*
135191783Srmacklem * This structure must be filled in by a primary bootstrap or bootstrap
136191783Srmacklem * server for a diskless/dataless machine. It is initialized below just
137191783Srmacklem * to ensure that it is allocated to initialized data (.data not .bss).
138191783Srmacklem */
139191783Srmacklemstruct nfs_diskless newnfs_diskless = { { { 0 } } };
140191783Srmacklemstruct nfsv3_diskless newnfsv3_diskless = { { { 0 } } };
141191783Srmacklemint newnfs_diskless_valid = 0;
142191783Srmacklem
143191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
144192145Srmacklem    &newnfs_diskless_valid, 0,
145192145Srmacklem    "Has the diskless struct been filled correctly");
146191783Srmacklem
147191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
148192145Srmacklem    newnfsv3_diskless.root_hostnam, 0, "Path to nfs root");
149191783Srmacklem
150191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
151192145Srmacklem    &newnfsv3_diskless.root_saddr, sizeof newnfsv3_diskless.root_saddr,
152192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
153191783Srmacklem
154191783Srmacklem
155191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
156191783Srmacklemstatic int	nfs_mountdiskless(char *,
157191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
158191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
159191783Srmacklemstatic void	nfs_convert_diskless(void);
160191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
161191783Srmacklem		    struct onfs_args *oargs);
162191783Srmacklem
163191783Srmacklemint
164191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
165191783Srmacklem{
166191783Srmacklem	int iosize, maxio;
167191783Srmacklem
168191783Srmacklem	/* First, set the upper limit for iosize */
169191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
170191783Srmacklem		maxio = NFS_MAXBSIZE;
171191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
172191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
173191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
174191783Srmacklem		else
175191783Srmacklem			maxio = NFS_MAXBSIZE;
176191783Srmacklem	} else {
177191783Srmacklem		maxio = NFS_V2MAXDATA;
178191783Srmacklem	}
179191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
180191783Srmacklem		nmp->nm_rsize = maxio;
181191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
182191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
183191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
184191783Srmacklem		nmp->nm_readdirsize = maxio;
185191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
186191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
187191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
188191783Srmacklem		nmp->nm_wsize = maxio;
189191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
190191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
191191783Srmacklem
192191783Srmacklem	/*
193191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
194191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
195191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
196191783Srmacklem	 * space.
197191783Srmacklem	 */
198191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
199191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
200191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
201191783Srmacklem	return (iosize);
202191783Srmacklem}
203191783Srmacklem
204191783Srmacklemstatic void
205191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
206191783Srmacklem{
207191783Srmacklem
208191783Srmacklem	args->version = NFS_ARGSVERSION;
209191783Srmacklem	args->addr = oargs->addr;
210191783Srmacklem	args->addrlen = oargs->addrlen;
211191783Srmacklem	args->sotype = oargs->sotype;
212191783Srmacklem	args->proto = oargs->proto;
213191783Srmacklem	args->fh = oargs->fh;
214191783Srmacklem	args->fhsize = oargs->fhsize;
215191783Srmacklem	args->flags = oargs->flags;
216191783Srmacklem	args->wsize = oargs->wsize;
217191783Srmacklem	args->rsize = oargs->rsize;
218191783Srmacklem	args->readdirsize = oargs->readdirsize;
219191783Srmacklem	args->timeo = oargs->timeo;
220191783Srmacklem	args->retrans = oargs->retrans;
221191783Srmacklem	args->readahead = oargs->readahead;
222191783Srmacklem	args->hostname = oargs->hostname;
223191783Srmacklem}
224191783Srmacklem
225191783Srmacklemstatic void
226191783Srmacklemnfs_convert_diskless(void)
227191783Srmacklem{
228191783Srmacklem
229191783Srmacklem	bcopy(&newnfs_diskless.myif, &newnfsv3_diskless.myif,
230192145Srmacklem	    sizeof (struct ifaliasreq));
231191783Srmacklem	bcopy(&newnfs_diskless.mygateway, &newnfsv3_diskless.mygateway,
232192145Srmacklem	    sizeof (struct sockaddr_in));
233192145Srmacklem	nfs_convert_oargs(&newnfsv3_diskless.root_args,
234192145Srmacklem	    &newnfs_diskless.root_args);
235191783Srmacklem	if (newnfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
236191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_MYFH;
237192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
238192145Srmacklem		    NFSX_MYFH);
239191783Srmacklem	} else {
240191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_V2FH;
241192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
242192145Srmacklem		    NFSX_V2FH);
243191783Srmacklem	}
244191783Srmacklem	bcopy(&newnfs_diskless.root_saddr,&newnfsv3_diskless.root_saddr,
245192145Srmacklem	    sizeof(struct sockaddr_in));
246192145Srmacklem	bcopy(newnfs_diskless.root_hostnam, newnfsv3_diskless.root_hostnam,
247192145Srmacklem	    MNAMELEN);
248191783Srmacklem	newnfsv3_diskless.root_time = newnfs_diskless.root_time;
249191783Srmacklem	bcopy(newnfs_diskless.my_hostnam, newnfsv3_diskless.my_hostnam,
250192145Srmacklem	    MAXHOSTNAMELEN);
251191783Srmacklem	newnfs_diskless_valid = 3;
252191783Srmacklem}
253191783Srmacklem
254191783Srmacklem/*
255191783Srmacklem * nfs statfs call
256191783Srmacklem */
257191783Srmacklemstatic int
258191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
259191783Srmacklem{
260191783Srmacklem	struct vnode *vp;
261191990Sattilio	struct thread *td;
262191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
263191783Srmacklem	struct nfsvattr nfsva;
264191783Srmacklem	struct nfsfsinfo fs;
265191783Srmacklem	struct nfsstatfs sb;
266191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
267191783Srmacklem	struct nfsnode *np;
268191783Srmacklem
269191990Sattilio	td = curthread;
270191990Sattilio
271191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
272191783Srmacklem	if (error)
273191783Srmacklem		return (error);
274191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
275191783Srmacklem	if (error) {
276191783Srmacklem		vfs_unbusy(mp);
277191783Srmacklem		return (error);
278191783Srmacklem	}
279191783Srmacklem	vp = NFSTOV(np);
280191783Srmacklem	mtx_lock(&nmp->nm_mtx);
281191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
282191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
283191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
284191783Srmacklem		    &attrflag, NULL);
285191783Srmacklem		if (!error)
286191783Srmacklem			gotfsinfo = 1;
287191783Srmacklem	} else
288191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
289191783Srmacklem	if (!error)
290191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
291191783Srmacklem		    &attrflag, NULL);
292191783Srmacklem	if (attrflag == 0) {
293191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
294191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
295191783Srmacklem		if (ret) {
296191783Srmacklem			/*
297191783Srmacklem			 * Just set default values to get things going.
298191783Srmacklem			 */
299191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
300191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
301191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
302191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
303191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
304191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
305191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
306191783Srmacklem			nfsva.na_vattr.va_gen = 1;
307191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
308191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
309191783Srmacklem		}
310191783Srmacklem	}
311191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
312191783Srmacklem	if (!error) {
313191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
314191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
315191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
316191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
317191783Srmacklem	    sbp->f_flags = nmp->nm_flag;
318191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
319191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
320191783Srmacklem	    if (sbp != &mp->mnt_stat) {
321191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
322191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
323191783Srmacklem	    }
324191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
325191783Srmacklem	} else if (NFS_ISV4(vp)) {
326191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
327191783Srmacklem	}
328191783Srmacklem	vput(vp);
329191783Srmacklem	vfs_unbusy(mp);
330191783Srmacklem	return (error);
331191783Srmacklem}
332191783Srmacklem
333191783Srmacklem/*
334191783Srmacklem * nfs version 3 fsinfo rpc call
335191783Srmacklem */
336191783Srmacklemint
337191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
338191783Srmacklem    struct thread *td)
339191783Srmacklem{
340191783Srmacklem	struct nfsfsinfo fs;
341191783Srmacklem	struct nfsvattr nfsva;
342191783Srmacklem	int error, attrflag;
343191783Srmacklem
344191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
345191783Srmacklem	if (!error) {
346191783Srmacklem		if (attrflag)
347191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
348191783Srmacklem			    1);
349191783Srmacklem		mtx_lock(&nmp->nm_mtx);
350191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
351191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
352191783Srmacklem	}
353191783Srmacklem	return (error);
354191783Srmacklem}
355191783Srmacklem
356191783Srmacklem/*
357191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
358191783Srmacklem * newnfs_diskless structure that has been filled in properly by some primary
359191783Srmacklem * bootstrap.
360191783Srmacklem * It goes something like this:
361191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
362191783Srmacklem *   can talk to the server
363191783Srmacklem * - If newnfs_diskless.mygateway is filled in, use that address as
364191783Srmacklem *   a default gateway.
365191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
366191783Srmacklem *
367191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
368191783Srmacklem * structure, as well as other global NFS client variables here, as
369192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
370191783Srmacklem * client activity occurs.
371191783Srmacklem */
372191783Srmacklemint
373192145Srmacklemncl_mountroot(struct mount *mp)
374191783Srmacklem{
375192145Srmacklem	struct thread *td = curthread;
376191783Srmacklem	struct nfsv3_diskless *nd = &newnfsv3_diskless;
377191783Srmacklem	struct socket *so;
378191783Srmacklem	struct vnode *vp;
379191783Srmacklem	struct ifreq ir;
380193066Sjamie	int error;
381191783Srmacklem	u_long l;
382191783Srmacklem	char buf[128];
383191783Srmacklem	char *cp;
384191783Srmacklem
385191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
386192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
387191783Srmacklem#elif defined(NFS_ROOT)
388191783Srmacklem	nfs_setup_diskless();
389191783Srmacklem#endif
390191783Srmacklem
391191783Srmacklem	if (newnfs_diskless_valid == 0)
392191783Srmacklem		return (-1);
393191783Srmacklem	if (newnfs_diskless_valid == 1)
394191783Srmacklem		nfs_convert_diskless();
395191783Srmacklem
396191783Srmacklem	/*
397191783Srmacklem	 * XXX splnet, so networks will receive...
398191783Srmacklem	 */
399191783Srmacklem	splnet();
400191783Srmacklem
401191783Srmacklem	/*
402191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
403191783Srmacklem	 * talk to the server.
404191783Srmacklem	 */
405191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
406191783Srmacklem	    td->td_ucred, td);
407191783Srmacklem	if (error)
408192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
409191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
410191783Srmacklem
411191783Srmacklem#if 0 /* XXX Bad idea */
412191783Srmacklem	/*
413191783Srmacklem	 * We might not have been told the right interface, so we pass
414191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
415191783Srmacklem	 * one of them configured.
416191783Srmacklem	 */
417191783Srmacklem
418191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
419191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
420191783Srmacklem		nd->myif.ifra_name[i] <= '9';
421191783Srmacklem		nd->myif.ifra_name[i] ++) {
422191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
423191783Srmacklem		if(!error)
424191783Srmacklem			break;
425191783Srmacklem	}
426191783Srmacklem#endif
427191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
428191783Srmacklem	if (error)
429192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
430191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
431191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
432191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
433191783Srmacklem		freeenv(cp);
434191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
435191783Srmacklem		if (error)
436192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
437191783Srmacklem	}
438191783Srmacklem	soclose(so);
439191783Srmacklem
440191783Srmacklem	/*
441191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
442191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
443191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
444191783Srmacklem	 * to avoid panicking inappropriately in that situation.
445191783Srmacklem	 */
446191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
447191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
448191783Srmacklem		struct sockaddr_in mask, sin;
449191783Srmacklem
450191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
451191783Srmacklem		sin = mask;
452191783Srmacklem		sin.sin_family = AF_INET;
453191783Srmacklem		sin.sin_len = sizeof(sin);
454192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
455218757Sbz		CURVNET_SET(TD_TO_VNET(td));
456191783Srmacklem		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
457191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
458191783Srmacklem		    (struct sockaddr *)&mask,
459191783Srmacklem		    RTF_UP | RTF_GATEWAY, NULL);
460218757Sbz		CURVNET_RESTORE();
461191783Srmacklem		if (error)
462192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
463191783Srmacklem	}
464191783Srmacklem
465191783Srmacklem	/*
466191783Srmacklem	 * Create the rootfs mount point.
467191783Srmacklem	 */
468191783Srmacklem	nd->root_args.fh = nd->root_fh;
469191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
470191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
471191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
472191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
473191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
474191783Srmacklem	printf("NFS ROOT: %s\n", buf);
475192145Srmacklem	nd->root_args.hostname = buf;
476191783Srmacklem	if ((error = nfs_mountdiskless(buf,
477191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
478191783Srmacklem		return (error);
479191783Srmacklem	}
480191783Srmacklem
481191783Srmacklem	/*
482191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
483191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
484191783Srmacklem	 * mount the right /var based upon its preset value.
485191783Srmacklem	 */
486193066Sjamie	mtx_lock(&prison0.pr_mtx);
487194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
488194118Sjamie	    sizeof(prison0.pr_hostname));
489193066Sjamie	mtx_unlock(&prison0.pr_mtx);
490191783Srmacklem	inittodr(ntohl(nd->root_time));
491191783Srmacklem	return (0);
492191783Srmacklem}
493191783Srmacklem
494191783Srmacklem/*
495191783Srmacklem * Internal version of mount system call for diskless setup.
496191783Srmacklem */
497191783Srmacklemstatic int
498191783Srmacklemnfs_mountdiskless(char *path,
499191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
500191783Srmacklem    struct vnode **vpp, struct mount *mp)
501191783Srmacklem{
502191783Srmacklem	struct sockaddr *nam;
503191783Srmacklem	int error;
504191783Srmacklem
505191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
506191783Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, NULL, NULL, vpp,
507203303Srmacklem	    td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
508192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
509191783Srmacklem		return (error);
510191783Srmacklem	}
511191783Srmacklem	return (0);
512191783Srmacklem}
513191783Srmacklem
514191783Srmacklemstatic void
515192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
516192585Srmacklem{
517192585Srmacklem	if (!strcmp(sec, "krb5"))
518192585Srmacklem		*flagsp |= NFSMNT_KERB;
519192585Srmacklem	else if (!strcmp(sec, "krb5i"))
520192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
521192585Srmacklem	else if (!strcmp(sec, "krb5p"))
522192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
523192585Srmacklem}
524192585Srmacklem
525192585Srmacklemstatic void
526191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
527214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
528191783Srmacklem{
529191783Srmacklem	int s;
530191783Srmacklem	int adjsock;
531214048Srmacklem	char *p;
532191783Srmacklem
533191783Srmacklem	s = splnet();
534191783Srmacklem
535191783Srmacklem	/*
536191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
537191783Srmacklem	 * an update.  If this is not an update, then either the read-only
538191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
539191783Srmacklem	 * intentionally at some previous point.
540191783Srmacklem	 */
541191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
542191783Srmacklem		MNT_ILOCK(mp);
543191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
544191783Srmacklem		MNT_IUNLOCK(mp);
545191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
546191783Srmacklem		MNT_ILOCK(mp);
547191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
548191783Srmacklem		MNT_IUNLOCK(mp);
549191783Srmacklem	}
550191783Srmacklem
551191783Srmacklem	/*
552191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
553191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
554191783Srmacklem	 * and soft timeout behavior.
555191783Srmacklem	 */
556191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
557191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
558191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
559191783Srmacklem	}
560191783Srmacklem
561191783Srmacklem	/* Also clear RDIRPLUS if not NFSv3, it crashes some servers */
562191783Srmacklem	if ((argp->flags & NFSMNT_NFSV3) == 0)
563191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
564191783Srmacklem
565191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
566191783Srmacklem	adjsock = ((nmp->nm_flag & NFSMNT_NOCONN) !=
567191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
568191783Srmacklem
569191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
570191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
571191783Srmacklem	splx(s);
572191783Srmacklem
573191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
574191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
575191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
576191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
577191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
578191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
579191783Srmacklem	}
580191783Srmacklem
581191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
582191783Srmacklem		nmp->nm_retry = argp->retrans;
583191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
584191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
585191783Srmacklem	}
586191783Srmacklem
587191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
588191783Srmacklem		nmp->nm_wsize = argp->wsize;
589191783Srmacklem		/* Round down to multiple of blocksize */
590191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
591191783Srmacklem		if (nmp->nm_wsize <= 0)
592191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
593191783Srmacklem	}
594191783Srmacklem
595191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
596191783Srmacklem		nmp->nm_rsize = argp->rsize;
597191783Srmacklem		/* Round down to multiple of blocksize */
598191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
599191783Srmacklem		if (nmp->nm_rsize <= 0)
600191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
601191783Srmacklem	}
602191783Srmacklem
603191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
604191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
605191783Srmacklem	}
606191783Srmacklem
607191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
608191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
609191783Srmacklem	else
610191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
611191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
612191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
613191783Srmacklem	else
614191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
615191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
616191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
617191783Srmacklem	else
618191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
619191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
620191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
621191783Srmacklem	else
622191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
623191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
624191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
625191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
626191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
627191783Srmacklem
628191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
629191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
630191783Srmacklem			nmp->nm_readahead = argp->readahead;
631191783Srmacklem		else
632191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
633191783Srmacklem	}
634191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
635191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
636191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
637191783Srmacklem		else
638191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
639191783Srmacklem	}
640191783Srmacklem
641191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
642191783Srmacklem		    (nmp->nm_soproto != argp->proto));
643191783Srmacklem
644191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
645191783Srmacklem		int haslock = 0, error = 0;
646191783Srmacklem
647191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
648191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
649191783Srmacklem			if (!error)
650191783Srmacklem				haslock = 1;
651191783Srmacklem		}
652191783Srmacklem		if (!error) {
653191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
654191783Srmacklem		    if (haslock)
655191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
656191783Srmacklem		    nmp->nm_sotype = argp->sotype;
657191783Srmacklem		    nmp->nm_soproto = argp->proto;
658191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
659191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
660191783Srmacklem			    cred, td, 0)) {
661191783Srmacklem				printf("newnfs_args: retrying connect\n");
662207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
663191783Srmacklem			}
664191783Srmacklem		}
665191783Srmacklem	} else {
666191783Srmacklem		nmp->nm_sotype = argp->sotype;
667191783Srmacklem		nmp->nm_soproto = argp->proto;
668191783Srmacklem	}
669214048Srmacklem
670214048Srmacklem	if (hostname != NULL) {
671214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
672214048Srmacklem		    sizeof(nmp->nm_hostname));
673214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
674214048Srmacklem		if (p != NULL)
675214048Srmacklem			*p = '\0';
676214048Srmacklem	}
677191783Srmacklem}
678191783Srmacklem
679192585Srmacklemstatic const char *nfs_opts[] = { "from",
680191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
681191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
682192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
683192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
684192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
685192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
686192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
687203303Srmacklem    "negnametimeo",
688191783Srmacklem    NULL };
689191783Srmacklem
690191783Srmacklem/*
691191783Srmacklem * VFS Operations.
692191783Srmacklem *
693191783Srmacklem * mount system call
694191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
695191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
696191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
697191783Srmacklem * an error after that means that I have to release the mbuf.
698191783Srmacklem */
699191783Srmacklem/* ARGSUSED */
700191783Srmacklemstatic int
701191990Sattilionfs_mount(struct mount *mp)
702191783Srmacklem{
703191783Srmacklem	struct nfs_args args = {
704191783Srmacklem	    .version = NFS_ARGSVERSION,
705191783Srmacklem	    .addr = NULL,
706191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
707191783Srmacklem	    .sotype = SOCK_STREAM,
708191783Srmacklem	    .proto = 0,
709191783Srmacklem	    .fh = NULL,
710191783Srmacklem	    .fhsize = 0,
711191783Srmacklem	    .flags = 0,
712191783Srmacklem	    .wsize = NFS_WSIZE,
713191783Srmacklem	    .rsize = NFS_RSIZE,
714191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
715191783Srmacklem	    .timeo = 10,
716191783Srmacklem	    .retrans = NFS_RETRANS,
717191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
718191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
719191783Srmacklem	    .hostname = NULL,
720191783Srmacklem	    /* args version 4 */
721191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
722191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
723191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
724191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
725191783Srmacklem	    .dirlen = 0,
726191783Srmacklem	    .krbnamelen = 0,
727192585Srmacklem	    .srvkrbnamelen = 0,
728191783Srmacklem	};
729192585Srmacklem	int error = 0, ret, len;
730192585Srmacklem	struct sockaddr *nam = NULL;
731191783Srmacklem	struct vnode *vp;
732191990Sattilio	struct thread *td;
733191783Srmacklem	char hst[MNAMELEN];
734191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
735192585Srmacklem	char *opt, *name, *secname;
736203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
737191783Srmacklem
738191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
739191783Srmacklem		error = EINVAL;
740191783Srmacklem		goto out;
741191783Srmacklem	}
742191783Srmacklem
743191990Sattilio	td = curthread;
744191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
745192145Srmacklem		error = ncl_mountroot(mp);
746191783Srmacklem		goto out;
747191783Srmacklem	}
748191783Srmacklem
749192585Srmacklem	nfscl_init();
750191783Srmacklem
751192585Srmacklem	/* Handle the new style options. */
752192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
753192585Srmacklem		args.flags |= NFSMNT_NOCONN;
754192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
755192585Srmacklem		args.flags |= NFSMNT_NOCONN;
756192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
757192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
758192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
759192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
760192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
761192585Srmacklem		args.flags |= NFSMNT_INT;
762192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
763192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
764192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
765192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
766192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
767192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
768192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
769192585Srmacklem		args.flags |= NFSMNT_SOFT;
770192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
771192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
772192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
773192585Srmacklem		args.sotype = SOCK_DGRAM;
774192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
775192585Srmacklem		args.sotype = SOCK_DGRAM;
776192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
777192585Srmacklem		args.sotype = SOCK_STREAM;
778192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
779192585Srmacklem		args.flags |= NFSMNT_NFSV3;
780192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
781192585Srmacklem		args.flags |= NFSMNT_NFSV4;
782192585Srmacklem		args.sotype = SOCK_STREAM;
783191783Srmacklem	}
784192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
785192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
786192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
787192585Srmacklem		if (opt == NULL) {
788192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
789192585Srmacklem			error = EINVAL;
790192585Srmacklem			goto out;
791192585Srmacklem		}
792192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
793192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
794192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
795192585Srmacklem			    opt);
796192585Srmacklem			error = EINVAL;
797192585Srmacklem			goto out;
798192585Srmacklem		}
799192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
800192585Srmacklem	}
801192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
802192585Srmacklem		if (opt == NULL) {
803192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
804192585Srmacklem			error = EINVAL;
805192585Srmacklem			goto out;
806192585Srmacklem		}
807192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
808192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
809192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
810192585Srmacklem			    opt);
811192585Srmacklem			error = EINVAL;
812192585Srmacklem			goto out;
813192585Srmacklem		}
814192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
815192585Srmacklem	}
816192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
817192585Srmacklem		if (opt == NULL) {
818192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
819192585Srmacklem			error = EINVAL;
820192585Srmacklem			goto out;
821192585Srmacklem		}
822192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
823192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
824192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
825192585Srmacklem			    opt);
826192585Srmacklem			error = EINVAL;
827192585Srmacklem			goto out;
828192585Srmacklem		}
829192585Srmacklem		args.flags |= NFSMNT_WSIZE;
830192585Srmacklem	}
831192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
832192585Srmacklem		if (opt == NULL) {
833192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
834192585Srmacklem			error = EINVAL;
835192585Srmacklem			goto out;
836192585Srmacklem		}
837192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
838192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
839192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
840192585Srmacklem			    opt);
841192585Srmacklem			error = EINVAL;
842192585Srmacklem			goto out;
843192585Srmacklem		}
844192585Srmacklem		args.flags |= NFSMNT_RSIZE;
845192585Srmacklem	}
846192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
847192585Srmacklem		if (opt == NULL) {
848192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
849192585Srmacklem			error = EINVAL;
850192585Srmacklem			goto out;
851192585Srmacklem		}
852192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
853192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
854192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
855192585Srmacklem			    opt);
856192585Srmacklem			error = EINVAL;
857192585Srmacklem			goto out;
858192585Srmacklem		}
859192585Srmacklem		args.flags |= NFSMNT_RETRANS;
860192585Srmacklem	}
861192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
862192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
863192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
864192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
865192585Srmacklem			    opt);
866192585Srmacklem			error = EINVAL;
867192585Srmacklem			goto out;
868192585Srmacklem		}
869192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
870192585Srmacklem	}
871192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
872192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
873192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
874192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
875192585Srmacklem			    opt);
876192585Srmacklem			error = EINVAL;
877192585Srmacklem			goto out;
878192585Srmacklem		}
879192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
880192585Srmacklem	}
881192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
882192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
883192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
884192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
885192585Srmacklem			    opt);
886192585Srmacklem			error = EINVAL;
887192585Srmacklem			goto out;
888192585Srmacklem		}
889192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
890192585Srmacklem	}
891192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
892192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
893192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
894192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
895192585Srmacklem			    opt);
896192585Srmacklem			error = EINVAL;
897192585Srmacklem			goto out;
898192585Srmacklem		}
899192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
900192585Srmacklem	}
901192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
902192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
903192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
904192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
905192585Srmacklem			    opt);
906192585Srmacklem			error = EINVAL;
907192585Srmacklem			goto out;
908192585Srmacklem		}
909192585Srmacklem		args.flags |= NFSMNT_TIMEO;
910192585Srmacklem	}
911203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
912203303Srmacklem	    == 0) {
913203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
914203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
915203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
916203303Srmacklem			    opt);
917203303Srmacklem			error = EINVAL;
918203303Srmacklem			goto out;
919203303Srmacklem		}
920203303Srmacklem	}
921192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
922192585Srmacklem		(void **) &secname, NULL) == 0)
923192585Srmacklem		nfs_sec_name(secname, &args.flags);
924191783Srmacklem
925191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
926191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
927191783Srmacklem
928191783Srmacklem		if (nmp == NULL) {
929191783Srmacklem			error = EIO;
930191783Srmacklem			goto out;
931191783Srmacklem		}
932191783Srmacklem		/*
933191783Srmacklem		 * When doing an update, we can't change version,
934191783Srmacklem		 * security, switch lockd strategies or change cookie
935191783Srmacklem		 * translation
936191783Srmacklem		 */
937191783Srmacklem		args.flags = (args.flags &
938191783Srmacklem		    ~(NFSMNT_NFSV3 |
939191783Srmacklem		      NFSMNT_NFSV4 |
940191783Srmacklem		      NFSMNT_KERB |
941191783Srmacklem		      NFSMNT_INTEGRITY |
942191783Srmacklem		      NFSMNT_PRIVACY |
943191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
944191783Srmacklem		    (nmp->nm_flag &
945191783Srmacklem			(NFSMNT_NFSV3 |
946191783Srmacklem			 NFSMNT_NFSV4 |
947191783Srmacklem			 NFSMNT_KERB |
948191783Srmacklem			 NFSMNT_INTEGRITY |
949191783Srmacklem			 NFSMNT_PRIVACY |
950191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
951214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
952191783Srmacklem		goto out;
953191783Srmacklem	}
954191783Srmacklem
955191783Srmacklem	/*
956191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
957191783Srmacklem	 * or no-connection mode for those protocols that support
958191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
959191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
960191783Srmacklem	 * to receive replies from a different IP then the request was
961191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
962191783Srmacklem	 * not 0.
963191783Srmacklem	 */
964191783Srmacklem	if (nfs_ip_paranoia == 0)
965191783Srmacklem		args.flags |= NFSMNT_NOCONN;
966192585Srmacklem
967192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
968192585Srmacklem	    &args.fhsize) == 0) {
969208234Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
970192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
971191783Srmacklem			error = EINVAL;
972191783Srmacklem			goto out;
973191783Srmacklem		}
974192585Srmacklem		bcopy(args.fh, nfh, args.fhsize);
975191783Srmacklem	} else {
976192585Srmacklem		args.fhsize = 0;
977192585Srmacklem	}
978192585Srmacklem
979192585Srmacklem	(void) vfs_getopt(mp->mnt_optnew, "hostname", (void **)&args.hostname,
980192585Srmacklem	    &len);
981192585Srmacklem	if (args.hostname == NULL) {
982192585Srmacklem		vfs_mount_error(mp, "Invalid hostname");
983192585Srmacklem		error = EINVAL;
984192585Srmacklem		goto out;
985192585Srmacklem	}
986192585Srmacklem	bcopy(args.hostname, hst, MNAMELEN);
987192585Srmacklem	hst[MNAMELEN - 1] = '\0';
988192585Srmacklem
989192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
990192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
991192585Srmacklem	else
992192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
993192585Srmacklem	args.srvkrbnamelen = strlen(srvkrbname);
994192585Srmacklem
995192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
996192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
997192585Srmacklem	else
998191783Srmacklem		krbname[0] = '\0';
999192585Srmacklem	args.krbnamelen = strlen(krbname);
1000192585Srmacklem
1001192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1002192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1003192585Srmacklem	else
1004191783Srmacklem		dirpath[0] = '\0';
1005192585Srmacklem	args.dirlen = strlen(dirpath);
1006192585Srmacklem
1007192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "addr", (void **)&args.addr,
1008192585Srmacklem	    &args.addrlen) == 0) {
1009192585Srmacklem		if (args.addrlen > SOCK_MAXADDRLEN) {
1010192585Srmacklem			error = ENAMETOOLONG;
1011191783Srmacklem			goto out;
1012191783Srmacklem		}
1013192585Srmacklem		nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1014192585Srmacklem		bcopy(args.addr, nam, args.addrlen);
1015192585Srmacklem		nam->sa_len = args.addrlen;
1016191783Srmacklem	}
1017192585Srmacklem
1018191783Srmacklem	args.fh = nfh;
1019191783Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, dirpath, srvkrbname,
1020203303Srmacklem	    &vp, td->td_ucred, td, negnametimeo);
1021191783Srmacklemout:
1022191783Srmacklem	if (!error) {
1023191783Srmacklem		MNT_ILOCK(mp);
1024191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
1025191783Srmacklem		MNT_IUNLOCK(mp);
1026191783Srmacklem	}
1027191783Srmacklem	return (error);
1028191783Srmacklem}
1029191783Srmacklem
1030191783Srmacklem
1031191783Srmacklem/*
1032191783Srmacklem * VFS Operations.
1033191783Srmacklem *
1034191783Srmacklem * mount system call
1035191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1036191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1037191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1038191783Srmacklem * an error after that means that I have to release the mbuf.
1039191783Srmacklem */
1040191783Srmacklem/* ARGSUSED */
1041191783Srmacklemstatic int
1042191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags)
1043191783Srmacklem{
1044191783Srmacklem	int error;
1045191783Srmacklem	struct nfs_args args;
1046191783Srmacklem
1047191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1048191783Srmacklem	if (error)
1049191783Srmacklem		return error;
1050191783Srmacklem
1051191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1052191783Srmacklem
1053191783Srmacklem	error = kernel_mount(ma, flags);
1054191783Srmacklem	return (error);
1055191783Srmacklem}
1056191783Srmacklem
1057191783Srmacklem/*
1058191783Srmacklem * Common code for mount and mountroot
1059191783Srmacklem */
1060191783Srmacklemstatic int
1061191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1062191783Srmacklem    char *hst, u_char *krbname, u_char *dirpath, u_char *srvkrbname,
1063203303Srmacklem    struct vnode **vpp, struct ucred *cred, struct thread *td,
1064203303Srmacklem    int negnametimeo)
1065191783Srmacklem{
1066191783Srmacklem	struct nfsmount *nmp;
1067191783Srmacklem	struct nfsnode *np;
1068195762Srmacklem	int error, trycnt, ret;
1069191783Srmacklem	struct nfsvattr nfsva;
1070191783Srmacklem	static u_int64_t clval = 0;
1071191783Srmacklem
1072191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1073191783Srmacklem		nmp = VFSTONFS(mp);
1074191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1075191783Srmacklem		FREE(nam, M_SONAME);
1076191783Srmacklem		return (0);
1077191783Srmacklem	} else {
1078191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1079191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2,
1080191783Srmacklem		    M_NEWNFSMNT, M_WAITOK);
1081191783Srmacklem		bzero((caddr_t)nmp, sizeof (struct nfsmount) +
1082191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2);
1083191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1084191783Srmacklem		if (clval == 0)
1085191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1086191783Srmacklem		nmp->nm_clval = clval++;
1087191783Srmacklem		nmp->nm_krbnamelen = argp->krbnamelen;
1088191783Srmacklem		nmp->nm_dirpathlen = argp->dirlen;
1089191783Srmacklem		nmp->nm_srvkrbnamelen = argp->srvkrbnamelen;
1090192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1091191783Srmacklem			/*
1092192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1093192675Srmacklem			 * the nfsv4 state handling operations if there is
1094192675Srmacklem			 * no host based principal set. Use the uid of
1095192675Srmacklem			 * this user if not root, since they are doing the
1096192675Srmacklem			 * mount. I don't think setting this for root will
1097192675Srmacklem			 * work, since root normally does not have user
1098192675Srmacklem			 * credentials in a credentials cache.
1099191783Srmacklem			 */
1100192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1101191783Srmacklem		} else {
1102191783Srmacklem			/*
1103192675Srmacklem			 * Just set to -1, so it won't be used.
1104191783Srmacklem			 */
1105191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1106191783Srmacklem		}
1107191783Srmacklem
1108191783Srmacklem		/* Copy and null terminate all the names */
1109191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1110191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1111191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1112191783Srmacklem		}
1113191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1114191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1115191783Srmacklem			    nmp->nm_dirpathlen);
1116191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1117191783Srmacklem			    + 1] = '\0';
1118191783Srmacklem		}
1119191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1120191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1121191783Srmacklem			    nmp->nm_srvkrbnamelen);
1122191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1123191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1124191783Srmacklem		}
1125191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1126191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1127191783Srmacklem		mp->mnt_data = nmp;
1128214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1129216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1130191783Srmacklem	}
1131191783Srmacklem	vfs_getnewfsid(mp);
1132191783Srmacklem	nmp->nm_mountp = mp;
1133191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1134203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1135191783Srmacklem
1136214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1137192585Srmacklem
1138191783Srmacklem	/*
1139191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1140191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1141191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1142191783Srmacklem	 *
1143191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1144191783Srmacklem	 * that we can handle until we find out otherwise.
1145191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1146191783Srmacklem	 * buffer cache using signed(!) block numbers.
1147191783Srmacklem	 */
1148191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1149191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1150191783Srmacklem	else
1151191783Srmacklem		nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1;
1152191783Srmacklem
1153191783Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1154191783Srmacklem	nmp->nm_retry = NFS_RETRANS;
1155191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1156191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1157191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1158191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1159191783Srmacklem	}
1160191783Srmacklem	nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1161191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1162191783Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1163191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1164191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1165191783Srmacklem		nmp->nm_tprintf_delay = 0;
1166191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1167191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1168191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1169191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1170191783Srmacklem	if (nmp->nm_fhsize > 0)
1171191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1172191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1173191783Srmacklem	nmp->nm_nam = nam;
1174191783Srmacklem	/* Set up the sockets and per-host congestion */
1175191783Srmacklem	nmp->nm_sotype = argp->sotype;
1176191783Srmacklem	nmp->nm_soproto = argp->proto;
1177191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1178191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1179191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1180191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1181191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1182191783Srmacklem	else
1183191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1184191783Srmacklem
1185191783Srmacklem
1186191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1187191783Srmacklem		goto bad;
1188191783Srmacklem
1189191783Srmacklem	/*
1190191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1191191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1192191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1193191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1194191783Srmacklem	 * this problem, because one can identify root inodes by their
1195191783Srmacklem	 * number == ROOTINO (2).
1196191783Srmacklem	 */
1197191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1198191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1199191783Srmacklem		/*
1200191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1201191783Srmacklem		 * path needs to be looked up.
1202191783Srmacklem		 */
1203191783Srmacklem		trycnt = 3;
1204191783Srmacklem		do {
1205191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1206191783Srmacklem			    cred, td);
1207191783Srmacklem			if (error)
1208207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1209191783Srmacklem		} while (error && --trycnt > 0);
1210191783Srmacklem		if (error) {
1211191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1212191783Srmacklem			goto bad;
1213191783Srmacklem		}
1214191783Srmacklem	}
1215191783Srmacklem	if (nmp->nm_fhsize > 0) {
1216195762Srmacklem		/*
1217195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1218195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1219195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1220195762Srmacklem		 */
1221195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1222191783Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1223191783Srmacklem		if (error)
1224191783Srmacklem			goto bad;
1225191783Srmacklem		*vpp = NFSTOV(np);
1226191783Srmacklem
1227191783Srmacklem		/*
1228191783Srmacklem		 * Get file attributes and transfer parameters for the
1229191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1230191783Srmacklem		 * (*vpp)->v_type with the correct value.
1231191783Srmacklem		 */
1232191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1233191783Srmacklem		    cred, td, &nfsva, NULL);
1234191783Srmacklem		if (ret) {
1235191783Srmacklem			/*
1236191783Srmacklem			 * Just set default values to get things going.
1237191783Srmacklem			 */
1238191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1239191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1240191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1241191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1242191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1243191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1244191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1245191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1246191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1247191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1248191783Srmacklem		}
1249191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1250191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1251191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1252191783Srmacklem
1253191783Srmacklem		/*
1254191783Srmacklem		 * Lose the lock but keep the ref.
1255191783Srmacklem		 */
1256191783Srmacklem		VOP_UNLOCK(*vpp, 0);
1257191783Srmacklem		return (0);
1258191783Srmacklem	}
1259191783Srmacklem	error = EIO;
1260191783Srmacklem
1261191783Srmacklembad:
1262191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1263191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1264191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1265191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1266191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1267191783Srmacklem	FREE(nam, M_SONAME);
1268191783Srmacklem	return (error);
1269191783Srmacklem}
1270191783Srmacklem
1271191783Srmacklem/*
1272191783Srmacklem * unmount system call
1273191783Srmacklem */
1274191783Srmacklemstatic int
1275191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1276191783Srmacklem{
1277191990Sattilio	struct thread *td;
1278191783Srmacklem	struct nfsmount *nmp;
1279191783Srmacklem	int error, flags = 0, trycnt = 0;
1280191783Srmacklem
1281191990Sattilio	td = curthread;
1282191990Sattilio
1283191783Srmacklem	if (mntflags & MNT_FORCE)
1284191783Srmacklem		flags |= FORCECLOSE;
1285191783Srmacklem	nmp = VFSTONFS(mp);
1286191783Srmacklem	/*
1287191783Srmacklem	 * Goes something like this..
1288191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1289191783Srmacklem	 * - Close the socket
1290191783Srmacklem	 * - Free up the data structures
1291191783Srmacklem	 */
1292191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1293191783Srmacklem	if (mntflags & MNT_FORCE) {
1294191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1295191783Srmacklem		if (error)
1296191783Srmacklem			goto out;
1297191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1298191783Srmacklem		nfscl_umount(nmp, td);
1299191783Srmacklem	}
1300191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1301191783Srmacklem	do {
1302191783Srmacklem		error = vflush(mp, 1, flags, td);
1303191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1304207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1305191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1306191783Srmacklem	if (error)
1307191783Srmacklem		goto out;
1308191783Srmacklem
1309191783Srmacklem	/*
1310191783Srmacklem	 * We are now committed to the unmount.
1311191783Srmacklem	 */
1312191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1313191783Srmacklem		nfscl_umount(nmp, td);
1314191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1315191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1316191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1317191783Srmacklem
1318191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1319191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1320191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1321191783Srmacklemout:
1322191783Srmacklem	return (error);
1323191783Srmacklem}
1324191783Srmacklem
1325191783Srmacklem/*
1326191783Srmacklem * Return root of a filesystem
1327191783Srmacklem */
1328191783Srmacklemstatic int
1329191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1330191783Srmacklem{
1331191783Srmacklem	struct vnode *vp;
1332191783Srmacklem	struct nfsmount *nmp;
1333191783Srmacklem	struct nfsnode *np;
1334191783Srmacklem	int error;
1335191783Srmacklem
1336191783Srmacklem	nmp = VFSTONFS(mp);
1337191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1338191783Srmacklem	if (error)
1339191783Srmacklem		return error;
1340191783Srmacklem	vp = NFSTOV(np);
1341191783Srmacklem	/*
1342191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1343191783Srmacklem	 */
1344191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1345191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1346191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1347191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1348191783Srmacklem	} else
1349191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1350191783Srmacklem	if (vp->v_type == VNON)
1351191783Srmacklem	    vp->v_type = VDIR;
1352191783Srmacklem	vp->v_vflag |= VV_ROOT;
1353191783Srmacklem	*vpp = vp;
1354191783Srmacklem	return (0);
1355191783Srmacklem}
1356191783Srmacklem
1357191783Srmacklem/*
1358191783Srmacklem * Flush out the buffer cache
1359191783Srmacklem */
1360191783Srmacklem/* ARGSUSED */
1361191783Srmacklemstatic int
1362191990Sattilionfs_sync(struct mount *mp, int waitfor)
1363191783Srmacklem{
1364191783Srmacklem	struct vnode *vp, *mvp;
1365191990Sattilio	struct thread *td;
1366191783Srmacklem	int error, allerror = 0;
1367191783Srmacklem
1368191990Sattilio	td = curthread;
1369191990Sattilio
1370191783Srmacklem	/*
1371191783Srmacklem	 * Force stale buffer cache information to be flushed.
1372191783Srmacklem	 */
1373191783Srmacklem	MNT_ILOCK(mp);
1374191783Srmacklemloop:
1375191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1376191783Srmacklem		VI_LOCK(vp);
1377191783Srmacklem		MNT_IUNLOCK(mp);
1378191783Srmacklem		/* XXX Racy bv_cnt check. */
1379191783Srmacklem		if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1380191783Srmacklem		    waitfor == MNT_LAZY) {
1381191783Srmacklem			VI_UNLOCK(vp);
1382191783Srmacklem			MNT_ILOCK(mp);
1383191783Srmacklem			continue;
1384191783Srmacklem		}
1385191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1386191783Srmacklem			MNT_ILOCK(mp);
1387191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1388191783Srmacklem			goto loop;
1389191783Srmacklem		}
1390191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1391191783Srmacklem		if (error)
1392191783Srmacklem			allerror = error;
1393191783Srmacklem		VOP_UNLOCK(vp, 0);
1394191783Srmacklem		vrele(vp);
1395191783Srmacklem
1396191783Srmacklem		MNT_ILOCK(mp);
1397191783Srmacklem	}
1398191783Srmacklem	MNT_IUNLOCK(mp);
1399191783Srmacklem	return (allerror);
1400191783Srmacklem}
1401191783Srmacklem
1402191783Srmacklemstatic int
1403191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1404191783Srmacklem{
1405191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1406191783Srmacklem	struct vfsquery vq;
1407191783Srmacklem	int error;
1408191783Srmacklem
1409191783Srmacklem	bzero(&vq, sizeof(vq));
1410191783Srmacklem	switch (op) {
1411191783Srmacklem#if 0
1412191783Srmacklem	case VFS_CTL_NOLOCKS:
1413191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1414191783Srmacklem 		if (req->oldptr != NULL) {
1415191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1416191783Srmacklem 			if (error)
1417191783Srmacklem 				return (error);
1418191783Srmacklem 		}
1419191783Srmacklem 		if (req->newptr != NULL) {
1420191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1421191783Srmacklem 			if (error)
1422191783Srmacklem 				return (error);
1423191783Srmacklem			if (val)
1424191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1425191783Srmacklem			else
1426191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1427191783Srmacklem 		}
1428191783Srmacklem		break;
1429191783Srmacklem#endif
1430191783Srmacklem	case VFS_CTL_QUERY:
1431191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1432191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1433191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1434191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1435191783Srmacklem#if 0
1436191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1437191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1438191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1439191783Srmacklem#endif
1440191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1441191783Srmacklem		break;
1442191783Srmacklem 	case VFS_CTL_TIMEO:
1443191783Srmacklem 		if (req->oldptr != NULL) {
1444191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1445191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1446191783Srmacklem 			if (error)
1447191783Srmacklem 				return (error);
1448191783Srmacklem 		}
1449191783Srmacklem 		if (req->newptr != NULL) {
1450191783Srmacklem			error = vfs_suser(mp, req->td);
1451191783Srmacklem			if (error)
1452191783Srmacklem				return (error);
1453191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1454191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1455191783Srmacklem 			if (error)
1456191783Srmacklem 				return (error);
1457191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1458191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1459191783Srmacklem 		}
1460191783Srmacklem		break;
1461191783Srmacklem	default:
1462191783Srmacklem		return (ENOTSUP);
1463191783Srmacklem	}
1464191783Srmacklem	return (0);
1465191783Srmacklem}
1466191783Srmacklem
1467214048Srmacklem/*
1468214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1469214048Srmacklem */
1470214048Srmacklemstatic void
1471214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1472216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1473216931Srmacklem    struct timeval *timeop)
1474214048Srmacklem{
1475214048Srmacklem	struct nfsmount *nmp;
1476214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1477214048Srmacklem
1478214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1479214048Srmacklem	if (fhlenp != NULL)
1480214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1481214048Srmacklem	if (fhp != NULL)
1482214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1483214048Srmacklem	if (sp != NULL)
1484214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1485214048Srmacklem	if (is_v3p != NULL)
1486214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1487214048Srmacklem	if (sizep != NULL)
1488214048Srmacklem		*sizep = np->n_size;
1489216931Srmacklem	if (timeop != NULL) {
1490216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1491216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1492216931Srmacklem	}
1493214048Srmacklem}
1494214048Srmacklem
1495