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