nfs_clvfsops.c revision 193066
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 193066 2009-05-29 21:27:12Z jamie $");
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,
99191783Srmacklem		    struct nfs_args *argp, struct ucred *, struct thread *);
100191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
101191783Srmacklem		    struct sockaddr *, char *, u_char *, u_char *, u_char *,
102191783Srmacklem		    struct vnode **, struct ucred *, struct thread *);
103191783Srmacklemstatic vfs_mount_t nfs_mount;
104191783Srmacklemstatic vfs_cmount_t nfs_cmount;
105191783Srmacklemstatic vfs_unmount_t nfs_unmount;
106191783Srmacklemstatic vfs_root_t nfs_root;
107191783Srmacklemstatic vfs_statfs_t nfs_statfs;
108191783Srmacklemstatic vfs_sync_t nfs_sync;
109191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
110191783Srmacklem
111191783Srmacklem/*
112191783Srmacklem * nfs vfs operations.
113191783Srmacklem */
114191783Srmacklemstatic struct vfsops nfs_vfsops = {
115191783Srmacklem	.vfs_init =		ncl_init,
116191783Srmacklem	.vfs_mount =		nfs_mount,
117191783Srmacklem	.vfs_cmount =		nfs_cmount,
118191783Srmacklem	.vfs_root =		nfs_root,
119191783Srmacklem	.vfs_statfs =		nfs_statfs,
120191783Srmacklem	.vfs_sync =		nfs_sync,
121191783Srmacklem	.vfs_uninit =		ncl_uninit,
122191783Srmacklem	.vfs_unmount =		nfs_unmount,
123191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
124191783Srmacklem};
125191783SrmacklemVFS_SET(nfs_vfsops, newnfs, VFCF_NETWORK);
126191783Srmacklem
127191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
128191783SrmacklemMODULE_VERSION(newnfs, 1);
129191783Srmacklem
130191783Srmacklem/*
131191783Srmacklem * This structure must be filled in by a primary bootstrap or bootstrap
132191783Srmacklem * server for a diskless/dataless machine. It is initialized below just
133191783Srmacklem * to ensure that it is allocated to initialized data (.data not .bss).
134191783Srmacklem */
135191783Srmacklemstruct nfs_diskless newnfs_diskless = { { { 0 } } };
136191783Srmacklemstruct nfsv3_diskless newnfsv3_diskless = { { { 0 } } };
137191783Srmacklemint newnfs_diskless_valid = 0;
138191783Srmacklem
139191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
140192145Srmacklem    &newnfs_diskless_valid, 0,
141192145Srmacklem    "Has the diskless struct been filled correctly");
142191783Srmacklem
143191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
144192145Srmacklem    newnfsv3_diskless.root_hostnam, 0, "Path to nfs root");
145191783Srmacklem
146191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
147192145Srmacklem    &newnfsv3_diskless.root_saddr, sizeof newnfsv3_diskless.root_saddr,
148192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
149191783Srmacklem
150191783Srmacklem
151191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
152191783Srmacklemstatic int	nfs_mountdiskless(char *,
153191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
154191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
155191783Srmacklemstatic void	nfs_convert_diskless(void);
156191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
157191783Srmacklem		    struct onfs_args *oargs);
158191783Srmacklem
159191783Srmacklemint
160191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
161191783Srmacklem{
162191783Srmacklem	int iosize, maxio;
163191783Srmacklem
164191783Srmacklem	/* First, set the upper limit for iosize */
165191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
166191783Srmacklem		maxio = NFS_MAXBSIZE;
167191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
168191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
169191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
170191783Srmacklem		else
171191783Srmacklem			maxio = NFS_MAXBSIZE;
172191783Srmacklem	} else {
173191783Srmacklem		maxio = NFS_V2MAXDATA;
174191783Srmacklem	}
175191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
176191783Srmacklem		nmp->nm_rsize = maxio;
177191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
178191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
179191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
180191783Srmacklem		nmp->nm_readdirsize = maxio;
181191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
182191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
183191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
184191783Srmacklem		nmp->nm_wsize = maxio;
185191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
186191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
187191783Srmacklem
188191783Srmacklem	/*
189191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
190191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
191191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
192191783Srmacklem	 * space.
193191783Srmacklem	 */
194191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
195191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
196191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
197191783Srmacklem	return (iosize);
198191783Srmacklem}
199191783Srmacklem
200191783Srmacklemstatic void
201191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
202191783Srmacklem{
203191783Srmacklem
204191783Srmacklem	args->version = NFS_ARGSVERSION;
205191783Srmacklem	args->addr = oargs->addr;
206191783Srmacklem	args->addrlen = oargs->addrlen;
207191783Srmacklem	args->sotype = oargs->sotype;
208191783Srmacklem	args->proto = oargs->proto;
209191783Srmacklem	args->fh = oargs->fh;
210191783Srmacklem	args->fhsize = oargs->fhsize;
211191783Srmacklem	args->flags = oargs->flags;
212191783Srmacklem	args->wsize = oargs->wsize;
213191783Srmacklem	args->rsize = oargs->rsize;
214191783Srmacklem	args->readdirsize = oargs->readdirsize;
215191783Srmacklem	args->timeo = oargs->timeo;
216191783Srmacklem	args->retrans = oargs->retrans;
217191783Srmacklem	args->readahead = oargs->readahead;
218191783Srmacklem	args->hostname = oargs->hostname;
219191783Srmacklem}
220191783Srmacklem
221191783Srmacklemstatic void
222191783Srmacklemnfs_convert_diskless(void)
223191783Srmacklem{
224191783Srmacklem
225191783Srmacklem	bcopy(&newnfs_diskless.myif, &newnfsv3_diskless.myif,
226192145Srmacklem	    sizeof (struct ifaliasreq));
227191783Srmacklem	bcopy(&newnfs_diskless.mygateway, &newnfsv3_diskless.mygateway,
228192145Srmacklem	    sizeof (struct sockaddr_in));
229192145Srmacklem	nfs_convert_oargs(&newnfsv3_diskless.root_args,
230192145Srmacklem	    &newnfs_diskless.root_args);
231191783Srmacklem	if (newnfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
232191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_MYFH;
233192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
234192145Srmacklem		    NFSX_MYFH);
235191783Srmacklem	} else {
236191783Srmacklem		newnfsv3_diskless.root_fhsize = NFSX_V2FH;
237192145Srmacklem		bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh,
238192145Srmacklem		    NFSX_V2FH);
239191783Srmacklem	}
240191783Srmacklem	bcopy(&newnfs_diskless.root_saddr,&newnfsv3_diskless.root_saddr,
241192145Srmacklem	    sizeof(struct sockaddr_in));
242192145Srmacklem	bcopy(newnfs_diskless.root_hostnam, newnfsv3_diskless.root_hostnam,
243192145Srmacklem	    MNAMELEN);
244191783Srmacklem	newnfsv3_diskless.root_time = newnfs_diskless.root_time;
245191783Srmacklem	bcopy(newnfs_diskless.my_hostnam, newnfsv3_diskless.my_hostnam,
246192145Srmacklem	    MAXHOSTNAMELEN);
247191783Srmacklem	newnfs_diskless_valid = 3;
248191783Srmacklem}
249191783Srmacklem
250191783Srmacklem/*
251191783Srmacklem * nfs statfs call
252191783Srmacklem */
253191783Srmacklemstatic int
254191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
255191783Srmacklem{
256191783Srmacklem	struct vnode *vp;
257191990Sattilio	struct thread *td;
258191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
259191783Srmacklem	struct nfsvattr nfsva;
260191783Srmacklem	struct nfsfsinfo fs;
261191783Srmacklem	struct nfsstatfs sb;
262191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
263191783Srmacklem	struct nfsnode *np;
264191783Srmacklem
265191990Sattilio	td = curthread;
266191990Sattilio
267191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
268191783Srmacklem	if (error)
269191783Srmacklem		return (error);
270191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
271191783Srmacklem	if (error) {
272191783Srmacklem		vfs_unbusy(mp);
273191783Srmacklem		return (error);
274191783Srmacklem	}
275191783Srmacklem	vp = NFSTOV(np);
276191783Srmacklem	mtx_lock(&nmp->nm_mtx);
277191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
278191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
279191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
280191783Srmacklem		    &attrflag, NULL);
281191783Srmacklem		if (!error)
282191783Srmacklem			gotfsinfo = 1;
283191783Srmacklem	} else
284191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
285191783Srmacklem	if (!error)
286191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
287191783Srmacklem		    &attrflag, NULL);
288191783Srmacklem	if (attrflag == 0) {
289191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
290191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
291191783Srmacklem		if (ret) {
292191783Srmacklem			/*
293191783Srmacklem			 * Just set default values to get things going.
294191783Srmacklem			 */
295191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
296191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
297191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
298191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
299191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
300191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
301191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
302191783Srmacklem			nfsva.na_vattr.va_gen = 1;
303191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
304191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
305191783Srmacklem		}
306191783Srmacklem	}
307191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
308191783Srmacklem	if (!error) {
309191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
310191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
311191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
312191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
313191783Srmacklem	    sbp->f_flags = nmp->nm_flag;
314191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
315191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
316191783Srmacklem	    if (sbp != &mp->mnt_stat) {
317191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
318191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
319191783Srmacklem	    }
320191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
321191783Srmacklem	} else if (NFS_ISV4(vp)) {
322191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
323191783Srmacklem	}
324191783Srmacklem	vput(vp);
325191783Srmacklem	vfs_unbusy(mp);
326191783Srmacklem	return (error);
327191783Srmacklem}
328191783Srmacklem
329191783Srmacklem/*
330191783Srmacklem * nfs version 3 fsinfo rpc call
331191783Srmacklem */
332191783Srmacklemint
333191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
334191783Srmacklem    struct thread *td)
335191783Srmacklem{
336191783Srmacklem	struct nfsfsinfo fs;
337191783Srmacklem	struct nfsvattr nfsva;
338191783Srmacklem	int error, attrflag;
339191783Srmacklem
340191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
341191783Srmacklem	if (!error) {
342191783Srmacklem		if (attrflag)
343191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
344191783Srmacklem			    1);
345191783Srmacklem		mtx_lock(&nmp->nm_mtx);
346191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
347191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
348191783Srmacklem	}
349191783Srmacklem	return (error);
350191783Srmacklem}
351191783Srmacklem
352191783Srmacklem/*
353191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
354191783Srmacklem * newnfs_diskless structure that has been filled in properly by some primary
355191783Srmacklem * bootstrap.
356191783Srmacklem * It goes something like this:
357191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
358191783Srmacklem *   can talk to the server
359191783Srmacklem * - If newnfs_diskless.mygateway is filled in, use that address as
360191783Srmacklem *   a default gateway.
361191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
362191783Srmacklem *
363191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
364191783Srmacklem * structure, as well as other global NFS client variables here, as
365192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
366191783Srmacklem * client activity occurs.
367191783Srmacklem */
368191783Srmacklemint
369192145Srmacklemncl_mountroot(struct mount *mp)
370191783Srmacklem{
371192145Srmacklem	struct thread *td = curthread;
372191783Srmacklem	struct nfsv3_diskless *nd = &newnfsv3_diskless;
373191783Srmacklem	struct socket *so;
374191783Srmacklem	struct vnode *vp;
375191783Srmacklem	struct ifreq ir;
376193066Sjamie	int error;
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	 */
480193066Sjamie	mtx_lock(&prison0.pr_mtx);
481193066Sjamie	strlcpy(prison0.pr_host, nd->my_hostnam, sizeof(prison0.pr_host));
482193066Sjamie	mtx_unlock(&prison0.pr_mtx);
483191783Srmacklem	inittodr(ntohl(nd->root_time));
484191783Srmacklem	return (0);
485191783Srmacklem}
486191783Srmacklem
487191783Srmacklem/*
488191783Srmacklem * Internal version of mount system call for diskless setup.
489191783Srmacklem */
490191783Srmacklemstatic int
491191783Srmacklemnfs_mountdiskless(char *path,
492191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
493191783Srmacklem    struct vnode **vpp, struct mount *mp)
494191783Srmacklem{
495191783Srmacklem	struct sockaddr *nam;
496191783Srmacklem	int error;
497191783Srmacklem
498191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
499191783Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, NULL, NULL, vpp,
500191783Srmacklem	    td->td_ucred, td)) != 0) {
501192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
502191783Srmacklem		return (error);
503191783Srmacklem	}
504191783Srmacklem	return (0);
505191783Srmacklem}
506191783Srmacklem
507191783Srmacklemstatic void
508192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
509192585Srmacklem{
510192585Srmacklem	if (!strcmp(sec, "krb5"))
511192585Srmacklem		*flagsp |= NFSMNT_KERB;
512192585Srmacklem	else if (!strcmp(sec, "krb5i"))
513192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
514192585Srmacklem	else if (!strcmp(sec, "krb5p"))
515192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
516192585Srmacklem}
517192585Srmacklem
518192585Srmacklemstatic void
519191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
520191783Srmacklem    struct ucred *cred, struct thread *td)
521191783Srmacklem{
522191783Srmacklem	int s;
523191783Srmacklem	int adjsock;
524191783Srmacklem
525191783Srmacklem	s = splnet();
526191783Srmacklem
527191783Srmacklem	/*
528191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
529191783Srmacklem	 * an update.  If this is not an update, then either the read-only
530191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
531191783Srmacklem	 * intentionally at some previous point.
532191783Srmacklem	 */
533191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
534191783Srmacklem		MNT_ILOCK(mp);
535191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
536191783Srmacklem		MNT_IUNLOCK(mp);
537191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
538191783Srmacklem		MNT_ILOCK(mp);
539191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
540191783Srmacklem		MNT_IUNLOCK(mp);
541191783Srmacklem	}
542191783Srmacklem
543191783Srmacklem	/*
544191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
545191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
546191783Srmacklem	 * and soft timeout behavior.
547191783Srmacklem	 */
548191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
549191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
550191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
551191783Srmacklem	}
552191783Srmacklem
553191783Srmacklem	/* Also clear RDIRPLUS if not NFSv3, it crashes some servers */
554191783Srmacklem	if ((argp->flags & NFSMNT_NFSV3) == 0)
555191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
556191783Srmacklem
557191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
558191783Srmacklem	adjsock = ((nmp->nm_flag & NFSMNT_NOCONN) !=
559191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
560191783Srmacklem
561191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
562191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
563191783Srmacklem	splx(s);
564191783Srmacklem
565191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
566191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
567191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
568191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
569191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
570191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
571191783Srmacklem	}
572191783Srmacklem
573191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
574191783Srmacklem		nmp->nm_retry = argp->retrans;
575191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
576191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
577191783Srmacklem	}
578191783Srmacklem
579191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
580191783Srmacklem		nmp->nm_wsize = argp->wsize;
581191783Srmacklem		/* Round down to multiple of blocksize */
582191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
583191783Srmacklem		if (nmp->nm_wsize <= 0)
584191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
585191783Srmacklem	}
586191783Srmacklem
587191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
588191783Srmacklem		nmp->nm_rsize = argp->rsize;
589191783Srmacklem		/* Round down to multiple of blocksize */
590191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
591191783Srmacklem		if (nmp->nm_rsize <= 0)
592191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
593191783Srmacklem	}
594191783Srmacklem
595191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
596191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
597191783Srmacklem	}
598191783Srmacklem
599191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
600191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
601191783Srmacklem	else
602191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
603191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
604191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
605191783Srmacklem	else
606191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
607191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
608191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
609191783Srmacklem	else
610191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
611191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
612191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
613191783Srmacklem	else
614191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
615191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
616191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
617191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
618191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
619191783Srmacklem
620191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
621191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
622191783Srmacklem			nmp->nm_readahead = argp->readahead;
623191783Srmacklem		else
624191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
625191783Srmacklem	}
626191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
627191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
628191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
629191783Srmacklem		else
630191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
631191783Srmacklem	}
632191783Srmacklem
633191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
634191783Srmacklem		    (nmp->nm_soproto != argp->proto));
635191783Srmacklem
636191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
637191783Srmacklem		int haslock = 0, error = 0;
638191783Srmacklem
639191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
640191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
641191783Srmacklem			if (!error)
642191783Srmacklem				haslock = 1;
643191783Srmacklem		}
644191783Srmacklem		if (!error) {
645191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
646191783Srmacklem		    if (haslock)
647191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
648191783Srmacklem		    nmp->nm_sotype = argp->sotype;
649191783Srmacklem		    nmp->nm_soproto = argp->proto;
650191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
651191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
652191783Srmacklem			    cred, td, 0)) {
653191783Srmacklem				printf("newnfs_args: retrying connect\n");
654191783Srmacklem				(void) nfs_catnap(PSOCK, "newnfscon");
655191783Srmacklem			}
656191783Srmacklem		}
657191783Srmacklem	} else {
658191783Srmacklem		nmp->nm_sotype = argp->sotype;
659191783Srmacklem		nmp->nm_soproto = argp->proto;
660191783Srmacklem	}
661191783Srmacklem}
662191783Srmacklem
663192585Srmacklemstatic const char *nfs_opts[] = { "from",
664191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
665191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
666192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
667192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
668192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
669192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
670192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
671191783Srmacklem    NULL };
672191783Srmacklem
673191783Srmacklem/*
674191783Srmacklem * VFS Operations.
675191783Srmacklem *
676191783Srmacklem * mount system call
677191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
678191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
679191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
680191783Srmacklem * an error after that means that I have to release the mbuf.
681191783Srmacklem */
682191783Srmacklem/* ARGSUSED */
683191783Srmacklemstatic int
684191990Sattilionfs_mount(struct mount *mp)
685191783Srmacklem{
686191783Srmacklem	struct nfs_args args = {
687191783Srmacklem	    .version = NFS_ARGSVERSION,
688191783Srmacklem	    .addr = NULL,
689191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
690191783Srmacklem	    .sotype = SOCK_STREAM,
691191783Srmacklem	    .proto = 0,
692191783Srmacklem	    .fh = NULL,
693191783Srmacklem	    .fhsize = 0,
694191783Srmacklem	    .flags = 0,
695191783Srmacklem	    .wsize = NFS_WSIZE,
696191783Srmacklem	    .rsize = NFS_RSIZE,
697191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
698191783Srmacklem	    .timeo = 10,
699191783Srmacklem	    .retrans = NFS_RETRANS,
700191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
701191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
702191783Srmacklem	    .hostname = NULL,
703191783Srmacklem	    /* args version 4 */
704191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
705191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
706191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
707191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
708191783Srmacklem	    .dirlen = 0,
709191783Srmacklem	    .krbnamelen = 0,
710192585Srmacklem	    .srvkrbnamelen = 0,
711191783Srmacklem	};
712192585Srmacklem	int error = 0, ret, len;
713192585Srmacklem	struct sockaddr *nam = NULL;
714191783Srmacklem	struct vnode *vp;
715191990Sattilio	struct thread *td;
716191783Srmacklem	char hst[MNAMELEN];
717191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
718192585Srmacklem	char *opt, *name, *secname;
719191783Srmacklem
720191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
721191783Srmacklem		error = EINVAL;
722191783Srmacklem		goto out;
723191783Srmacklem	}
724191783Srmacklem
725191990Sattilio	td = curthread;
726191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
727192145Srmacklem		error = ncl_mountroot(mp);
728191783Srmacklem		goto out;
729191783Srmacklem	}
730191783Srmacklem
731192585Srmacklem	nfscl_init();
732191783Srmacklem
733192585Srmacklem	/* Handle the new style options. */
734192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
735192585Srmacklem		args.flags |= NFSMNT_NOCONN;
736192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
737192585Srmacklem		args.flags |= NFSMNT_NOCONN;
738192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
739192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
740192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
741192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
742192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
743192585Srmacklem		args.flags |= NFSMNT_INT;
744192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
745192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
746192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
747192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
748192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
749192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
750192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
751192585Srmacklem		args.flags |= NFSMNT_SOFT;
752192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
753192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
754192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
755192585Srmacklem		args.sotype = SOCK_DGRAM;
756192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
757192585Srmacklem		args.sotype = SOCK_DGRAM;
758192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
759192585Srmacklem		args.sotype = SOCK_STREAM;
760192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
761192585Srmacklem		args.flags |= NFSMNT_NFSV3;
762192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
763192585Srmacklem		args.flags |= NFSMNT_NFSV4;
764192585Srmacklem		args.sotype = SOCK_STREAM;
765191783Srmacklem	}
766192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
767192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
768192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
769192585Srmacklem		if (opt == NULL) {
770192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
771192585Srmacklem			error = EINVAL;
772192585Srmacklem			goto out;
773192585Srmacklem		}
774192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
775192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
776192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
777192585Srmacklem			    opt);
778192585Srmacklem			error = EINVAL;
779192585Srmacklem			goto out;
780192585Srmacklem		}
781192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
782192585Srmacklem	}
783192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
784192585Srmacklem		if (opt == NULL) {
785192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
786192585Srmacklem			error = EINVAL;
787192585Srmacklem			goto out;
788192585Srmacklem		}
789192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
790192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
791192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
792192585Srmacklem			    opt);
793192585Srmacklem			error = EINVAL;
794192585Srmacklem			goto out;
795192585Srmacklem		}
796192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
797192585Srmacklem	}
798192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
799192585Srmacklem		if (opt == NULL) {
800192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
801192585Srmacklem			error = EINVAL;
802192585Srmacklem			goto out;
803192585Srmacklem		}
804192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
805192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
806192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
807192585Srmacklem			    opt);
808192585Srmacklem			error = EINVAL;
809192585Srmacklem			goto out;
810192585Srmacklem		}
811192585Srmacklem		args.flags |= NFSMNT_WSIZE;
812192585Srmacklem	}
813192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
814192585Srmacklem		if (opt == NULL) {
815192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
816192585Srmacklem			error = EINVAL;
817192585Srmacklem			goto out;
818192585Srmacklem		}
819192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
820192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
821192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
822192585Srmacklem			    opt);
823192585Srmacklem			error = EINVAL;
824192585Srmacklem			goto out;
825192585Srmacklem		}
826192585Srmacklem		args.flags |= NFSMNT_RSIZE;
827192585Srmacklem	}
828192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
829192585Srmacklem		if (opt == NULL) {
830192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
831192585Srmacklem			error = EINVAL;
832192585Srmacklem			goto out;
833192585Srmacklem		}
834192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
835192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
836192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
837192585Srmacklem			    opt);
838192585Srmacklem			error = EINVAL;
839192585Srmacklem			goto out;
840192585Srmacklem		}
841192585Srmacklem		args.flags |= NFSMNT_RETRANS;
842192585Srmacklem	}
843192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
844192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
845192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
846192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
847192585Srmacklem			    opt);
848192585Srmacklem			error = EINVAL;
849192585Srmacklem			goto out;
850192585Srmacklem		}
851192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
852192585Srmacklem	}
853192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
854192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
855192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
856192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
857192585Srmacklem			    opt);
858192585Srmacklem			error = EINVAL;
859192585Srmacklem			goto out;
860192585Srmacklem		}
861192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
862192585Srmacklem	}
863192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
864192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
865192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
866192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
867192585Srmacklem			    opt);
868192585Srmacklem			error = EINVAL;
869192585Srmacklem			goto out;
870192585Srmacklem		}
871192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
872192585Srmacklem	}
873192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
874192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
875192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
876192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
877192585Srmacklem			    opt);
878192585Srmacklem			error = EINVAL;
879192585Srmacklem			goto out;
880192585Srmacklem		}
881192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
882192585Srmacklem	}
883192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
884192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
885192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
886192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
887192585Srmacklem			    opt);
888192585Srmacklem			error = EINVAL;
889192585Srmacklem			goto out;
890192585Srmacklem		}
891192585Srmacklem		args.flags |= NFSMNT_TIMEO;
892192585Srmacklem	}
893192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
894192585Srmacklem		(void **) &secname, NULL) == 0)
895192585Srmacklem		nfs_sec_name(secname, &args.flags);
896191783Srmacklem
897191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
898191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
899191783Srmacklem
900191783Srmacklem		if (nmp == NULL) {
901191783Srmacklem			error = EIO;
902191783Srmacklem			goto out;
903191783Srmacklem		}
904191783Srmacklem		/*
905191783Srmacklem		 * When doing an update, we can't change version,
906191783Srmacklem		 * security, switch lockd strategies or change cookie
907191783Srmacklem		 * translation
908191783Srmacklem		 */
909191783Srmacklem		args.flags = (args.flags &
910191783Srmacklem		    ~(NFSMNT_NFSV3 |
911191783Srmacklem		      NFSMNT_NFSV4 |
912191783Srmacklem		      NFSMNT_KERB |
913191783Srmacklem		      NFSMNT_INTEGRITY |
914191783Srmacklem		      NFSMNT_PRIVACY |
915191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
916191783Srmacklem		    (nmp->nm_flag &
917191783Srmacklem			(NFSMNT_NFSV3 |
918191783Srmacklem			 NFSMNT_NFSV4 |
919191783Srmacklem			 NFSMNT_KERB |
920191783Srmacklem			 NFSMNT_INTEGRITY |
921191783Srmacklem			 NFSMNT_PRIVACY |
922191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
923191783Srmacklem		nfs_decode_args(mp, nmp, &args, td->td_ucred, td);
924191783Srmacklem		goto out;
925191783Srmacklem	}
926191783Srmacklem
927191783Srmacklem	/*
928191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
929191783Srmacklem	 * or no-connection mode for those protocols that support
930191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
931191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
932191783Srmacklem	 * to receive replies from a different IP then the request was
933191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
934191783Srmacklem	 * not 0.
935191783Srmacklem	 */
936191783Srmacklem	if (nfs_ip_paranoia == 0)
937191783Srmacklem		args.flags |= NFSMNT_NOCONN;
938192585Srmacklem
939192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
940192585Srmacklem	    &args.fhsize) == 0) {
941192585Srmacklem		if (args.fhsize > NFSX_FHMAX) {
942192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
943191783Srmacklem			error = EINVAL;
944191783Srmacklem			goto out;
945191783Srmacklem		}
946192585Srmacklem		bcopy(args.fh, nfh, args.fhsize);
947191783Srmacklem	} else {
948192585Srmacklem		args.fhsize = 0;
949192585Srmacklem	}
950192585Srmacklem
951192585Srmacklem	(void) vfs_getopt(mp->mnt_optnew, "hostname", (void **)&args.hostname,
952192585Srmacklem	    &len);
953192585Srmacklem	if (args.hostname == NULL) {
954192585Srmacklem		vfs_mount_error(mp, "Invalid hostname");
955192585Srmacklem		error = EINVAL;
956192585Srmacklem		goto out;
957192585Srmacklem	}
958192585Srmacklem	bcopy(args.hostname, hst, MNAMELEN);
959192585Srmacklem	hst[MNAMELEN - 1] = '\0';
960192585Srmacklem
961192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
962192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
963192585Srmacklem	else
964192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
965192585Srmacklem	args.srvkrbnamelen = strlen(srvkrbname);
966192585Srmacklem
967192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
968192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
969192585Srmacklem	else
970191783Srmacklem		krbname[0] = '\0';
971192585Srmacklem	args.krbnamelen = strlen(krbname);
972192585Srmacklem
973192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
974192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
975192585Srmacklem	else
976191783Srmacklem		dirpath[0] = '\0';
977192585Srmacklem	args.dirlen = strlen(dirpath);
978192585Srmacklem
979192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "addr", (void **)&args.addr,
980192585Srmacklem	    &args.addrlen) == 0) {
981192585Srmacklem		if (args.addrlen > SOCK_MAXADDRLEN) {
982192585Srmacklem			error = ENAMETOOLONG;
983191783Srmacklem			goto out;
984191783Srmacklem		}
985192585Srmacklem		nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
986192585Srmacklem		bcopy(args.addr, nam, args.addrlen);
987192585Srmacklem		nam->sa_len = args.addrlen;
988191783Srmacklem	}
989192585Srmacklem
990191783Srmacklem	args.fh = nfh;
991191783Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, dirpath, srvkrbname,
992191783Srmacklem	    &vp, td->td_ucred, td);
993191783Srmacklemout:
994191783Srmacklem	if (!error) {
995191783Srmacklem		MNT_ILOCK(mp);
996191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
997191783Srmacklem		MNT_IUNLOCK(mp);
998191783Srmacklem	}
999191783Srmacklem	return (error);
1000191783Srmacklem}
1001191783Srmacklem
1002191783Srmacklem
1003191783Srmacklem/*
1004191783Srmacklem * VFS Operations.
1005191783Srmacklem *
1006191783Srmacklem * mount system call
1007191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1008191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1009191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1010191783Srmacklem * an error after that means that I have to release the mbuf.
1011191783Srmacklem */
1012191783Srmacklem/* ARGSUSED */
1013191783Srmacklemstatic int
1014191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags)
1015191783Srmacklem{
1016191783Srmacklem	int error;
1017191783Srmacklem	struct nfs_args args;
1018191783Srmacklem
1019191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1020191783Srmacklem	if (error)
1021191783Srmacklem		return error;
1022191783Srmacklem
1023191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1024191783Srmacklem
1025191783Srmacklem	error = kernel_mount(ma, flags);
1026191783Srmacklem	return (error);
1027191783Srmacklem}
1028191783Srmacklem
1029191783Srmacklem/*
1030191783Srmacklem * Common code for mount and mountroot
1031191783Srmacklem */
1032191783Srmacklemstatic int
1033191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1034191783Srmacklem    char *hst, u_char *krbname, u_char *dirpath, u_char *srvkrbname,
1035191783Srmacklem    struct vnode **vpp, struct ucred *cred, struct thread *td)
1036191783Srmacklem{
1037191783Srmacklem	struct nfsmount *nmp;
1038191783Srmacklem	struct nfsnode *np;
1039191783Srmacklem	int error, trycnt, ret, clearintr;
1040191783Srmacklem	struct nfsvattr nfsva;
1041191783Srmacklem	static u_int64_t clval = 0;
1042191783Srmacklem
1043191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1044191783Srmacklem		nmp = VFSTONFS(mp);
1045191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1046191783Srmacklem		FREE(nam, M_SONAME);
1047191783Srmacklem		return (0);
1048191783Srmacklem	} else {
1049191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1050191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2,
1051191783Srmacklem		    M_NEWNFSMNT, M_WAITOK);
1052191783Srmacklem		bzero((caddr_t)nmp, sizeof (struct nfsmount) +
1053191783Srmacklem		    argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2);
1054191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1055191783Srmacklem		if (clval == 0)
1056191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1057191783Srmacklem		nmp->nm_clval = clval++;
1058191783Srmacklem		nmp->nm_krbnamelen = argp->krbnamelen;
1059191783Srmacklem		nmp->nm_dirpathlen = argp->dirlen;
1060191783Srmacklem		nmp->nm_srvkrbnamelen = argp->srvkrbnamelen;
1061192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1062191783Srmacklem			/*
1063192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1064192675Srmacklem			 * the nfsv4 state handling operations if there is
1065192675Srmacklem			 * no host based principal set. Use the uid of
1066192675Srmacklem			 * this user if not root, since they are doing the
1067192675Srmacklem			 * mount. I don't think setting this for root will
1068192675Srmacklem			 * work, since root normally does not have user
1069192675Srmacklem			 * credentials in a credentials cache.
1070191783Srmacklem			 */
1071192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1072191783Srmacklem		} else {
1073191783Srmacklem			/*
1074192675Srmacklem			 * Just set to -1, so it won't be used.
1075191783Srmacklem			 */
1076191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1077191783Srmacklem		}
1078191783Srmacklem
1079191783Srmacklem		/* Copy and null terminate all the names */
1080191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1081191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1082191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1083191783Srmacklem		}
1084191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1085191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1086191783Srmacklem			    nmp->nm_dirpathlen);
1087191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1088191783Srmacklem			    + 1] = '\0';
1089191783Srmacklem		}
1090191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1091191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1092191783Srmacklem			    nmp->nm_srvkrbnamelen);
1093191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1094191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1095191783Srmacklem		}
1096191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1097191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1098191783Srmacklem		mp->mnt_data = nmp;
1099191783Srmacklem	}
1100191783Srmacklem	vfs_getnewfsid(mp);
1101191783Srmacklem	nmp->nm_mountp = mp;
1102191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1103191783Srmacklem
1104192585Srmacklem	nfs_decode_args(mp, nmp, argp, cred, td);
1105192585Srmacklem
1106191783Srmacklem	/*
1107191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1108191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1109191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1110191783Srmacklem	 *
1111191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1112191783Srmacklem	 * that we can handle until we find out otherwise.
1113191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1114191783Srmacklem	 * buffer cache using signed(!) block numbers.
1115191783Srmacklem	 */
1116191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1117191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1118191783Srmacklem	else
1119191783Srmacklem		nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1;
1120191783Srmacklem
1121191783Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1122191783Srmacklem	nmp->nm_retry = NFS_RETRANS;
1123191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1124191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1125191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1126191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1127191783Srmacklem	}
1128191783Srmacklem	nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1129191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1130191783Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1131191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1132191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1133191783Srmacklem		nmp->nm_tprintf_delay = 0;
1134191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1135191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1136191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1137191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1138191783Srmacklem	if (nmp->nm_fhsize > 0)
1139191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1140191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1141191783Srmacklem	nmp->nm_nam = nam;
1142191783Srmacklem	/* Set up the sockets and per-host congestion */
1143191783Srmacklem	nmp->nm_sotype = argp->sotype;
1144191783Srmacklem	nmp->nm_soproto = argp->proto;
1145191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1146191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1147191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1148191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1149191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1150191783Srmacklem	else
1151191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1152191783Srmacklem
1153191783Srmacklem
1154191783Srmacklem	/*
1155191783Srmacklem	 * For Connection based sockets (TCP,...) do the connect here,
1156191783Srmacklem	 * but make it interruptible, even for non-interuptible mounts.
1157191783Srmacklem	 */
1158191783Srmacklem	if ((nmp->nm_flag & NFSMNT_INT) == 0) {
1159191783Srmacklem		nmp->nm_flag |= NFSMNT_INT;
1160191783Srmacklem		clearintr = 1;
1161191783Srmacklem	} else {
1162191783Srmacklem		clearintr = 0;
1163191783Srmacklem	}
1164191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1165191783Srmacklem		goto bad;
1166191783Srmacklem	if (clearintr)
1167191783Srmacklem		nmp->nm_flag &= ~NFSMNT_INT;
1168191783Srmacklem
1169191783Srmacklem	/*
1170191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1171191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1172191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1173191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1174191783Srmacklem	 * this problem, because one can identify root inodes by their
1175191783Srmacklem	 * number == ROOTINO (2).
1176191783Srmacklem	 */
1177191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1178191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1179191783Srmacklem		/*
1180191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1181191783Srmacklem		 * path needs to be looked up.
1182191783Srmacklem		 */
1183191783Srmacklem		trycnt = 3;
1184191783Srmacklem		do {
1185191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1186191783Srmacklem			    cred, td);
1187191783Srmacklem			if (error)
1188191783Srmacklem				(void) nfs_catnap(PZERO, "nfsgetdirp");
1189191783Srmacklem		} while (error && --trycnt > 0);
1190191783Srmacklem		if (error) {
1191191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1192191783Srmacklem			goto bad;
1193191783Srmacklem		}
1194191783Srmacklem	}
1195191783Srmacklem	if (nmp->nm_fhsize > 0) {
1196191783Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1197191783Srmacklem		if (error)
1198191783Srmacklem			goto bad;
1199191783Srmacklem		*vpp = NFSTOV(np);
1200191783Srmacklem
1201191783Srmacklem		/*
1202191783Srmacklem		 * Get file attributes and transfer parameters for the
1203191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1204191783Srmacklem		 * (*vpp)->v_type with the correct value.
1205191783Srmacklem		 */
1206191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1207191783Srmacklem		    cred, td, &nfsva, NULL);
1208191783Srmacklem		if (ret) {
1209191783Srmacklem			/*
1210191783Srmacklem			 * Just set default values to get things going.
1211191783Srmacklem			 */
1212191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1213191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1214191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1215191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1216191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1217191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1218191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1219191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1220191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1221191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1222191783Srmacklem		}
1223191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1224191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1225191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1226191783Srmacklem
1227191783Srmacklem		/*
1228191783Srmacklem		 * Lose the lock but keep the ref.
1229191783Srmacklem		 */
1230191783Srmacklem		VOP_UNLOCK(*vpp, 0);
1231191783Srmacklem		return (0);
1232191783Srmacklem	}
1233191783Srmacklem	error = EIO;
1234191783Srmacklem
1235191783Srmacklembad:
1236191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1237191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1238191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1239191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1240191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1241191783Srmacklem	FREE(nam, M_SONAME);
1242191783Srmacklem	return (error);
1243191783Srmacklem}
1244191783Srmacklem
1245191783Srmacklem/*
1246191783Srmacklem * unmount system call
1247191783Srmacklem */
1248191783Srmacklemstatic int
1249191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1250191783Srmacklem{
1251191990Sattilio	struct thread *td;
1252191783Srmacklem	struct nfsmount *nmp;
1253191783Srmacklem	int error, flags = 0, trycnt = 0;
1254191783Srmacklem
1255191990Sattilio	td = curthread;
1256191990Sattilio
1257191783Srmacklem	if (mntflags & MNT_FORCE)
1258191783Srmacklem		flags |= FORCECLOSE;
1259191783Srmacklem	nmp = VFSTONFS(mp);
1260191783Srmacklem	/*
1261191783Srmacklem	 * Goes something like this..
1262191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1263191783Srmacklem	 * - Close the socket
1264191783Srmacklem	 * - Free up the data structures
1265191783Srmacklem	 */
1266191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1267191783Srmacklem	if (mntflags & MNT_FORCE) {
1268191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1269191783Srmacklem		if (error)
1270191783Srmacklem			goto out;
1271191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1272191783Srmacklem		nfscl_umount(nmp, td);
1273191783Srmacklem	}
1274191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1275191783Srmacklem	do {
1276191783Srmacklem		error = vflush(mp, 1, flags, td);
1277191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1278191783Srmacklem			(void) nfs_catnap(PSOCK, "newndm");
1279191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1280191783Srmacklem	if (error)
1281191783Srmacklem		goto out;
1282191783Srmacklem
1283191783Srmacklem	/*
1284191783Srmacklem	 * We are now committed to the unmount.
1285191783Srmacklem	 */
1286191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1287191783Srmacklem		nfscl_umount(nmp, td);
1288191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1289191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1290191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1291191783Srmacklem
1292191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1293191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1294191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1295191783Srmacklemout:
1296191783Srmacklem	return (error);
1297191783Srmacklem}
1298191783Srmacklem
1299191783Srmacklem/*
1300191783Srmacklem * Return root of a filesystem
1301191783Srmacklem */
1302191783Srmacklemstatic int
1303191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1304191783Srmacklem{
1305191783Srmacklem	struct vnode *vp;
1306191783Srmacklem	struct nfsmount *nmp;
1307191783Srmacklem	struct nfsnode *np;
1308191783Srmacklem	int error;
1309191783Srmacklem
1310191783Srmacklem	nmp = VFSTONFS(mp);
1311191783Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np);
1312191783Srmacklem	if (error)
1313191783Srmacklem		return error;
1314191783Srmacklem	vp = NFSTOV(np);
1315191783Srmacklem	/*
1316191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1317191783Srmacklem	 */
1318191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1319191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1320191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1321191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1322191783Srmacklem	} else
1323191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1324191783Srmacklem	if (vp->v_type == VNON)
1325191783Srmacklem	    vp->v_type = VDIR;
1326191783Srmacklem	vp->v_vflag |= VV_ROOT;
1327191783Srmacklem	*vpp = vp;
1328191783Srmacklem	return (0);
1329191783Srmacklem}
1330191783Srmacklem
1331191783Srmacklem/*
1332191783Srmacklem * Flush out the buffer cache
1333191783Srmacklem */
1334191783Srmacklem/* ARGSUSED */
1335191783Srmacklemstatic int
1336191990Sattilionfs_sync(struct mount *mp, int waitfor)
1337191783Srmacklem{
1338191783Srmacklem	struct vnode *vp, *mvp;
1339191990Sattilio	struct thread *td;
1340191783Srmacklem	int error, allerror = 0;
1341191783Srmacklem
1342191990Sattilio	td = curthread;
1343191990Sattilio
1344191783Srmacklem	/*
1345191783Srmacklem	 * Force stale buffer cache information to be flushed.
1346191783Srmacklem	 */
1347191783Srmacklem	MNT_ILOCK(mp);
1348191783Srmacklemloop:
1349191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1350191783Srmacklem		VI_LOCK(vp);
1351191783Srmacklem		MNT_IUNLOCK(mp);
1352191783Srmacklem		/* XXX Racy bv_cnt check. */
1353191783Srmacklem		if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1354191783Srmacklem		    waitfor == MNT_LAZY) {
1355191783Srmacklem			VI_UNLOCK(vp);
1356191783Srmacklem			MNT_ILOCK(mp);
1357191783Srmacklem			continue;
1358191783Srmacklem		}
1359191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1360191783Srmacklem			MNT_ILOCK(mp);
1361191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1362191783Srmacklem			goto loop;
1363191783Srmacklem		}
1364191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1365191783Srmacklem		if (error)
1366191783Srmacklem			allerror = error;
1367191783Srmacklem		VOP_UNLOCK(vp, 0);
1368191783Srmacklem		vrele(vp);
1369191783Srmacklem
1370191783Srmacklem		MNT_ILOCK(mp);
1371191783Srmacklem	}
1372191783Srmacklem	MNT_IUNLOCK(mp);
1373191783Srmacklem	return (allerror);
1374191783Srmacklem}
1375191783Srmacklem
1376191783Srmacklemstatic int
1377191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1378191783Srmacklem{
1379191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1380191783Srmacklem	struct vfsquery vq;
1381191783Srmacklem	int error;
1382191783Srmacklem
1383191783Srmacklem	bzero(&vq, sizeof(vq));
1384191783Srmacklem	switch (op) {
1385191783Srmacklem#if 0
1386191783Srmacklem	case VFS_CTL_NOLOCKS:
1387191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1388191783Srmacklem 		if (req->oldptr != NULL) {
1389191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1390191783Srmacklem 			if (error)
1391191783Srmacklem 				return (error);
1392191783Srmacklem 		}
1393191783Srmacklem 		if (req->newptr != NULL) {
1394191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1395191783Srmacklem 			if (error)
1396191783Srmacklem 				return (error);
1397191783Srmacklem			if (val)
1398191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1399191783Srmacklem			else
1400191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1401191783Srmacklem 		}
1402191783Srmacklem		break;
1403191783Srmacklem#endif
1404191783Srmacklem	case VFS_CTL_QUERY:
1405191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1406191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1407191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1408191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1409191783Srmacklem#if 0
1410191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1411191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1412191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1413191783Srmacklem#endif
1414191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1415191783Srmacklem		break;
1416191783Srmacklem 	case VFS_CTL_TIMEO:
1417191783Srmacklem 		if (req->oldptr != NULL) {
1418191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1419191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1420191783Srmacklem 			if (error)
1421191783Srmacklem 				return (error);
1422191783Srmacklem 		}
1423191783Srmacklem 		if (req->newptr != NULL) {
1424191783Srmacklem			error = vfs_suser(mp, req->td);
1425191783Srmacklem			if (error)
1426191783Srmacklem				return (error);
1427191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1428191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1429191783Srmacklem 			if (error)
1430191783Srmacklem 				return (error);
1431191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1432191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1433191783Srmacklem 		}
1434191783Srmacklem		break;
1435191783Srmacklem	default:
1436191783Srmacklem		return (ENOTSUP);
1437191783Srmacklem	}
1438191783Srmacklem	return (0);
1439191783Srmacklem}
1440191783Srmacklem
1441