nfs_clvfsops.c revision 221066
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 221066 2011-04-26 13:50:11Z rmacklem $");
37191783Srmacklem
38191783Srmacklem
39191783Srmacklem#include "opt_bootp.h"
40191783Srmacklem#include "opt_nfsroot.h"
41191783Srmacklem
42191783Srmacklem#include <sys/param.h>
43191783Srmacklem#include <sys/systm.h>
44191783Srmacklem#include <sys/kernel.h>
45191783Srmacklem#include <sys/bio.h>
46191783Srmacklem#include <sys/buf.h>
47191783Srmacklem#include <sys/clock.h>
48193066Sjamie#include <sys/jail.h>
49220739Srmacklem#include <sys/limits.h>
50191783Srmacklem#include <sys/lock.h>
51191783Srmacklem#include <sys/malloc.h>
52191783Srmacklem#include <sys/mbuf.h>
53191783Srmacklem#include <sys/module.h>
54191783Srmacklem#include <sys/mount.h>
55191783Srmacklem#include <sys/proc.h>
56191783Srmacklem#include <sys/socket.h>
57191783Srmacklem#include <sys/socketvar.h>
58191783Srmacklem#include <sys/sockio.h>
59191783Srmacklem#include <sys/sysctl.h>
60191783Srmacklem#include <sys/vnode.h>
61191783Srmacklem#include <sys/signalvar.h>
62191783Srmacklem
63191783Srmacklem#include <vm/vm.h>
64191783Srmacklem#include <vm/vm_extern.h>
65191783Srmacklem#include <vm/uma.h>
66191783Srmacklem
67191783Srmacklem#include <net/if.h>
68191783Srmacklem#include <net/route.h>
69191783Srmacklem#include <netinet/in.h>
70191783Srmacklem
71191783Srmacklem#include <fs/nfs/nfsport.h>
72191783Srmacklem#include <fs/nfsclient/nfsnode.h>
73191783Srmacklem#include <fs/nfsclient/nfsmount.h>
74191783Srmacklem#include <fs/nfsclient/nfs.h>
75221040Srmacklem#include <nfs/nfsdiskless.h>
76191783Srmacklem
77219028SnetchildFEATURE(nfscl, "NFSv4 client");
78219028Snetchild
79191783Srmacklemextern int nfscl_ticks;
80191783Srmacklemextern struct timeval nfsboottime;
81191783Srmacklemextern struct nfsstats	newnfsstats;
82191783Srmacklem
83191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
84191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
85191783Srmacklem
86191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
87191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW,
88191783Srmacklem	&newnfsstats, nfsstats, "S,nfsstats");
89191783Srmacklemstatic int nfs_ip_paranoia = 1;
90191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
91191783Srmacklem    &nfs_ip_paranoia, 0, "");
92191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
93191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_INITIAL_DELAY,
94191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
95191783Srmacklem/* how long between console messages "nfs server foo not responding" */
96191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
97191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY,
98191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
99191783Srmacklem
100221040Srmacklemstatic int	nfs_mountroot(struct mount *);
101192585Srmacklemstatic void	nfs_sec_name(char *, int *);
102191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
103214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
104214048Srmacklem		    struct thread *);
105191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
106221014Srmacklem		    struct sockaddr *, char *, u_char *, int, u_char *, int,
107221014Srmacklem		    u_char *, int, struct vnode **, struct ucred *,
108221014Srmacklem		    struct thread *, int);
109214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
110216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
111216931Srmacklem		    struct timeval *);
112191783Srmacklemstatic vfs_mount_t nfs_mount;
113191783Srmacklemstatic vfs_cmount_t nfs_cmount;
114191783Srmacklemstatic vfs_unmount_t nfs_unmount;
115191783Srmacklemstatic vfs_root_t nfs_root;
116191783Srmacklemstatic vfs_statfs_t nfs_statfs;
117191783Srmacklemstatic vfs_sync_t nfs_sync;
118191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
119191783Srmacklem
120191783Srmacklem/*
121191783Srmacklem * nfs vfs operations.
122191783Srmacklem */
123191783Srmacklemstatic struct vfsops nfs_vfsops = {
124191783Srmacklem	.vfs_init =		ncl_init,
125191783Srmacklem	.vfs_mount =		nfs_mount,
126191783Srmacklem	.vfs_cmount =		nfs_cmount,
127191783Srmacklem	.vfs_root =		nfs_root,
128191783Srmacklem	.vfs_statfs =		nfs_statfs,
129191783Srmacklem	.vfs_sync =		nfs_sync,
130191783Srmacklem	.vfs_uninit =		ncl_uninit,
131191783Srmacklem	.vfs_unmount =		nfs_unmount,
132191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
133191783Srmacklem};
134191783SrmacklemVFS_SET(nfs_vfsops, newnfs, VFCF_NETWORK);
135191783Srmacklem
136191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
137191783SrmacklemMODULE_VERSION(newnfs, 1);
138191783Srmacklem
139191783Srmacklem/*
140221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it
141221066Srmacklem * can be shared by both NFS clients. It is declared here so that it
142221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it
143221066Srmacklem * isn't used in that case.
144191783Srmacklem */
145221066Srmacklem#if !defined(NFS_ROOT) && !defined(NFSCLIENT)
146221066Srmacklemstruct nfs_diskless	nfs_diskless = { { { 0 } } };
147221066Srmacklemstruct nfsv3_diskless	nfsv3_diskless = { { { 0 } } };
148221066Srmacklemint			nfs_diskless_valid = 0;
149221066Srmacklem#endif
150221066Srmacklem
151191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
152221040Srmacklem    &nfs_diskless_valid, 0,
153192145Srmacklem    "Has the diskless struct been filled correctly");
154191783Srmacklem
155191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
156221040Srmacklem    nfsv3_diskless.root_hostnam, 0, "Path to nfs root");
157191783Srmacklem
158191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
159221040Srmacklem    &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr),
160192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
161191783Srmacklem
162191783Srmacklem
163191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
164191783Srmacklemstatic int	nfs_mountdiskless(char *,
165191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
166191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
167191783Srmacklemstatic void	nfs_convert_diskless(void);
168191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
169191783Srmacklem		    struct onfs_args *oargs);
170191783Srmacklem
171191783Srmacklemint
172191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
173191783Srmacklem{
174191783Srmacklem	int iosize, maxio;
175191783Srmacklem
176191783Srmacklem	/* First, set the upper limit for iosize */
177191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
178191783Srmacklem		maxio = NFS_MAXBSIZE;
179191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
180191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
181191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
182191783Srmacklem		else
183191783Srmacklem			maxio = NFS_MAXBSIZE;
184191783Srmacklem	} else {
185191783Srmacklem		maxio = NFS_V2MAXDATA;
186191783Srmacklem	}
187191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
188191783Srmacklem		nmp->nm_rsize = maxio;
189191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
190191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
191191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
192191783Srmacklem		nmp->nm_readdirsize = maxio;
193191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
194191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
195191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
196191783Srmacklem		nmp->nm_wsize = maxio;
197191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
198191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
199191783Srmacklem
200191783Srmacklem	/*
201191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
202191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
203191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
204191783Srmacklem	 * space.
205191783Srmacklem	 */
206191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
207191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
208191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
209191783Srmacklem	return (iosize);
210191783Srmacklem}
211191783Srmacklem
212191783Srmacklemstatic void
213191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
214191783Srmacklem{
215191783Srmacklem
216191783Srmacklem	args->version = NFS_ARGSVERSION;
217191783Srmacklem	args->addr = oargs->addr;
218191783Srmacklem	args->addrlen = oargs->addrlen;
219191783Srmacklem	args->sotype = oargs->sotype;
220191783Srmacklem	args->proto = oargs->proto;
221191783Srmacklem	args->fh = oargs->fh;
222191783Srmacklem	args->fhsize = oargs->fhsize;
223191783Srmacklem	args->flags = oargs->flags;
224191783Srmacklem	args->wsize = oargs->wsize;
225191783Srmacklem	args->rsize = oargs->rsize;
226191783Srmacklem	args->readdirsize = oargs->readdirsize;
227191783Srmacklem	args->timeo = oargs->timeo;
228191783Srmacklem	args->retrans = oargs->retrans;
229191783Srmacklem	args->readahead = oargs->readahead;
230191783Srmacklem	args->hostname = oargs->hostname;
231191783Srmacklem}
232191783Srmacklem
233191783Srmacklemstatic void
234191783Srmacklemnfs_convert_diskless(void)
235191783Srmacklem{
236191783Srmacklem
237221040Srmacklem	bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif,
238221040Srmacklem		sizeof(struct ifaliasreq));
239221040Srmacklem	bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway,
240221040Srmacklem		sizeof(struct sockaddr_in));
241221040Srmacklem	nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args);
242221040Srmacklem	if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
243221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_MYFH;
244221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH);
245191783Srmacklem	} else {
246221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_V2FH;
247221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH);
248191783Srmacklem	}
249221040Srmacklem	bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr,
250221040Srmacklem		sizeof(struct sockaddr_in));
251221040Srmacklem	bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN);
252221040Srmacklem	nfsv3_diskless.root_time = nfs_diskless.root_time;
253221040Srmacklem	bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam,
254221040Srmacklem		MAXHOSTNAMELEN);
255221040Srmacklem	nfs_diskless_valid = 3;
256191783Srmacklem}
257191783Srmacklem
258191783Srmacklem/*
259191783Srmacklem * nfs statfs call
260191783Srmacklem */
261191783Srmacklemstatic int
262191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
263191783Srmacklem{
264191783Srmacklem	struct vnode *vp;
265191990Sattilio	struct thread *td;
266191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
267191783Srmacklem	struct nfsvattr nfsva;
268191783Srmacklem	struct nfsfsinfo fs;
269191783Srmacklem	struct nfsstatfs sb;
270191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
271191783Srmacklem	struct nfsnode *np;
272191783Srmacklem
273191990Sattilio	td = curthread;
274191990Sattilio
275191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
276191783Srmacklem	if (error)
277191783Srmacklem		return (error);
278220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
279191783Srmacklem	if (error) {
280191783Srmacklem		vfs_unbusy(mp);
281191783Srmacklem		return (error);
282191783Srmacklem	}
283191783Srmacklem	vp = NFSTOV(np);
284191783Srmacklem	mtx_lock(&nmp->nm_mtx);
285191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
286191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
287191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
288191783Srmacklem		    &attrflag, NULL);
289191783Srmacklem		if (!error)
290191783Srmacklem			gotfsinfo = 1;
291191783Srmacklem	} else
292191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
293191783Srmacklem	if (!error)
294191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
295191783Srmacklem		    &attrflag, NULL);
296191783Srmacklem	if (attrflag == 0) {
297191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
298191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
299191783Srmacklem		if (ret) {
300191783Srmacklem			/*
301191783Srmacklem			 * Just set default values to get things going.
302191783Srmacklem			 */
303191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
304191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
305191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
306191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
307191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
308191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
309191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
310191783Srmacklem			nfsva.na_vattr.va_gen = 1;
311191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
312191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
313191783Srmacklem		}
314191783Srmacklem	}
315191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
316191783Srmacklem	if (!error) {
317191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
318191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
319191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
320191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
321191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
322191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
323191783Srmacklem	    if (sbp != &mp->mnt_stat) {
324191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
325191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
326191783Srmacklem	    }
327191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
328191783Srmacklem	} else if (NFS_ISV4(vp)) {
329191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
330191783Srmacklem	}
331191783Srmacklem	vput(vp);
332191783Srmacklem	vfs_unbusy(mp);
333191783Srmacklem	return (error);
334191783Srmacklem}
335191783Srmacklem
336191783Srmacklem/*
337191783Srmacklem * nfs version 3 fsinfo rpc call
338191783Srmacklem */
339191783Srmacklemint
340191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
341191783Srmacklem    struct thread *td)
342191783Srmacklem{
343191783Srmacklem	struct nfsfsinfo fs;
344191783Srmacklem	struct nfsvattr nfsva;
345191783Srmacklem	int error, attrflag;
346191783Srmacklem
347191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
348191783Srmacklem	if (!error) {
349191783Srmacklem		if (attrflag)
350191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
351191783Srmacklem			    1);
352191783Srmacklem		mtx_lock(&nmp->nm_mtx);
353191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
354191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
355191783Srmacklem	}
356191783Srmacklem	return (error);
357191783Srmacklem}
358191783Srmacklem
359191783Srmacklem/*
360191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
361221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary
362191783Srmacklem * bootstrap.
363191783Srmacklem * It goes something like this:
364191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
365191783Srmacklem *   can talk to the server
366221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as
367191783Srmacklem *   a default gateway.
368191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
369191783Srmacklem *
370191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
371191783Srmacklem * structure, as well as other global NFS client variables here, as
372192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
373191783Srmacklem * client activity occurs.
374191783Srmacklem */
375221040Srmacklemstatic int
376221040Srmacklemnfs_mountroot(struct mount *mp)
377191783Srmacklem{
378192145Srmacklem	struct thread *td = curthread;
379221040Srmacklem	struct nfsv3_diskless *nd = &nfsv3_diskless;
380191783Srmacklem	struct socket *so;
381191783Srmacklem	struct vnode *vp;
382191783Srmacklem	struct ifreq ir;
383193066Sjamie	int error;
384191783Srmacklem	u_long l;
385191783Srmacklem	char buf[128];
386191783Srmacklem	char *cp;
387191783Srmacklem
388191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
389192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
390191783Srmacklem#elif defined(NFS_ROOT)
391191783Srmacklem	nfs_setup_diskless();
392191783Srmacklem#endif
393191783Srmacklem
394221040Srmacklem	if (nfs_diskless_valid == 0)
395191783Srmacklem		return (-1);
396221040Srmacklem	if (nfs_diskless_valid == 1)
397191783Srmacklem		nfs_convert_diskless();
398191783Srmacklem
399191783Srmacklem	/*
400191783Srmacklem	 * XXX splnet, so networks will receive...
401191783Srmacklem	 */
402191783Srmacklem	splnet();
403191783Srmacklem
404191783Srmacklem	/*
405191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
406191783Srmacklem	 * talk to the server.
407191783Srmacklem	 */
408191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
409191783Srmacklem	    td->td_ucred, td);
410191783Srmacklem	if (error)
411192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
412191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
413191783Srmacklem
414191783Srmacklem#if 0 /* XXX Bad idea */
415191783Srmacklem	/*
416191783Srmacklem	 * We might not have been told the right interface, so we pass
417191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
418191783Srmacklem	 * one of them configured.
419191783Srmacklem	 */
420191783Srmacklem
421191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
422191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
423191783Srmacklem		nd->myif.ifra_name[i] <= '9';
424191783Srmacklem		nd->myif.ifra_name[i] ++) {
425191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
426191783Srmacklem		if(!error)
427191783Srmacklem			break;
428191783Srmacklem	}
429191783Srmacklem#endif
430191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
431191783Srmacklem	if (error)
432192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
433191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
434191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
435191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
436191783Srmacklem		freeenv(cp);
437191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
438191783Srmacklem		if (error)
439192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
440191783Srmacklem	}
441191783Srmacklem	soclose(so);
442191783Srmacklem
443191783Srmacklem	/*
444191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
445191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
446191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
447191783Srmacklem	 * to avoid panicking inappropriately in that situation.
448191783Srmacklem	 */
449191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
450191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
451191783Srmacklem		struct sockaddr_in mask, sin;
452191783Srmacklem
453191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
454191783Srmacklem		sin = mask;
455191783Srmacklem		sin.sin_family = AF_INET;
456191783Srmacklem		sin.sin_len = sizeof(sin);
457192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
458218757Sbz		CURVNET_SET(TD_TO_VNET(td));
459191783Srmacklem		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
460191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
461191783Srmacklem		    (struct sockaddr *)&mask,
462191783Srmacklem		    RTF_UP | RTF_GATEWAY, NULL);
463218757Sbz		CURVNET_RESTORE();
464191783Srmacklem		if (error)
465192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
466191783Srmacklem	}
467191783Srmacklem
468191783Srmacklem	/*
469191783Srmacklem	 * Create the rootfs mount point.
470191783Srmacklem	 */
471191783Srmacklem	nd->root_args.fh = nd->root_fh;
472191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
473191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
474191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
475191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
476191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
477191783Srmacklem	printf("NFS ROOT: %s\n", buf);
478192145Srmacklem	nd->root_args.hostname = buf;
479191783Srmacklem	if ((error = nfs_mountdiskless(buf,
480191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
481191783Srmacklem		return (error);
482191783Srmacklem	}
483191783Srmacklem
484191783Srmacklem	/*
485191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
486191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
487191783Srmacklem	 * mount the right /var based upon its preset value.
488191783Srmacklem	 */
489193066Sjamie	mtx_lock(&prison0.pr_mtx);
490194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
491194118Sjamie	    sizeof(prison0.pr_hostname));
492193066Sjamie	mtx_unlock(&prison0.pr_mtx);
493191783Srmacklem	inittodr(ntohl(nd->root_time));
494191783Srmacklem	return (0);
495191783Srmacklem}
496191783Srmacklem
497191783Srmacklem/*
498191783Srmacklem * Internal version of mount system call for diskless setup.
499191783Srmacklem */
500191783Srmacklemstatic int
501191783Srmacklemnfs_mountdiskless(char *path,
502191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
503191783Srmacklem    struct vnode **vpp, struct mount *mp)
504191783Srmacklem{
505191783Srmacklem	struct sockaddr *nam;
506221014Srmacklem	int dirlen, error;
507221014Srmacklem	char *dirpath;
508191783Srmacklem
509221014Srmacklem	/*
510221014Srmacklem	 * Find the directory path in "path", which also has the server's
511221014Srmacklem	 * name/ip address in it.
512221014Srmacklem	 */
513221014Srmacklem	dirpath = strchr(path, ':');
514221014Srmacklem	if (dirpath != NULL)
515221014Srmacklem		dirlen = strlen(++dirpath);
516221014Srmacklem	else
517221014Srmacklem		dirlen = 0;
518191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
519221014Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
520221014Srmacklem	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
521192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
522191783Srmacklem		return (error);
523191783Srmacklem	}
524191783Srmacklem	return (0);
525191783Srmacklem}
526191783Srmacklem
527191783Srmacklemstatic void
528192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
529192585Srmacklem{
530192585Srmacklem	if (!strcmp(sec, "krb5"))
531192585Srmacklem		*flagsp |= NFSMNT_KERB;
532192585Srmacklem	else if (!strcmp(sec, "krb5i"))
533192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
534192585Srmacklem	else if (!strcmp(sec, "krb5p"))
535192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
536192585Srmacklem}
537192585Srmacklem
538192585Srmacklemstatic void
539191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
540214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
541191783Srmacklem{
542191783Srmacklem	int s;
543191783Srmacklem	int adjsock;
544214048Srmacklem	char *p;
545191783Srmacklem
546191783Srmacklem	s = splnet();
547191783Srmacklem
548191783Srmacklem	/*
549191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
550191783Srmacklem	 * an update.  If this is not an update, then either the read-only
551191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
552191783Srmacklem	 * intentionally at some previous point.
553191783Srmacklem	 */
554191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
555191783Srmacklem		MNT_ILOCK(mp);
556191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
557191783Srmacklem		MNT_IUNLOCK(mp);
558191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
559191783Srmacklem		MNT_ILOCK(mp);
560191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
561191783Srmacklem		MNT_IUNLOCK(mp);
562191783Srmacklem	}
563191783Srmacklem
564191783Srmacklem	/*
565191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
566191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
567191783Srmacklem	 * and soft timeout behavior.
568191783Srmacklem	 */
569191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
570191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
571191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
572220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
573220739Srmacklem			nmp->nm_retry = INT_MAX;
574220739Srmacklem		else
575220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
576191783Srmacklem	}
577191783Srmacklem
578220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
579220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
580220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
581191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
582220739Srmacklem	}
583191783Srmacklem
584220739Srmacklem	/* Clear NFSMNT_RESVPORT for NFSv4, since it is not required. */
585220739Srmacklem	if ((argp->flags & NFSMNT_NFSV4) != 0) {
586220739Srmacklem		argp->flags &= ~NFSMNT_RESVPORT;
587220739Srmacklem		nmp->nm_flag &= ~NFSMNT_RESVPORT;
588220739Srmacklem	}
589220739Srmacklem
590220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
591220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
592220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
593191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
594220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
595191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
596191783Srmacklem
597191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
598191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
599191783Srmacklem	splx(s);
600191783Srmacklem
601191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
602191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
603191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
604191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
605191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
606191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
607191783Srmacklem	}
608191783Srmacklem
609191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
610191783Srmacklem		nmp->nm_retry = argp->retrans;
611191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
612191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
613191783Srmacklem	}
614191783Srmacklem
615191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
616191783Srmacklem		nmp->nm_wsize = argp->wsize;
617191783Srmacklem		/* Round down to multiple of blocksize */
618191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
619191783Srmacklem		if (nmp->nm_wsize <= 0)
620191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
621191783Srmacklem	}
622191783Srmacklem
623191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
624191783Srmacklem		nmp->nm_rsize = argp->rsize;
625191783Srmacklem		/* Round down to multiple of blocksize */
626191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
627191783Srmacklem		if (nmp->nm_rsize <= 0)
628191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
629191783Srmacklem	}
630191783Srmacklem
631191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
632191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
633191783Srmacklem	}
634191783Srmacklem
635191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
636191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
637191783Srmacklem	else
638191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
639191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
640191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
641191783Srmacklem	else
642191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
643191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
644191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
645191783Srmacklem	else
646191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
647191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
648191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
649191783Srmacklem	else
650191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
651191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
652191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
653191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
654191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
655191783Srmacklem
656191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
657191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
658191783Srmacklem			nmp->nm_readahead = argp->readahead;
659191783Srmacklem		else
660191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
661191783Srmacklem	}
662191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
663191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
664191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
665191783Srmacklem		else
666191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
667191783Srmacklem	}
668191783Srmacklem
669191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
670191783Srmacklem		    (nmp->nm_soproto != argp->proto));
671191783Srmacklem
672191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
673191783Srmacklem		int haslock = 0, error = 0;
674191783Srmacklem
675191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
676191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
677191783Srmacklem			if (!error)
678191783Srmacklem				haslock = 1;
679191783Srmacklem		}
680191783Srmacklem		if (!error) {
681191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
682191783Srmacklem		    if (haslock)
683191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
684191783Srmacklem		    nmp->nm_sotype = argp->sotype;
685191783Srmacklem		    nmp->nm_soproto = argp->proto;
686191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
687191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
688191783Srmacklem			    cred, td, 0)) {
689191783Srmacklem				printf("newnfs_args: retrying connect\n");
690207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
691191783Srmacklem			}
692191783Srmacklem		}
693191783Srmacklem	} else {
694191783Srmacklem		nmp->nm_sotype = argp->sotype;
695191783Srmacklem		nmp->nm_soproto = argp->proto;
696191783Srmacklem	}
697214048Srmacklem
698214048Srmacklem	if (hostname != NULL) {
699214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
700214048Srmacklem		    sizeof(nmp->nm_hostname));
701214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
702214048Srmacklem		if (p != NULL)
703214048Srmacklem			*p = '\0';
704214048Srmacklem	}
705191783Srmacklem}
706191783Srmacklem
707192585Srmacklemstatic const char *nfs_opts[] = { "from",
708191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
709191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
710192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
711192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
712192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
713192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
714192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
715203303Srmacklem    "negnametimeo",
716191783Srmacklem    NULL };
717191783Srmacklem
718191783Srmacklem/*
719191783Srmacklem * VFS Operations.
720191783Srmacklem *
721191783Srmacklem * mount system call
722191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
723191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
724191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
725191783Srmacklem * an error after that means that I have to release the mbuf.
726191783Srmacklem */
727191783Srmacklem/* ARGSUSED */
728191783Srmacklemstatic int
729191990Sattilionfs_mount(struct mount *mp)
730191783Srmacklem{
731191783Srmacklem	struct nfs_args args = {
732191783Srmacklem	    .version = NFS_ARGSVERSION,
733191783Srmacklem	    .addr = NULL,
734191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
735191783Srmacklem	    .sotype = SOCK_STREAM,
736191783Srmacklem	    .proto = 0,
737191783Srmacklem	    .fh = NULL,
738191783Srmacklem	    .fhsize = 0,
739220739Srmacklem	    .flags = NFSMNT_RESVPORT,
740191783Srmacklem	    .wsize = NFS_WSIZE,
741191783Srmacklem	    .rsize = NFS_RSIZE,
742191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
743191783Srmacklem	    .timeo = 10,
744191783Srmacklem	    .retrans = NFS_RETRANS,
745191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
746191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
747191783Srmacklem	    .hostname = NULL,
748191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
749191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
750191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
751191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
752191783Srmacklem	};
753192585Srmacklem	int error = 0, ret, len;
754192585Srmacklem	struct sockaddr *nam = NULL;
755191783Srmacklem	struct vnode *vp;
756191990Sattilio	struct thread *td;
757191783Srmacklem	char hst[MNAMELEN];
758191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
759192585Srmacklem	char *opt, *name, *secname;
760203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
761221014Srmacklem	int dirlen, krbnamelen, srvkrbnamelen;
762191783Srmacklem
763191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
764191783Srmacklem		error = EINVAL;
765191783Srmacklem		goto out;
766191783Srmacklem	}
767191783Srmacklem
768191990Sattilio	td = curthread;
769191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
770221040Srmacklem		error = nfs_mountroot(mp);
771191783Srmacklem		goto out;
772191783Srmacklem	}
773191783Srmacklem
774192585Srmacklem	nfscl_init();
775191783Srmacklem
776192585Srmacklem	/* Handle the new style options. */
777192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
778192585Srmacklem		args.flags |= NFSMNT_NOCONN;
779192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
780192585Srmacklem		args.flags |= NFSMNT_NOCONN;
781192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
782192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
783192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
784192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
785192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
786192585Srmacklem		args.flags |= NFSMNT_INT;
787192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
788192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
789192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
790192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
791192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
792192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
793192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
794192585Srmacklem		args.flags |= NFSMNT_SOFT;
795192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
796192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
797192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
798192585Srmacklem		args.sotype = SOCK_DGRAM;
799192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
800192585Srmacklem		args.sotype = SOCK_DGRAM;
801192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
802192585Srmacklem		args.sotype = SOCK_STREAM;
803192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
804192585Srmacklem		args.flags |= NFSMNT_NFSV3;
805192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
806192585Srmacklem		args.flags |= NFSMNT_NFSV4;
807192585Srmacklem		args.sotype = SOCK_STREAM;
808191783Srmacklem	}
809192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
810192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
811192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
812192585Srmacklem		if (opt == NULL) {
813192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
814192585Srmacklem			error = EINVAL;
815192585Srmacklem			goto out;
816192585Srmacklem		}
817192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
818192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
819192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
820192585Srmacklem			    opt);
821192585Srmacklem			error = EINVAL;
822192585Srmacklem			goto out;
823192585Srmacklem		}
824192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
825192585Srmacklem	}
826192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
827192585Srmacklem		if (opt == NULL) {
828192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
829192585Srmacklem			error = EINVAL;
830192585Srmacklem			goto out;
831192585Srmacklem		}
832192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
833192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
834192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
835192585Srmacklem			    opt);
836192585Srmacklem			error = EINVAL;
837192585Srmacklem			goto out;
838192585Srmacklem		}
839192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
840192585Srmacklem	}
841192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
842192585Srmacklem		if (opt == NULL) {
843192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
844192585Srmacklem			error = EINVAL;
845192585Srmacklem			goto out;
846192585Srmacklem		}
847192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
848192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
849192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
850192585Srmacklem			    opt);
851192585Srmacklem			error = EINVAL;
852192585Srmacklem			goto out;
853192585Srmacklem		}
854192585Srmacklem		args.flags |= NFSMNT_WSIZE;
855192585Srmacklem	}
856192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
857192585Srmacklem		if (opt == NULL) {
858192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
859192585Srmacklem			error = EINVAL;
860192585Srmacklem			goto out;
861192585Srmacklem		}
862192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
863192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
864192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
865192585Srmacklem			    opt);
866192585Srmacklem			error = EINVAL;
867192585Srmacklem			goto out;
868192585Srmacklem		}
869192585Srmacklem		args.flags |= NFSMNT_RSIZE;
870192585Srmacklem	}
871192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
872192585Srmacklem		if (opt == NULL) {
873192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
874192585Srmacklem			error = EINVAL;
875192585Srmacklem			goto out;
876192585Srmacklem		}
877192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
878192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
879192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
880192585Srmacklem			    opt);
881192585Srmacklem			error = EINVAL;
882192585Srmacklem			goto out;
883192585Srmacklem		}
884192585Srmacklem		args.flags |= NFSMNT_RETRANS;
885192585Srmacklem	}
886192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
887192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
888192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
889192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
890192585Srmacklem			    opt);
891192585Srmacklem			error = EINVAL;
892192585Srmacklem			goto out;
893192585Srmacklem		}
894192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
895192585Srmacklem	}
896192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
897192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
898192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
899192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
900192585Srmacklem			    opt);
901192585Srmacklem			error = EINVAL;
902192585Srmacklem			goto out;
903192585Srmacklem		}
904192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
905192585Srmacklem	}
906192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
907192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
908192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
909192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
910192585Srmacklem			    opt);
911192585Srmacklem			error = EINVAL;
912192585Srmacklem			goto out;
913192585Srmacklem		}
914192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
915192585Srmacklem	}
916192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
917192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
918192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
919192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
920192585Srmacklem			    opt);
921192585Srmacklem			error = EINVAL;
922192585Srmacklem			goto out;
923192585Srmacklem		}
924192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
925192585Srmacklem	}
926192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
927192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
928192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
929192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
930192585Srmacklem			    opt);
931192585Srmacklem			error = EINVAL;
932192585Srmacklem			goto out;
933192585Srmacklem		}
934192585Srmacklem		args.flags |= NFSMNT_TIMEO;
935192585Srmacklem	}
936203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
937203303Srmacklem	    == 0) {
938203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
939203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
940203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
941203303Srmacklem			    opt);
942203303Srmacklem			error = EINVAL;
943203303Srmacklem			goto out;
944203303Srmacklem		}
945203303Srmacklem	}
946192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
947192585Srmacklem		(void **) &secname, NULL) == 0)
948192585Srmacklem		nfs_sec_name(secname, &args.flags);
949191783Srmacklem
950191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
951191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
952191783Srmacklem
953191783Srmacklem		if (nmp == NULL) {
954191783Srmacklem			error = EIO;
955191783Srmacklem			goto out;
956191783Srmacklem		}
957191783Srmacklem		/*
958191783Srmacklem		 * When doing an update, we can't change version,
959191783Srmacklem		 * security, switch lockd strategies or change cookie
960191783Srmacklem		 * translation
961191783Srmacklem		 */
962191783Srmacklem		args.flags = (args.flags &
963191783Srmacklem		    ~(NFSMNT_NFSV3 |
964191783Srmacklem		      NFSMNT_NFSV4 |
965191783Srmacklem		      NFSMNT_KERB |
966191783Srmacklem		      NFSMNT_INTEGRITY |
967191783Srmacklem		      NFSMNT_PRIVACY |
968191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
969191783Srmacklem		    (nmp->nm_flag &
970191783Srmacklem			(NFSMNT_NFSV3 |
971191783Srmacklem			 NFSMNT_NFSV4 |
972191783Srmacklem			 NFSMNT_KERB |
973191783Srmacklem			 NFSMNT_INTEGRITY |
974191783Srmacklem			 NFSMNT_PRIVACY |
975191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
976214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
977191783Srmacklem		goto out;
978191783Srmacklem	}
979191783Srmacklem
980191783Srmacklem	/*
981191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
982191783Srmacklem	 * or no-connection mode for those protocols that support
983191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
984191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
985191783Srmacklem	 * to receive replies from a different IP then the request was
986191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
987191783Srmacklem	 * not 0.
988191783Srmacklem	 */
989191783Srmacklem	if (nfs_ip_paranoia == 0)
990191783Srmacklem		args.flags |= NFSMNT_NOCONN;
991192585Srmacklem
992192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
993192585Srmacklem	    &args.fhsize) == 0) {
994208234Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
995192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
996191783Srmacklem			error = EINVAL;
997191783Srmacklem			goto out;
998191783Srmacklem		}
999192585Srmacklem		bcopy(args.fh, nfh, args.fhsize);
1000191783Srmacklem	} else {
1001192585Srmacklem		args.fhsize = 0;
1002192585Srmacklem	}
1003192585Srmacklem
1004192585Srmacklem	(void) vfs_getopt(mp->mnt_optnew, "hostname", (void **)&args.hostname,
1005192585Srmacklem	    &len);
1006192585Srmacklem	if (args.hostname == NULL) {
1007192585Srmacklem		vfs_mount_error(mp, "Invalid hostname");
1008192585Srmacklem		error = EINVAL;
1009192585Srmacklem		goto out;
1010192585Srmacklem	}
1011192585Srmacklem	bcopy(args.hostname, hst, MNAMELEN);
1012192585Srmacklem	hst[MNAMELEN - 1] = '\0';
1013192585Srmacklem
1014192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1015192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1016192585Srmacklem	else
1017192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1018221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1019192585Srmacklem
1020192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1021192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1022192585Srmacklem	else
1023191783Srmacklem		krbname[0] = '\0';
1024221014Srmacklem	krbnamelen = strlen(krbname);
1025192585Srmacklem
1026192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1027192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1028192585Srmacklem	else
1029191783Srmacklem		dirpath[0] = '\0';
1030221014Srmacklem	dirlen = strlen(dirpath);
1031192585Srmacklem
1032192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "addr", (void **)&args.addr,
1033192585Srmacklem	    &args.addrlen) == 0) {
1034192585Srmacklem		if (args.addrlen > SOCK_MAXADDRLEN) {
1035192585Srmacklem			error = ENAMETOOLONG;
1036191783Srmacklem			goto out;
1037191783Srmacklem		}
1038192585Srmacklem		nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1039192585Srmacklem		bcopy(args.addr, nam, args.addrlen);
1040192585Srmacklem		nam->sa_len = args.addrlen;
1041191783Srmacklem	}
1042192585Srmacklem
1043191783Srmacklem	args.fh = nfh;
1044221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1045221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1046221014Srmacklem	    negnametimeo);
1047191783Srmacklemout:
1048191783Srmacklem	if (!error) {
1049191783Srmacklem		MNT_ILOCK(mp);
1050191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
1051191783Srmacklem		MNT_IUNLOCK(mp);
1052191783Srmacklem	}
1053191783Srmacklem	return (error);
1054191783Srmacklem}
1055191783Srmacklem
1056191783Srmacklem
1057191783Srmacklem/*
1058191783Srmacklem * VFS Operations.
1059191783Srmacklem *
1060191783Srmacklem * mount system call
1061191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1062191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1063191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1064191783Srmacklem * an error after that means that I have to release the mbuf.
1065191783Srmacklem */
1066191783Srmacklem/* ARGSUSED */
1067191783Srmacklemstatic int
1068191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags)
1069191783Srmacklem{
1070191783Srmacklem	int error;
1071191783Srmacklem	struct nfs_args args;
1072191783Srmacklem
1073191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1074191783Srmacklem	if (error)
1075191783Srmacklem		return error;
1076191783Srmacklem
1077191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1078191783Srmacklem
1079191783Srmacklem	error = kernel_mount(ma, flags);
1080191783Srmacklem	return (error);
1081191783Srmacklem}
1082191783Srmacklem
1083191783Srmacklem/*
1084191783Srmacklem * Common code for mount and mountroot
1085191783Srmacklem */
1086191783Srmacklemstatic int
1087191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1088221014Srmacklem    char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
1089221014Srmacklem    u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
1090221014Srmacklem    struct ucred *cred, struct thread *td, int negnametimeo)
1091191783Srmacklem{
1092191783Srmacklem	struct nfsmount *nmp;
1093191783Srmacklem	struct nfsnode *np;
1094195762Srmacklem	int error, trycnt, ret;
1095191783Srmacklem	struct nfsvattr nfsva;
1096191783Srmacklem	static u_int64_t clval = 0;
1097191783Srmacklem
1098191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1099191783Srmacklem		nmp = VFSTONFS(mp);
1100191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1101191783Srmacklem		FREE(nam, M_SONAME);
1102191783Srmacklem		return (0);
1103191783Srmacklem	} else {
1104191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1105221014Srmacklem		    krbnamelen + dirlen + srvkrbnamelen + 2,
1106221014Srmacklem		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
1107191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1108191783Srmacklem		if (clval == 0)
1109191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1110191783Srmacklem		nmp->nm_clval = clval++;
1111221014Srmacklem		nmp->nm_krbnamelen = krbnamelen;
1112221014Srmacklem		nmp->nm_dirpathlen = dirlen;
1113221014Srmacklem		nmp->nm_srvkrbnamelen = srvkrbnamelen;
1114192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1115191783Srmacklem			/*
1116192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1117192675Srmacklem			 * the nfsv4 state handling operations if there is
1118192675Srmacklem			 * no host based principal set. Use the uid of
1119192675Srmacklem			 * this user if not root, since they are doing the
1120192675Srmacklem			 * mount. I don't think setting this for root will
1121192675Srmacklem			 * work, since root normally does not have user
1122192675Srmacklem			 * credentials in a credentials cache.
1123191783Srmacklem			 */
1124192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1125191783Srmacklem		} else {
1126191783Srmacklem			/*
1127192675Srmacklem			 * Just set to -1, so it won't be used.
1128191783Srmacklem			 */
1129191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1130191783Srmacklem		}
1131191783Srmacklem
1132191783Srmacklem		/* Copy and null terminate all the names */
1133191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1134191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1135191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1136191783Srmacklem		}
1137191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1138191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1139191783Srmacklem			    nmp->nm_dirpathlen);
1140191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1141191783Srmacklem			    + 1] = '\0';
1142191783Srmacklem		}
1143191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1144191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1145191783Srmacklem			    nmp->nm_srvkrbnamelen);
1146191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1147191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1148191783Srmacklem		}
1149191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1150191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1151191783Srmacklem		mp->mnt_data = nmp;
1152214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1153216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1154191783Srmacklem	}
1155191783Srmacklem	vfs_getnewfsid(mp);
1156191783Srmacklem	nmp->nm_mountp = mp;
1157191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1158203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1159191783Srmacklem
1160214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1161192585Srmacklem
1162191783Srmacklem	/*
1163191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1164191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1165191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1166191783Srmacklem	 *
1167191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1168191783Srmacklem	 * that we can handle until we find out otherwise.
1169191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1170191783Srmacklem	 * buffer cache using signed(!) block numbers.
1171191783Srmacklem	 */
1172191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1173191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1174191783Srmacklem	else
1175191783Srmacklem		nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1;
1176191783Srmacklem
1177191783Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1178191783Srmacklem	nmp->nm_retry = NFS_RETRANS;
1179191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1180191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1181191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1182191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1183191783Srmacklem	}
1184191783Srmacklem	nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1185191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1186191783Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1187191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1188191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1189191783Srmacklem		nmp->nm_tprintf_delay = 0;
1190191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1191191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1192191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1193191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1194191783Srmacklem	if (nmp->nm_fhsize > 0)
1195191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1196191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1197191783Srmacklem	nmp->nm_nam = nam;
1198191783Srmacklem	/* Set up the sockets and per-host congestion */
1199191783Srmacklem	nmp->nm_sotype = argp->sotype;
1200191783Srmacklem	nmp->nm_soproto = argp->proto;
1201191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1202191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1203191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1204191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1205191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1206191783Srmacklem	else
1207191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1208191783Srmacklem
1209191783Srmacklem
1210191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1211191783Srmacklem		goto bad;
1212191783Srmacklem
1213191783Srmacklem	/*
1214191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1215191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1216191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1217191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1218191783Srmacklem	 * this problem, because one can identify root inodes by their
1219191783Srmacklem	 * number == ROOTINO (2).
1220191783Srmacklem	 */
1221191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1222191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1223191783Srmacklem		/*
1224191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1225191783Srmacklem		 * path needs to be looked up.
1226191783Srmacklem		 */
1227191783Srmacklem		trycnt = 3;
1228191783Srmacklem		do {
1229191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1230191783Srmacklem			    cred, td);
1231191783Srmacklem			if (error)
1232207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1233191783Srmacklem		} while (error && --trycnt > 0);
1234191783Srmacklem		if (error) {
1235191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1236191783Srmacklem			goto bad;
1237191783Srmacklem		}
1238191783Srmacklem	}
1239191783Srmacklem	if (nmp->nm_fhsize > 0) {
1240195762Srmacklem		/*
1241195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1242195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1243195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1244195762Srmacklem		 */
1245195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1246220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1247220732Srmacklem		    LK_EXCLUSIVE);
1248191783Srmacklem		if (error)
1249191783Srmacklem			goto bad;
1250191783Srmacklem		*vpp = NFSTOV(np);
1251191783Srmacklem
1252191783Srmacklem		/*
1253191783Srmacklem		 * Get file attributes and transfer parameters for the
1254191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1255191783Srmacklem		 * (*vpp)->v_type with the correct value.
1256191783Srmacklem		 */
1257191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1258191783Srmacklem		    cred, td, &nfsva, NULL);
1259191783Srmacklem		if (ret) {
1260191783Srmacklem			/*
1261191783Srmacklem			 * Just set default values to get things going.
1262191783Srmacklem			 */
1263191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1264191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1265191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1266191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1267191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1268191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1269191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1270191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1271191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1272191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1273191783Srmacklem		}
1274191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1275191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1276191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1277191783Srmacklem
1278191783Srmacklem		/*
1279191783Srmacklem		 * Lose the lock but keep the ref.
1280191783Srmacklem		 */
1281191783Srmacklem		VOP_UNLOCK(*vpp, 0);
1282191783Srmacklem		return (0);
1283191783Srmacklem	}
1284191783Srmacklem	error = EIO;
1285191783Srmacklem
1286191783Srmacklembad:
1287191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1288191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1289191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1290191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1291191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1292191783Srmacklem	FREE(nam, M_SONAME);
1293191783Srmacklem	return (error);
1294191783Srmacklem}
1295191783Srmacklem
1296191783Srmacklem/*
1297191783Srmacklem * unmount system call
1298191783Srmacklem */
1299191783Srmacklemstatic int
1300191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1301191783Srmacklem{
1302191990Sattilio	struct thread *td;
1303191783Srmacklem	struct nfsmount *nmp;
1304191783Srmacklem	int error, flags = 0, trycnt = 0;
1305191783Srmacklem
1306191990Sattilio	td = curthread;
1307191990Sattilio
1308191783Srmacklem	if (mntflags & MNT_FORCE)
1309191783Srmacklem		flags |= FORCECLOSE;
1310191783Srmacklem	nmp = VFSTONFS(mp);
1311191783Srmacklem	/*
1312191783Srmacklem	 * Goes something like this..
1313191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1314191783Srmacklem	 * - Close the socket
1315191783Srmacklem	 * - Free up the data structures
1316191783Srmacklem	 */
1317191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1318191783Srmacklem	if (mntflags & MNT_FORCE) {
1319191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1320191783Srmacklem		if (error)
1321191783Srmacklem			goto out;
1322191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1323191783Srmacklem		nfscl_umount(nmp, td);
1324191783Srmacklem	}
1325191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1326191783Srmacklem	do {
1327191783Srmacklem		error = vflush(mp, 1, flags, td);
1328191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1329207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1330191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1331191783Srmacklem	if (error)
1332191783Srmacklem		goto out;
1333191783Srmacklem
1334191783Srmacklem	/*
1335191783Srmacklem	 * We are now committed to the unmount.
1336191783Srmacklem	 */
1337191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1338191783Srmacklem		nfscl_umount(nmp, td);
1339191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1340191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1341191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1342191783Srmacklem
1343191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1344191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1345191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1346191783Srmacklemout:
1347191783Srmacklem	return (error);
1348191783Srmacklem}
1349191783Srmacklem
1350191783Srmacklem/*
1351191783Srmacklem * Return root of a filesystem
1352191783Srmacklem */
1353191783Srmacklemstatic int
1354191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1355191783Srmacklem{
1356191783Srmacklem	struct vnode *vp;
1357191783Srmacklem	struct nfsmount *nmp;
1358191783Srmacklem	struct nfsnode *np;
1359191783Srmacklem	int error;
1360191783Srmacklem
1361191783Srmacklem	nmp = VFSTONFS(mp);
1362220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1363191783Srmacklem	if (error)
1364191783Srmacklem		return error;
1365191783Srmacklem	vp = NFSTOV(np);
1366191783Srmacklem	/*
1367191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1368191783Srmacklem	 */
1369191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1370191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1371191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1372191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1373191783Srmacklem	} else
1374191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1375191783Srmacklem	if (vp->v_type == VNON)
1376191783Srmacklem	    vp->v_type = VDIR;
1377191783Srmacklem	vp->v_vflag |= VV_ROOT;
1378191783Srmacklem	*vpp = vp;
1379191783Srmacklem	return (0);
1380191783Srmacklem}
1381191783Srmacklem
1382191783Srmacklem/*
1383191783Srmacklem * Flush out the buffer cache
1384191783Srmacklem */
1385191783Srmacklem/* ARGSUSED */
1386191783Srmacklemstatic int
1387191990Sattilionfs_sync(struct mount *mp, int waitfor)
1388191783Srmacklem{
1389191783Srmacklem	struct vnode *vp, *mvp;
1390191990Sattilio	struct thread *td;
1391191783Srmacklem	int error, allerror = 0;
1392191783Srmacklem
1393191990Sattilio	td = curthread;
1394191990Sattilio
1395191783Srmacklem	/*
1396191783Srmacklem	 * Force stale buffer cache information to be flushed.
1397191783Srmacklem	 */
1398191783Srmacklem	MNT_ILOCK(mp);
1399191783Srmacklemloop:
1400191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1401191783Srmacklem		VI_LOCK(vp);
1402191783Srmacklem		MNT_IUNLOCK(mp);
1403191783Srmacklem		/* XXX Racy bv_cnt check. */
1404191783Srmacklem		if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1405191783Srmacklem		    waitfor == MNT_LAZY) {
1406191783Srmacklem			VI_UNLOCK(vp);
1407191783Srmacklem			MNT_ILOCK(mp);
1408191783Srmacklem			continue;
1409191783Srmacklem		}
1410191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1411191783Srmacklem			MNT_ILOCK(mp);
1412191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1413191783Srmacklem			goto loop;
1414191783Srmacklem		}
1415191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1416191783Srmacklem		if (error)
1417191783Srmacklem			allerror = error;
1418191783Srmacklem		VOP_UNLOCK(vp, 0);
1419191783Srmacklem		vrele(vp);
1420191783Srmacklem
1421191783Srmacklem		MNT_ILOCK(mp);
1422191783Srmacklem	}
1423191783Srmacklem	MNT_IUNLOCK(mp);
1424191783Srmacklem	return (allerror);
1425191783Srmacklem}
1426191783Srmacklem
1427191783Srmacklemstatic int
1428191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1429191783Srmacklem{
1430191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1431191783Srmacklem	struct vfsquery vq;
1432191783Srmacklem	int error;
1433191783Srmacklem
1434191783Srmacklem	bzero(&vq, sizeof(vq));
1435191783Srmacklem	switch (op) {
1436191783Srmacklem#if 0
1437191783Srmacklem	case VFS_CTL_NOLOCKS:
1438191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1439191783Srmacklem 		if (req->oldptr != NULL) {
1440191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1441191783Srmacklem 			if (error)
1442191783Srmacklem 				return (error);
1443191783Srmacklem 		}
1444191783Srmacklem 		if (req->newptr != NULL) {
1445191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1446191783Srmacklem 			if (error)
1447191783Srmacklem 				return (error);
1448191783Srmacklem			if (val)
1449191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1450191783Srmacklem			else
1451191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1452191783Srmacklem 		}
1453191783Srmacklem		break;
1454191783Srmacklem#endif
1455191783Srmacklem	case VFS_CTL_QUERY:
1456191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1457191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1458191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1459191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1460191783Srmacklem#if 0
1461191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1462191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1463191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1464191783Srmacklem#endif
1465191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1466191783Srmacklem		break;
1467191783Srmacklem 	case VFS_CTL_TIMEO:
1468191783Srmacklem 		if (req->oldptr != NULL) {
1469191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1470191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1471191783Srmacklem 			if (error)
1472191783Srmacklem 				return (error);
1473191783Srmacklem 		}
1474191783Srmacklem 		if (req->newptr != NULL) {
1475191783Srmacklem			error = vfs_suser(mp, req->td);
1476191783Srmacklem			if (error)
1477191783Srmacklem				return (error);
1478191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1479191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1480191783Srmacklem 			if (error)
1481191783Srmacklem 				return (error);
1482191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1483191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1484191783Srmacklem 		}
1485191783Srmacklem		break;
1486191783Srmacklem	default:
1487191783Srmacklem		return (ENOTSUP);
1488191783Srmacklem	}
1489191783Srmacklem	return (0);
1490191783Srmacklem}
1491191783Srmacklem
1492214048Srmacklem/*
1493214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1494214048Srmacklem */
1495214048Srmacklemstatic void
1496214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1497216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1498216931Srmacklem    struct timeval *timeop)
1499214048Srmacklem{
1500214048Srmacklem	struct nfsmount *nmp;
1501214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1502214048Srmacklem
1503214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1504214048Srmacklem	if (fhlenp != NULL)
1505214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1506214048Srmacklem	if (fhp != NULL)
1507214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1508214048Srmacklem	if (sp != NULL)
1509214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1510214048Srmacklem	if (is_v3p != NULL)
1511214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1512214048Srmacklem	if (sizep != NULL)
1513214048Srmacklem		*sizep = np->n_size;
1514216931Srmacklem	if (timeop != NULL) {
1515216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1516216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1517216931Srmacklem	}
1518214048Srmacklem}
1519214048Srmacklem
1520