nfs_clvfsops.c revision 192675
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 192675 2009-05-24 03:22:49Z 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>
48191783Srmacklem#include <sys/lock.h>
49191783Srmacklem#include <sys/malloc.h>
50191783Srmacklem#include <sys/mbuf.h>
51191783Srmacklem#include <sys/module.h>
52191783Srmacklem#include <sys/mount.h>
53191783Srmacklem#include <sys/proc.h>
54191783Srmacklem#include <sys/socket.h>
55191783Srmacklem#include <sys/socketvar.h>
56191783Srmacklem#include <sys/sockio.h>
57191783Srmacklem#include <sys/sysctl.h>
58191783Srmacklem#include <sys/vnode.h>
59191783Srmacklem#include <sys/signalvar.h>
60191783Srmacklem
61191783Srmacklem#include <vm/vm.h>
62191783Srmacklem#include <vm/vm_extern.h>
63191783Srmacklem#include <vm/uma.h>
64191783Srmacklem
65191783Srmacklem#include <net/if.h>
66191783Srmacklem#include <net/route.h>
67191783Srmacklem#include <netinet/in.h>
68191783Srmacklem
69191783Srmacklem#include <fs/nfs/nfsport.h>
70191783Srmacklem#include <fs/nfsclient/nfsnode.h>
71191783Srmacklem#include <fs/nfsclient/nfsmount.h>
72191783Srmacklem#include <fs/nfsclient/nfs.h>
73191783Srmacklem#include <fs/nfsclient/nfsdiskless.h>
74191783Srmacklem
75191783Srmacklemextern int nfscl_ticks;
76191783Srmacklemextern struct timeval nfsboottime;
77191783Srmacklemextern struct nfsstats	newnfsstats;
78191783Srmacklem
79191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
80191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
81191783Srmacklem
82191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
83191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW,
84191783Srmacklem	&newnfsstats, nfsstats, "S,nfsstats");
85191783Srmacklemstatic int nfs_ip_paranoia = 1;
86191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
87191783Srmacklem    &nfs_ip_paranoia, 0, "");
88191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
89191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_INITIAL_DELAY,
90191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
91191783Srmacklem/* how long between console messages "nfs server foo not responding" */
92191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
93191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY,
94191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
95191783Srmacklem
96192585Srmacklemstatic void	nfs_sec_name(char *, int *);
97191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
98191783Srmacklem		    struct nfs_args *argp, struct ucred *, struct thread *);
99191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
100191783Srmacklem		    struct sockaddr *, char *, u_char *, u_char *, u_char *,
101191783Srmacklem		    struct vnode **, struct ucred *, struct thread *);
102191783Srmacklemstatic vfs_mount_t nfs_mount;
103191783Srmacklemstatic vfs_cmount_t nfs_cmount;
104191783Srmacklemstatic vfs_unmount_t nfs_unmount;
105191783Srmacklemstatic vfs_root_t nfs_root;
106191783Srmacklemstatic vfs_statfs_t nfs_statfs;
107191783Srmacklemstatic vfs_sync_t nfs_sync;
108191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
109191783Srmacklem
110191783Srmacklem/*
111191783Srmacklem * nfs vfs operations.
112191783Srmacklem */
113191783Srmacklemstatic struct vfsops nfs_vfsops = {
114191783Srmacklem	.vfs_init =		ncl_init,
115191783Srmacklem	.vfs_mount =		nfs_mount,
116191783Srmacklem	.vfs_cmount =		nfs_cmount,
117191783Srmacklem	.vfs_root =		nfs_root,
118191783Srmacklem	.vfs_statfs =		nfs_statfs,
119191783Srmacklem	.vfs_sync =		nfs_sync,
120191783Srmacklem	.vfs_uninit =		ncl_uninit,
121191783Srmacklem	.vfs_unmount =		nfs_unmount,
122191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
123191783Srmacklem};
124191783SrmacklemVFS_SET(nfs_vfsops, newnfs, VFCF_NETWORK);
125191783Srmacklem
126191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
127191783SrmacklemMODULE_VERSION(newnfs, 1);
128191783Srmacklem
129191783Srmacklem/*
130191783Srmacklem * This structure must be filled in by a primary bootstrap or bootstrap
131191783Srmacklem * server for a diskless/dataless machine. It is initialized below just
132191783Srmacklem * to ensure that it is allocated to initialized data (.data not .bss).
133191783Srmacklem */
134191783Srmacklemstruct nfs_diskless newnfs_diskless = { { { 0 } } };
135191783Srmacklemstruct nfsv3_diskless newnfsv3_diskless = { { { 0 } } };
136191783Srmacklemint newnfs_diskless_valid = 0;
137191783Srmacklem
138191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
139192145Srmacklem    &newnfs_diskless_valid, 0,
140192145Srmacklem    "Has the diskless struct been filled correctly");
141191783Srmacklem
142191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
143192145Srmacklem    newnfsv3_diskless.root_hostnam, 0, "Path to nfs root");
144191783Srmacklem
145191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
146192145Srmacklem    &newnfsv3_diskless.root_saddr, sizeof newnfsv3_diskless.root_saddr,
147192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
148191783Srmacklem
149191783Srmacklem
150191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
151191783Srmacklemstatic int	nfs_mountdiskless(char *,
152191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
153191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
154191783Srmacklemstatic void	nfs_convert_diskless(void);
155191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
156191783Srmacklem		    struct onfs_args *oargs);
157191783Srmacklem
158191783Srmacklemint
159191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
160191783Srmacklem{
161191783Srmacklem	int iosize, maxio;
162191783Srmacklem
163191783Srmacklem	/* First, set the upper limit for iosize */
164191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
165191783Srmacklem		maxio = NFS_MAXBSIZE;
166191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
167191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
168191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
169191783Srmacklem		else
170191783Srmacklem			maxio = NFS_MAXBSIZE;
171191783Srmacklem	} else {
172191783Srmacklem		maxio = NFS_V2MAXDATA;
173191783Srmacklem	}
174191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
175191783Srmacklem		nmp->nm_rsize = maxio;
176191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
177191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
178191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
179191783Srmacklem		nmp->nm_readdirsize = maxio;
180191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
181191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
182191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
183191783Srmacklem		nmp->nm_wsize = maxio;
184191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
185191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
186191783Srmacklem
187191783Srmacklem	/*
188191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
189191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
190191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
191191783Srmacklem	 * space.
192191783Srmacklem	 */
193191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
194191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
195191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
196191783Srmacklem	return (iosize);
197191783Srmacklem}
198191783Srmacklem
199191783Srmacklemstatic void
200191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
201191783Srmacklem{
202191783Srmacklem
203191783Srmacklem	args->version = NFS_ARGSVERSION;
204191783Srmacklem	args->addr = oargs->addr;
205191783Srmacklem	args->addrlen = oargs->addrlen;
206191783Srmacklem	args->sotype = oargs->sotype;
207191783Srmacklem	args->proto = oargs->proto;
208191783Srmacklem	args->fh = oargs->fh;
209191783Srmacklem	args->fhsize = oargs->fhsize;
210191783Srmacklem	args->flags = oargs->flags;
211191783Srmacklem	args->wsize = oargs->wsize;
212191783Srmacklem	args->rsize = oargs->rsize;
213191783Srmacklem	args->readdirsize = oargs->readdirsize;
214191783Srmacklem	args->timeo = oargs->timeo;
215191783Srmacklem	args->retrans = oargs->retrans;
216191783Srmacklem	args->readahead = oargs->readahead;
217191783Srmacklem	args->hostname = oargs->hostname;
218191783Srmacklem}
219191783Srmacklem
220191783Srmacklemstatic void
221191783Srmacklemnfs_convert_diskless(void)
222191783Srmacklem{
223191783Srmacklem
224191783Srmacklem	bcopy(&newnfs_diskless.myif, &newnfsv3_diskless.myif,
225192145Srmacklem	    sizeof (struct ifaliasreq));
226191783Srmacklem	bcopy(&newnfs_diskless.mygateway, &newnfsv3_diskless.mygateway,
227192145Srmacklem	    sizeof (struct sockaddr_in));
228192145Srmacklem	nfs_convert_oargs(&newnfsv3_diskless.root_args,
229192145Srmacklem	    &newnfs_diskless.root_args);
230191783Srmacklem	if (newnfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
231191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_MYFH;
232192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
233192145Srmacklem		    NFSX_MYFH);
234191783Srmacklem	} else {
235191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_V2FH;
236192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
237192145Srmacklem		    NFSX_V2FH);
238191783Srmacklem	}
239191783Srmacklem	bcopy(&newnfs_diskless.root_saddr,&newnfsv3_diskless.root_saddr,
240192145Srmacklem	    sizeof(struct sockaddr_in));
241192145Srmacklem	bcopy(newnfs_diskless.root_hostnam, newnfsv3_diskless.root_hostnam,
242192145Srmacklem	    MNAMELEN);
243191783Srmacklem	newnfsv3_diskless.root_time = newnfs_diskless.root_time;
244191783Srmacklem	bcopy(newnfs_diskless.my_hostnam, newnfsv3_diskless.my_hostnam,
245192145Srmacklem	    MAXHOSTNAMELEN);
246191783Srmacklem	newnfs_diskless_valid = 3;
247191783Srmacklem}
248191783Srmacklem
249191783Srmacklem/*
250191783Srmacklem * nfs statfs call
251191783Srmacklem */
252191783Srmacklemstatic int
253191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
254191783Srmacklem{
255191783Srmacklem	struct vnode *vp;
256191990Sattilio	struct thread *td;
257191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
258191783Srmacklem	struct nfsvattr nfsva;
259191783Srmacklem	struct nfsfsinfo fs;
260191783Srmacklem	struct nfsstatfs sb;
261191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
262191783Srmacklem	struct nfsnode *np;
263191783Srmacklem
264191990Sattilio	td = curthread;
265191990Sattilio
266191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
267191783Srmacklem	if (error)
268191783Srmacklem		return (error);
269191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
270191783Srmacklem	if (error) {
271191783Srmacklem		vfs_unbusy(mp);
272191783Srmacklem		return (error);
273191783Srmacklem	}
274191783Srmacklem	vp = NFSTOV(np);
275191783Srmacklem	mtx_lock(&nmp->nm_mtx);
276191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
277191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
278191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
279191783Srmacklem		    &attrflag, NULL);
280191783Srmacklem		if (!error)
281191783Srmacklem			gotfsinfo = 1;
282191783Srmacklem	} else
283191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
284191783Srmacklem	if (!error)
285191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
286191783Srmacklem		    &attrflag, NULL);
287191783Srmacklem	if (attrflag == 0) {
288191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
289191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
290191783Srmacklem		if (ret) {
291191783Srmacklem			/*
292191783Srmacklem			 * Just set default values to get things going.
293191783Srmacklem			 */
294191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
295191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
296191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
297191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
298191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
299191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
300191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
301191783Srmacklem			nfsva.na_vattr.va_gen = 1;
302191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
303191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
304191783Srmacklem		}
305191783Srmacklem	}
306191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
307191783Srmacklem	if (!error) {
308191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
309191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
310191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
311191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
312191783Srmacklem	    sbp->f_flags = nmp->nm_flag;
313191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
314191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
315191783Srmacklem	    if (sbp != &mp->mnt_stat) {
316191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
317191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
318191783Srmacklem	    }
319191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
320191783Srmacklem	} else if (NFS_ISV4(vp)) {
321191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
322191783Srmacklem	}
323191783Srmacklem	vput(vp);
324191783Srmacklem	vfs_unbusy(mp);
325191783Srmacklem	return (error);
326191783Srmacklem}
327191783Srmacklem
328191783Srmacklem/*
329191783Srmacklem * nfs version 3 fsinfo rpc call
330191783Srmacklem */
331191783Srmacklemint
332191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
333191783Srmacklem    struct thread *td)
334191783Srmacklem{
335191783Srmacklem	struct nfsfsinfo fs;
336191783Srmacklem	struct nfsvattr nfsva;
337191783Srmacklem	int error, attrflag;
338191783Srmacklem
339191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
340191783Srmacklem	if (!error) {
341191783Srmacklem		if (attrflag)
342191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
343191783Srmacklem			    1);
344191783Srmacklem		mtx_lock(&nmp->nm_mtx);
345191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
346191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
347191783Srmacklem	}
348191783Srmacklem	return (error);
349191783Srmacklem}
350191783Srmacklem
351191783Srmacklem/*
352191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
353191783Srmacklem * newnfs_diskless structure that has been filled in properly by some primary
354191783Srmacklem * bootstrap.
355191783Srmacklem * It goes something like this:
356191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
357191783Srmacklem *   can talk to the server
358191783Srmacklem * - If newnfs_diskless.mygateway is filled in, use that address as
359191783Srmacklem *   a default gateway.
360191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
361191783Srmacklem *
362191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
363191783Srmacklem * structure, as well as other global NFS client variables here, as
364192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
365191783Srmacklem * client activity occurs.
366191783Srmacklem */
367191783Srmacklemint
368192145Srmacklemncl_mountroot(struct mount *mp)
369191783Srmacklem{
370192145Srmacklem	struct thread *td = curthread;
371192145Srmacklem	INIT_VPROCG(TD_TO_VPROCG(td));
372191783Srmacklem	struct nfsv3_diskless *nd = &newnfsv3_diskless;
373191783Srmacklem	struct socket *so;
374191783Srmacklem	struct vnode *vp;
375191783Srmacklem	struct ifreq ir;
376191783Srmacklem	int error, i;
377191783Srmacklem	u_long l;
378191783Srmacklem	char buf[128];
379191783Srmacklem	char *cp;
380191783Srmacklem
381191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
382192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
383191783Srmacklem#elif defined(NFS_ROOT)
384191783Srmacklem	nfs_setup_diskless();
385191783Srmacklem#endif
386191783Srmacklem
387191783Srmacklem	if (newnfs_diskless_valid == 0)
388191783Srmacklem		return (-1);
389191783Srmacklem	if (newnfs_diskless_valid == 1)
390191783Srmacklem		nfs_convert_diskless();
391191783Srmacklem
392191783Srmacklem	/*
393191783Srmacklem	 * XXX splnet, so networks will receive...
394191783Srmacklem	 */
395191783Srmacklem	splnet();
396191783Srmacklem
397191783Srmacklem	/*
398191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
399191783Srmacklem	 * talk to the server.
400191783Srmacklem	 */
401191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
402191783Srmacklem	    td->td_ucred, td);
403191783Srmacklem	if (error)
404192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
405191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
406191783Srmacklem
407191783Srmacklem#if 0 /* XXX Bad idea */
408191783Srmacklem	/*
409191783Srmacklem	 * We might not have been told the right interface, so we pass
410191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
411191783Srmacklem	 * one of them configured.
412191783Srmacklem	 */
413191783Srmacklem
414191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
415191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
416191783Srmacklem		nd->myif.ifra_name[i] <= '9';
417191783Srmacklem		nd->myif.ifra_name[i] ++) {
418191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
419191783Srmacklem		if(!error)
420191783Srmacklem			break;
421191783Srmacklem	}
422191783Srmacklem#endif
423191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
424191783Srmacklem	if (error)
425192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
426191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
427191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
428191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
429191783Srmacklem		freeenv(cp);
430191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
431191783Srmacklem		if (error)
432192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
433191783Srmacklem	}
434191783Srmacklem	soclose(so);
435191783Srmacklem
436191783Srmacklem	/*
437191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
438191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
439191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
440191783Srmacklem	 * to avoid panicking inappropriately in that situation.
441191783Srmacklem	 */
442191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
443191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
444191783Srmacklem		struct sockaddr_in mask, sin;
445191783Srmacklem
446191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
447191783Srmacklem		sin = mask;
448191783Srmacklem		sin.sin_family = AF_INET;
449191783Srmacklem		sin.sin_len = sizeof(sin);
450192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
451191783Srmacklem		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
452191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
453191783Srmacklem		    (struct sockaddr *)&mask,
454191783Srmacklem		    RTF_UP | RTF_GATEWAY, NULL);
455191783Srmacklem		if (error)
456192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
457191783Srmacklem	}
458191783Srmacklem
459191783Srmacklem	/*
460191783Srmacklem	 * Create the rootfs mount point.
461191783Srmacklem	 */
462191783Srmacklem	nd->root_args.fh = nd->root_fh;
463191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
464191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
465191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
466191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
467191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
468191783Srmacklem	printf("NFS ROOT: %s\n", buf);
469192145Srmacklem	nd->root_args.hostname = buf;
470191783Srmacklem	if ((error = nfs_mountdiskless(buf,
471191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
472191783Srmacklem		return (error);
473191783Srmacklem	}
474191783Srmacklem
475191783Srmacklem	/*
476191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
477191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
478191783Srmacklem	 * mount the right /var based upon its preset value.
479191783Srmacklem	 */
480192145Srmacklem	mtx_lock(&hostname_mtx);
481192145Srmacklem	bcopy(nd->my_hostnam, V_hostname, MAXHOSTNAMELEN);
482192145Srmacklem	V_hostname[MAXHOSTNAMELEN - 1] = '\0';
483191783Srmacklem	for (i = 0; i < MAXHOSTNAMELEN; i++)
484192145Srmacklem		if (V_hostname[i] == '\0')
485191783Srmacklem			break;
486192145Srmacklem	mtx_unlock(&hostname_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,
504191783Srmacklem	    td->td_ucred, td)) != 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,
524191783Srmacklem    struct ucred *cred, struct thread *td)
525191783Srmacklem{
526191783Srmacklem	int s;
527191783Srmacklem	int adjsock;
528191783Srmacklem
529191783Srmacklem	s = splnet();
530191783Srmacklem
531191783Srmacklem	/*
532191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
533191783Srmacklem	 * an update.  If this is not an update, then either the read-only
534191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
535191783Srmacklem	 * intentionally at some previous point.
536191783Srmacklem	 */
537191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
538191783Srmacklem		MNT_ILOCK(mp);
539191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
540191783Srmacklem		MNT_IUNLOCK(mp);
541191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
542191783Srmacklem		MNT_ILOCK(mp);
543191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
544191783Srmacklem		MNT_IUNLOCK(mp);
545191783Srmacklem	}
546191783Srmacklem
547191783Srmacklem	/*
548191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
549191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
550191783Srmacklem	 * and soft timeout behavior.
551191783Srmacklem	 */
552191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
553191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
554191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
555191783Srmacklem	}
556191783Srmacklem
557191783Srmacklem	/* Also clear RDIRPLUS if not NFSv3, it crashes some servers */
558191783Srmacklem	if ((argp->flags & NFSMNT_NFSV3) == 0)
559191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
560191783Srmacklem
561191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
562191783Srmacklem	adjsock = ((nmp->nm_flag & NFSMNT_NOCONN) !=
563191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
564191783Srmacklem
565191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
566191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
567191783Srmacklem	splx(s);
568191783Srmacklem
569191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
570191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
571191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
572191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
573191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
574191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
575191783Srmacklem	}
576191783Srmacklem
577191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
578191783Srmacklem		nmp->nm_retry = argp->retrans;
579191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
580191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
581191783Srmacklem	}
582191783Srmacklem
583191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
584191783Srmacklem		nmp->nm_wsize = argp->wsize;
585191783Srmacklem		/* Round down to multiple of blocksize */
586191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
587191783Srmacklem		if (nmp->nm_wsize <= 0)
588191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
589191783Srmacklem	}
590191783Srmacklem
591191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
592191783Srmacklem		nmp->nm_rsize = argp->rsize;
593191783Srmacklem		/* Round down to multiple of blocksize */
594191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
595191783Srmacklem		if (nmp->nm_rsize <= 0)
596191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
597191783Srmacklem	}
598191783Srmacklem
599191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
600191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
601191783Srmacklem	}
602191783Srmacklem
603191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
604191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
605191783Srmacklem	else
606191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
607191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
608191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
609191783Srmacklem	else
610191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
611191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
612191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
613191783Srmacklem	else
614191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
615191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
616191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
617191783Srmacklem	else
618191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
619191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
620191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
621191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
622191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
623191783Srmacklem
624191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
625191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
626191783Srmacklem			nmp->nm_readahead = argp->readahead;
627191783Srmacklem		else
628191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
629191783Srmacklem	}
630191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
631191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
632191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
633191783Srmacklem		else
634191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
635191783Srmacklem	}
636191783Srmacklem
637191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
638191783Srmacklem		    (nmp->nm_soproto != argp->proto));
639191783Srmacklem
640191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
641191783Srmacklem		int haslock = 0, error = 0;
642191783Srmacklem
643191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
644191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
645191783Srmacklem			if (!error)
646191783Srmacklem				haslock = 1;
647191783Srmacklem		}
648191783Srmacklem		if (!error) {
649191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
650191783Srmacklem		    if (haslock)
651191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
652191783Srmacklem		    nmp->nm_sotype = argp->sotype;
653191783Srmacklem		    nmp->nm_soproto = argp->proto;
654191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
655191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
656191783Srmacklem			    cred, td, 0)) {
657191783Srmacklem				printf("newnfs_args: retrying connect\n");
658191783Srmacklem				(void) nfs_catnap(PSOCK, "newnfscon");
659191783Srmacklem			}
660191783Srmacklem		}
661191783Srmacklem	} else {
662191783Srmacklem		nmp->nm_sotype = argp->sotype;
663191783Srmacklem		nmp->nm_soproto = argp->proto;
664191783Srmacklem	}
665191783Srmacklem}
666191783Srmacklem
667192585Srmacklemstatic const char *nfs_opts[] = { "from",
668191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
669191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
670192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
671192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
672192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
673192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
674192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
675191783Srmacklem    NULL };
676191783Srmacklem
677191783Srmacklem/*
678191783Srmacklem * VFS Operations.
679191783Srmacklem *
680191783Srmacklem * mount system call
681191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
682191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
683191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
684191783Srmacklem * an error after that means that I have to release the mbuf.
685191783Srmacklem */
686191783Srmacklem/* ARGSUSED */
687191783Srmacklemstatic int
688191990Sattilionfs_mount(struct mount *mp)
689191783Srmacklem{
690191783Srmacklem	struct nfs_args args = {
691191783Srmacklem	    .version = NFS_ARGSVERSION,
692191783Srmacklem	    .addr = NULL,
693191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
694191783Srmacklem	    .sotype = SOCK_STREAM,
695191783Srmacklem	    .proto = 0,
696191783Srmacklem	    .fh = NULL,
697191783Srmacklem	    .fhsize = 0,
698191783Srmacklem	    .flags = 0,
699191783Srmacklem	    .wsize = NFS_WSIZE,
700191783Srmacklem	    .rsize = NFS_RSIZE,
701191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
702191783Srmacklem	    .timeo = 10,
703191783Srmacklem	    .retrans = NFS_RETRANS,
704191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
705191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
706191783Srmacklem	    .hostname = NULL,
707191783Srmacklem	    /* args version 4 */
708191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
709191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
710191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
711191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
712191783Srmacklem	    .dirlen = 0,
713191783Srmacklem	    .krbnamelen = 0,
714192585Srmacklem	    .srvkrbnamelen = 0,
715191783Srmacklem	};
716192585Srmacklem	int error = 0, ret, len;
717192585Srmacklem	struct sockaddr *nam = NULL;
718191783Srmacklem	struct vnode *vp;
719191990Sattilio	struct thread *td;
720191783Srmacklem	char hst[MNAMELEN];
721191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
722192585Srmacklem	char *opt, *name, *secname;
723191783Srmacklem
724191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
725191783Srmacklem		error = EINVAL;
726191783Srmacklem		goto out;
727191783Srmacklem	}
728191783Srmacklem
729191990Sattilio	td = curthread;
730191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
731192145Srmacklem		error = ncl_mountroot(mp);
732191783Srmacklem		goto out;
733191783Srmacklem	}
734191783Srmacklem
735192585Srmacklem	nfscl_init();
736191783Srmacklem
737192585Srmacklem	/* Handle the new style options. */
738192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
739192585Srmacklem		args.flags |= NFSMNT_NOCONN;
740192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
741192585Srmacklem		args.flags |= NFSMNT_NOCONN;
742192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
743192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
744192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
745192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
746192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
747192585Srmacklem		args.flags |= NFSMNT_INT;
748192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
749192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
750192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
751192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
752192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
753192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
754192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
755192585Srmacklem		args.flags |= NFSMNT_SOFT;
756192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
757192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
758192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
759192585Srmacklem		args.sotype = SOCK_DGRAM;
760192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
761192585Srmacklem		args.sotype = SOCK_DGRAM;
762192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
763192585Srmacklem		args.sotype = SOCK_STREAM;
764192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
765192585Srmacklem		args.flags |= NFSMNT_NFSV3;
766192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
767192585Srmacklem		args.flags |= NFSMNT_NFSV4;
768192585Srmacklem		args.sotype = SOCK_STREAM;
769191783Srmacklem	}
770192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
771192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
772192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
773192585Srmacklem		if (opt == NULL) {
774192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
775192585Srmacklem			error = EINVAL;
776192585Srmacklem			goto out;
777192585Srmacklem		}
778192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
779192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
780192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
781192585Srmacklem			    opt);
782192585Srmacklem			error = EINVAL;
783192585Srmacklem			goto out;
784192585Srmacklem		}
785192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
786192585Srmacklem	}
787192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
788192585Srmacklem		if (opt == NULL) {
789192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
790192585Srmacklem			error = EINVAL;
791192585Srmacklem			goto out;
792192585Srmacklem		}
793192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
794192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
795192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
796192585Srmacklem			    opt);
797192585Srmacklem			error = EINVAL;
798192585Srmacklem			goto out;
799192585Srmacklem		}
800192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
801192585Srmacklem	}
802192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
803192585Srmacklem		if (opt == NULL) {
804192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
805192585Srmacklem			error = EINVAL;
806192585Srmacklem			goto out;
807192585Srmacklem		}
808192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
809192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
810192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
811192585Srmacklem			    opt);
812192585Srmacklem			error = EINVAL;
813192585Srmacklem			goto out;
814192585Srmacklem		}
815192585Srmacklem		args.flags |= NFSMNT_WSIZE;
816192585Srmacklem	}
817192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
818192585Srmacklem		if (opt == NULL) {
819192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
820192585Srmacklem			error = EINVAL;
821192585Srmacklem			goto out;
822192585Srmacklem		}
823192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
824192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
825192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
826192585Srmacklem			    opt);
827192585Srmacklem			error = EINVAL;
828192585Srmacklem			goto out;
829192585Srmacklem		}
830192585Srmacklem		args.flags |= NFSMNT_RSIZE;
831192585Srmacklem	}
832192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
833192585Srmacklem		if (opt == NULL) {
834192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
835192585Srmacklem			error = EINVAL;
836192585Srmacklem			goto out;
837192585Srmacklem		}
838192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
839192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
840192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
841192585Srmacklem			    opt);
842192585Srmacklem			error = EINVAL;
843192585Srmacklem			goto out;
844192585Srmacklem		}
845192585Srmacklem		args.flags |= NFSMNT_RETRANS;
846192585Srmacklem	}
847192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
848192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
849192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
850192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
851192585Srmacklem			    opt);
852192585Srmacklem			error = EINVAL;
853192585Srmacklem			goto out;
854192585Srmacklem		}
855192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
856192585Srmacklem	}
857192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
858192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
859192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
860192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
861192585Srmacklem			    opt);
862192585Srmacklem			error = EINVAL;
863192585Srmacklem			goto out;
864192585Srmacklem		}
865192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
866192585Srmacklem	}
867192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
868192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
869192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
870192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
871192585Srmacklem			    opt);
872192585Srmacklem			error = EINVAL;
873192585Srmacklem			goto out;
874192585Srmacklem		}
875192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
876192585Srmacklem	}
877192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
878192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
879192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
880192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
881192585Srmacklem			    opt);
882192585Srmacklem			error = EINVAL;
883192585Srmacklem			goto out;
884192585Srmacklem		}
885192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
886192585Srmacklem	}
887192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
888192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
889192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
890192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
891192585Srmacklem			    opt);
892192585Srmacklem			error = EINVAL;
893192585Srmacklem			goto out;
894192585Srmacklem		}
895192585Srmacklem		args.flags |= NFSMNT_TIMEO;
896192585Srmacklem	}
897192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
898192585Srmacklem		(void **) &secname, NULL) == 0)
899192585Srmacklem		nfs_sec_name(secname, &args.flags);
900191783Srmacklem
901191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
902191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
903191783Srmacklem
904191783Srmacklem		if (nmp == NULL) {
905191783Srmacklem			error = EIO;
906191783Srmacklem			goto out;
907191783Srmacklem		}
908191783Srmacklem		/*
909191783Srmacklem		 * When doing an update, we can't change version,
910191783Srmacklem		 * security, switch lockd strategies or change cookie
911191783Srmacklem		 * translation
912191783Srmacklem		 */
913191783Srmacklem		args.flags = (args.flags &
914191783Srmacklem		    ~(NFSMNT_NFSV3 |
915191783Srmacklem		      NFSMNT_NFSV4 |
916191783Srmacklem		      NFSMNT_KERB |
917191783Srmacklem		      NFSMNT_INTEGRITY |
918191783Srmacklem		      NFSMNT_PRIVACY |
919191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
920191783Srmacklem		    (nmp->nm_flag &
921191783Srmacklem			(NFSMNT_NFSV3 |
922191783Srmacklem			 NFSMNT_NFSV4 |
923191783Srmacklem			 NFSMNT_KERB |
924191783Srmacklem			 NFSMNT_INTEGRITY |
925191783Srmacklem			 NFSMNT_PRIVACY |
926191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
927191783Srmacklem		nfs_decode_args(mp, nmp, &args, td->td_ucred, td);
928191783Srmacklem		goto out;
929191783Srmacklem	}
930191783Srmacklem
931191783Srmacklem	/*
932191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
933191783Srmacklem	 * or no-connection mode for those protocols that support
934191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
935191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
936191783Srmacklem	 * to receive replies from a different IP then the request was
937191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
938191783Srmacklem	 * not 0.
939191783Srmacklem	 */
940191783Srmacklem	if (nfs_ip_paranoia == 0)
941191783Srmacklem		args.flags |= NFSMNT_NOCONN;
942192585Srmacklem
943192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
944192585Srmacklem	    &args.fhsize) == 0) {
945192585Srmacklem		if (args.fhsize > NFSX_FHMAX) {
946192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
947191783Srmacklem			error = EINVAL;
948191783Srmacklem			goto out;
949191783Srmacklem		}
950192585Srmacklem		bcopy(args.fh, nfh, args.fhsize);
951191783Srmacklem	} else {
952192585Srmacklem		args.fhsize = 0;
953192585Srmacklem	}
954192585Srmacklem
955192585Srmacklem	(void) vfs_getopt(mp->mnt_optnew, "hostname", (void **)&args.hostname,
956192585Srmacklem	    &len);
957192585Srmacklem	if (args.hostname == NULL) {
958192585Srmacklem		vfs_mount_error(mp, "Invalid hostname");
959192585Srmacklem		error = EINVAL;
960192585Srmacklem		goto out;
961192585Srmacklem	}
962192585Srmacklem	bcopy(args.hostname, hst, MNAMELEN);
963192585Srmacklem	hst[MNAMELEN - 1] = '\0';
964192585Srmacklem
965192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
966192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
967192585Srmacklem	else
968192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
969192585Srmacklem	args.srvkrbnamelen = strlen(srvkrbname);
970192585Srmacklem
971192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
972192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
973192585Srmacklem	else
974191783Srmacklem		krbname[0] = '\0';
975192585Srmacklem	args.krbnamelen = strlen(krbname);
976192585Srmacklem
977192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
978192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
979192585Srmacklem	else
980191783Srmacklem		dirpath[0] = '\0';
981192585Srmacklem	args.dirlen = strlen(dirpath);
982192585Srmacklem
983192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "addr", (void **)&args.addr,
984192585Srmacklem	    &args.addrlen) == 0) {
985192585Srmacklem		if (args.addrlen > SOCK_MAXADDRLEN) {
986192585Srmacklem			error = ENAMETOOLONG;
987191783Srmacklem			goto out;
988191783Srmacklem		}
989192585Srmacklem		nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
990192585Srmacklem		bcopy(args.addr, nam, args.addrlen);
991192585Srmacklem		nam->sa_len = args.addrlen;
992191783Srmacklem	}
993192585Srmacklem
994191783Srmacklem	args.fh = nfh;
995191783Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, dirpath, srvkrbname,
996191783Srmacklem	    &vp, td->td_ucred, td);
997191783Srmacklemout:
998191783Srmacklem	if (!error) {
999191783Srmacklem		MNT_ILOCK(mp);
1000191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
1001191783Srmacklem		MNT_IUNLOCK(mp);
1002191783Srmacklem	}
1003191783Srmacklem	return (error);
1004191783Srmacklem}
1005191783Srmacklem
1006191783Srmacklem
1007191783Srmacklem/*
1008191783Srmacklem * VFS Operations.
1009191783Srmacklem *
1010191783Srmacklem * mount system call
1011191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1012191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1013191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1014191783Srmacklem * an error after that means that I have to release the mbuf.
1015191783Srmacklem */
1016191783Srmacklem/* ARGSUSED */
1017191783Srmacklemstatic int
1018191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags)
1019191783Srmacklem{
1020191783Srmacklem	int error;
1021191783Srmacklem	struct nfs_args args;
1022191783Srmacklem
1023191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1024191783Srmacklem	if (error)
1025191783Srmacklem		return error;
1026191783Srmacklem
1027191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1028191783Srmacklem
1029191783Srmacklem	error = kernel_mount(ma, flags);
1030191783Srmacklem	return (error);
1031191783Srmacklem}
1032191783Srmacklem
1033191783Srmacklem/*
1034191783Srmacklem * Common code for mount and mountroot
1035191783Srmacklem */
1036191783Srmacklemstatic int
1037191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1038191783Srmacklem    char *hst, u_char *krbname, u_char *dirpath, u_char *srvkrbname,
1039191783Srmacklem    struct vnode **vpp, struct ucred *cred, struct thread *td)
1040191783Srmacklem{
1041191783Srmacklem	struct nfsmount *nmp;
1042191783Srmacklem	struct nfsnode *np;
1043191783Srmacklem	int error, trycnt, ret, clearintr;
1044191783Srmacklem	struct nfsvattr nfsva;
1045191783Srmacklem	static u_int64_t clval = 0;
1046191783Srmacklem
1047191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1048191783Srmacklem		nmp = VFSTONFS(mp);
1049191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1050191783Srmacklem		FREE(nam, M_SONAME);
1051191783Srmacklem		return (0);
1052191783Srmacklem	} else {
1053191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1054191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2,
1055191783Srmacklem		    M_NEWNFSMNT, M_WAITOK);
1056191783Srmacklem		bzero((caddr_t)nmp, sizeof (struct nfsmount) +
1057191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2);
1058191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1059191783Srmacklem		if (clval == 0)
1060191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1061191783Srmacklem		nmp->nm_clval = clval++;
1062191783Srmacklem		nmp->nm_krbnamelen = argp->krbnamelen;
1063191783Srmacklem		nmp->nm_dirpathlen = argp->dirlen;
1064191783Srmacklem		nmp->nm_srvkrbnamelen = argp->srvkrbnamelen;
1065192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1066191783Srmacklem			/*
1067192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1068192675Srmacklem			 * the nfsv4 state handling operations if there is
1069192675Srmacklem			 * no host based principal set. Use the uid of
1070192675Srmacklem			 * this user if not root, since they are doing the
1071192675Srmacklem			 * mount. I don't think setting this for root will
1072192675Srmacklem			 * work, since root normally does not have user
1073192675Srmacklem			 * credentials in a credentials cache.
1074191783Srmacklem			 */
1075192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1076191783Srmacklem		} else {
1077191783Srmacklem			/*
1078192675Srmacklem			 * Just set to -1, so it won't be used.
1079191783Srmacklem			 */
1080191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1081191783Srmacklem		}
1082191783Srmacklem
1083191783Srmacklem		/* Copy and null terminate all the names */
1084191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1085191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1086191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1087191783Srmacklem		}
1088191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1089191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1090191783Srmacklem			    nmp->nm_dirpathlen);
1091191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1092191783Srmacklem			    + 1] = '\0';
1093191783Srmacklem		}
1094191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1095191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1096191783Srmacklem			    nmp->nm_srvkrbnamelen);
1097191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1098191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1099191783Srmacklem		}
1100191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1101191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1102191783Srmacklem		mp->mnt_data = nmp;
1103191783Srmacklem	}
1104191783Srmacklem	vfs_getnewfsid(mp);
1105191783Srmacklem	nmp->nm_mountp = mp;
1106191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1107191783Srmacklem
1108192585Srmacklem	nfs_decode_args(mp, nmp, argp, cred, td);
1109192585Srmacklem
1110191783Srmacklem	/*
1111191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1112191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1113191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1114191783Srmacklem	 *
1115191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1116191783Srmacklem	 * that we can handle until we find out otherwise.
1117191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1118191783Srmacklem	 * buffer cache using signed(!) block numbers.
1119191783Srmacklem	 */
1120191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1121191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1122191783Srmacklem	else
1123191783Srmacklem		nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1;
1124191783Srmacklem
1125191783Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1126191783Srmacklem	nmp->nm_retry = NFS_RETRANS;
1127191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1128191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1129191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1130191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1131191783Srmacklem	}
1132191783Srmacklem	nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1133191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1134191783Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1135191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1136191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1137191783Srmacklem		nmp->nm_tprintf_delay = 0;
1138191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1139191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1140191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1141191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1142191783Srmacklem	if (nmp->nm_fhsize > 0)
1143191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1144191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1145191783Srmacklem	nmp->nm_nam = nam;
1146191783Srmacklem	/* Set up the sockets and per-host congestion */
1147191783Srmacklem	nmp->nm_sotype = argp->sotype;
1148191783Srmacklem	nmp->nm_soproto = argp->proto;
1149191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1150191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1151191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1152191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1153191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1154191783Srmacklem	else
1155191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1156191783Srmacklem
1157191783Srmacklem
1158191783Srmacklem	/*
1159191783Srmacklem	 * For Connection based sockets (TCP,...) do the connect here,
1160191783Srmacklem	 * but make it interruptible, even for non-interuptible mounts.
1161191783Srmacklem	 */
1162191783Srmacklem	if ((nmp->nm_flag & NFSMNT_INT) == 0) {
1163191783Srmacklem		nmp->nm_flag |= NFSMNT_INT;
1164191783Srmacklem		clearintr = 1;
1165191783Srmacklem	} else {
1166191783Srmacklem		clearintr = 0;
1167191783Srmacklem	}
1168191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1169191783Srmacklem		goto bad;
1170191783Srmacklem	if (clearintr)
1171191783Srmacklem		nmp->nm_flag &= ~NFSMNT_INT;
1172191783Srmacklem
1173191783Srmacklem	/*
1174191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1175191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1176191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1177191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1178191783Srmacklem	 * this problem, because one can identify root inodes by their
1179191783Srmacklem	 * number == ROOTINO (2).
1180191783Srmacklem	 */
1181191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1182191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1183191783Srmacklem		/*
1184191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1185191783Srmacklem		 * path needs to be looked up.
1186191783Srmacklem		 */
1187191783Srmacklem		trycnt = 3;
1188191783Srmacklem		do {
1189191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1190191783Srmacklem			    cred, td);
1191191783Srmacklem			if (error)
1192191783Srmacklem				(void) nfs_catnap(PZERO, "nfsgetdirp");
1193191783Srmacklem		} while (error && --trycnt > 0);
1194191783Srmacklem		if (error) {
1195191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1196191783Srmacklem			goto bad;
1197191783Srmacklem		}
1198191783Srmacklem	}
1199191783Srmacklem	if (nmp->nm_fhsize > 0) {
1200191783Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1201191783Srmacklem		if (error)
1202191783Srmacklem			goto bad;
1203191783Srmacklem		*vpp = NFSTOV(np);
1204191783Srmacklem
1205191783Srmacklem		/*
1206191783Srmacklem		 * Get file attributes and transfer parameters for the
1207191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1208191783Srmacklem		 * (*vpp)->v_type with the correct value.
1209191783Srmacklem		 */
1210191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1211191783Srmacklem		    cred, td, &nfsva, NULL);
1212191783Srmacklem		if (ret) {
1213191783Srmacklem			/*
1214191783Srmacklem			 * Just set default values to get things going.
1215191783Srmacklem			 */
1216191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1217191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1218191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1219191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1220191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1221191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1222191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1223191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1224191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1225191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1226191783Srmacklem		}
1227191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1228191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1229191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1230191783Srmacklem
1231191783Srmacklem		/*
1232191783Srmacklem		 * Lose the lock but keep the ref.
1233191783Srmacklem		 */
1234191783Srmacklem		VOP_UNLOCK(*vpp, 0);
1235191783Srmacklem		return (0);
1236191783Srmacklem	}
1237191783Srmacklem	error = EIO;
1238191783Srmacklem
1239191783Srmacklembad:
1240191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1241191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1242191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1243191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1244191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1245191783Srmacklem	FREE(nam, M_SONAME);
1246191783Srmacklem	return (error);
1247191783Srmacklem}
1248191783Srmacklem
1249191783Srmacklem/*
1250191783Srmacklem * unmount system call
1251191783Srmacklem */
1252191783Srmacklemstatic int
1253191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1254191783Srmacklem{
1255191990Sattilio	struct thread *td;
1256191783Srmacklem	struct nfsmount *nmp;
1257191783Srmacklem	int error, flags = 0, trycnt = 0;
1258191783Srmacklem
1259191990Sattilio	td = curthread;
1260191990Sattilio
1261191783Srmacklem	if (mntflags & MNT_FORCE)
1262191783Srmacklem		flags |= FORCECLOSE;
1263191783Srmacklem	nmp = VFSTONFS(mp);
1264191783Srmacklem	/*
1265191783Srmacklem	 * Goes something like this..
1266191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1267191783Srmacklem	 * - Close the socket
1268191783Srmacklem	 * - Free up the data structures
1269191783Srmacklem	 */
1270191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1271191783Srmacklem	if (mntflags & MNT_FORCE) {
1272191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1273191783Srmacklem		if (error)
1274191783Srmacklem			goto out;
1275191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1276191783Srmacklem		nfscl_umount(nmp, td);
1277191783Srmacklem	}
1278191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1279191783Srmacklem	do {
1280191783Srmacklem		error = vflush(mp, 1, flags, td);
1281191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1282191783Srmacklem			(void) nfs_catnap(PSOCK, "newndm");
1283191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1284191783Srmacklem	if (error)
1285191783Srmacklem		goto out;
1286191783Srmacklem
1287191783Srmacklem	/*
1288191783Srmacklem	 * We are now committed to the unmount.
1289191783Srmacklem	 */
1290191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1291191783Srmacklem		nfscl_umount(nmp, td);
1292191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1293191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1294191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1295191783Srmacklem
1296191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1297191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1298191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1299191783Srmacklemout:
1300191783Srmacklem	return (error);
1301191783Srmacklem}
1302191783Srmacklem
1303191783Srmacklem/*
1304191783Srmacklem * Return root of a filesystem
1305191783Srmacklem */
1306191783Srmacklemstatic int
1307191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1308191783Srmacklem{
1309191783Srmacklem	struct vnode *vp;
1310191783Srmacklem	struct nfsmount *nmp;
1311191783Srmacklem	struct nfsnode *np;
1312191783Srmacklem	int error;
1313191783Srmacklem
1314191783Srmacklem	nmp = VFSTONFS(mp);
1315191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1316191783Srmacklem	if (error)
1317191783Srmacklem		return error;
1318191783Srmacklem	vp = NFSTOV(np);
1319191783Srmacklem	/*
1320191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1321191783Srmacklem	 */
1322191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1323191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1324191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1325191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1326191783Srmacklem	} else
1327191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1328191783Srmacklem	if (vp->v_type == VNON)
1329191783Srmacklem	    vp->v_type = VDIR;
1330191783Srmacklem	vp->v_vflag |= VV_ROOT;
1331191783Srmacklem	*vpp = vp;
1332191783Srmacklem	return (0);
1333191783Srmacklem}
1334191783Srmacklem
1335191783Srmacklem/*
1336191783Srmacklem * Flush out the buffer cache
1337191783Srmacklem */
1338191783Srmacklem/* ARGSUSED */
1339191783Srmacklemstatic int
1340191990Sattilionfs_sync(struct mount *mp, int waitfor)
1341191783Srmacklem{
1342191783Srmacklem	struct vnode *vp, *mvp;
1343191990Sattilio	struct thread *td;
1344191783Srmacklem	int error, allerror = 0;
1345191783Srmacklem
1346191990Sattilio	td = curthread;
1347191990Sattilio
1348191783Srmacklem	/*
1349191783Srmacklem	 * Force stale buffer cache information to be flushed.
1350191783Srmacklem	 */
1351191783Srmacklem	MNT_ILOCK(mp);
1352191783Srmacklemloop:
1353191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1354191783Srmacklem		VI_LOCK(vp);
1355191783Srmacklem		MNT_IUNLOCK(mp);
1356191783Srmacklem		/* XXX Racy bv_cnt check. */
1357191783Srmacklem		if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1358191783Srmacklem		    waitfor == MNT_LAZY) {
1359191783Srmacklem			VI_UNLOCK(vp);
1360191783Srmacklem			MNT_ILOCK(mp);
1361191783Srmacklem			continue;
1362191783Srmacklem		}
1363191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1364191783Srmacklem			MNT_ILOCK(mp);
1365191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1366191783Srmacklem			goto loop;
1367191783Srmacklem		}
1368191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1369191783Srmacklem		if (error)
1370191783Srmacklem			allerror = error;
1371191783Srmacklem		VOP_UNLOCK(vp, 0);
1372191783Srmacklem		vrele(vp);
1373191783Srmacklem
1374191783Srmacklem		MNT_ILOCK(mp);
1375191783Srmacklem	}
1376191783Srmacklem	MNT_IUNLOCK(mp);
1377191783Srmacklem	return (allerror);
1378191783Srmacklem}
1379191783Srmacklem
1380191783Srmacklemstatic int
1381191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1382191783Srmacklem{
1383191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1384191783Srmacklem	struct vfsquery vq;
1385191783Srmacklem	int error;
1386191783Srmacklem
1387191783Srmacklem	bzero(&vq, sizeof(vq));
1388191783Srmacklem	switch (op) {
1389191783Srmacklem#if 0
1390191783Srmacklem	case VFS_CTL_NOLOCKS:
1391191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1392191783Srmacklem 		if (req->oldptr != NULL) {
1393191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1394191783Srmacklem 			if (error)
1395191783Srmacklem 				return (error);
1396191783Srmacklem 		}
1397191783Srmacklem 		if (req->newptr != NULL) {
1398191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1399191783Srmacklem 			if (error)
1400191783Srmacklem 				return (error);
1401191783Srmacklem			if (val)
1402191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1403191783Srmacklem			else
1404191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1405191783Srmacklem 		}
1406191783Srmacklem		break;
1407191783Srmacklem#endif
1408191783Srmacklem	case VFS_CTL_QUERY:
1409191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1410191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1411191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1412191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1413191783Srmacklem#if 0
1414191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1415191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1416191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1417191783Srmacklem#endif
1418191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1419191783Srmacklem		break;
1420191783Srmacklem 	case VFS_CTL_TIMEO:
1421191783Srmacklem 		if (req->oldptr != NULL) {
1422191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1423191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1424191783Srmacklem 			if (error)
1425191783Srmacklem 				return (error);
1426191783Srmacklem 		}
1427191783Srmacklem 		if (req->newptr != NULL) {
1428191783Srmacklem			error = vfs_suser(mp, req->td);
1429191783Srmacklem			if (error)
1430191783Srmacklem				return (error);
1431191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1432191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1433191783Srmacklem 			if (error)
1434191783Srmacklem 				return (error);
1435191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1436191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1437191783Srmacklem 		}
1438191783Srmacklem		break;
1439191783Srmacklem	default:
1440191783Srmacklem		return (ENOTSUP);
1441191783Srmacklem	}
1442191783Srmacklem	return (0);
1443191783Srmacklem}
1444191783Srmacklem
1445