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: releng/10.3/sys/fs/nfsclient/nfs_clvfsops.c 291551 2015-12-01 02:30:41Z 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;
82222233Srmacklemextern int nfsrv_useacl;
83244042Srmacklemextern int nfscl_debuglevel;
84249630Srmacklemextern enum nfsiod_state ncl_iodwant[NFS_MAXASYNCDAEMON];
85249630Srmacklemextern struct nfsmount *ncl_iodmount[NFS_MAXASYNCDAEMON];
86249630Srmacklemextern struct mtx ncl_iod_mutex;
87244042SrmacklemNFSCLSTATEMUTEX;
88191783Srmacklem
89191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
90191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
91191783Srmacklem
92221973SrmacklemSYSCTL_DECL(_vfs_nfs);
93191783Srmacklemstatic int nfs_ip_paranoia = 1;
94221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
95191783Srmacklem    &nfs_ip_paranoia, 0, "");
96191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
97221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_INITIAL_DELAY,
98191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
99191783Srmacklem/* how long between console messages "nfs server foo not responding" */
100191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
101221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_DELAY,
102191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
103191783Srmacklem
104221040Srmacklemstatic int	nfs_mountroot(struct mount *);
105192585Srmacklemstatic void	nfs_sec_name(char *, int *);
106191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
107214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
108214048Srmacklem		    struct thread *);
109191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
110221014Srmacklem		    struct sockaddr *, char *, u_char *, int, u_char *, int,
111221014Srmacklem		    u_char *, int, struct vnode **, struct ucred *,
112244042Srmacklem		    struct thread *, int, int, int);
113214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
114216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
115216931Srmacklem		    struct timeval *);
116191783Srmacklemstatic vfs_mount_t nfs_mount;
117191783Srmacklemstatic vfs_cmount_t nfs_cmount;
118191783Srmacklemstatic vfs_unmount_t nfs_unmount;
119191783Srmacklemstatic vfs_root_t nfs_root;
120191783Srmacklemstatic vfs_statfs_t nfs_statfs;
121191783Srmacklemstatic vfs_sync_t nfs_sync;
122191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
123255136Srmacklemstatic vfs_purge_t nfs_purge;
124191783Srmacklem
125191783Srmacklem/*
126191783Srmacklem * nfs vfs operations.
127191783Srmacklem */
128191783Srmacklemstatic struct vfsops nfs_vfsops = {
129191783Srmacklem	.vfs_init =		ncl_init,
130191783Srmacklem	.vfs_mount =		nfs_mount,
131191783Srmacklem	.vfs_cmount =		nfs_cmount,
132191783Srmacklem	.vfs_root =		nfs_root,
133191783Srmacklem	.vfs_statfs =		nfs_statfs,
134191783Srmacklem	.vfs_sync =		nfs_sync,
135191783Srmacklem	.vfs_uninit =		ncl_uninit,
136191783Srmacklem	.vfs_unmount =		nfs_unmount,
137191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
138255136Srmacklem	.vfs_purge =		nfs_purge,
139191783Srmacklem};
140247116SjhbVFS_SET(nfs_vfsops, nfs, VFCF_NETWORK | VFCF_SBDRY);
141191783Srmacklem
142191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
143221139SrmacklemMODULE_VERSION(nfs, 1);
144221139SrmacklemMODULE_DEPEND(nfs, nfscommon, 1, 1, 1);
145221139SrmacklemMODULE_DEPEND(nfs, krpc, 1, 1, 1);
146221139SrmacklemMODULE_DEPEND(nfs, nfssvc, 1, 1, 1);
147221139SrmacklemMODULE_DEPEND(nfs, nfslock, 1, 1, 1);
148191783Srmacklem
149191783Srmacklem/*
150221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it
151221066Srmacklem * can be shared by both NFS clients. It is declared here so that it
152221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it
153221066Srmacklem * isn't used in that case.
154191783Srmacklem */
155221066Srmacklem#if !defined(NFS_ROOT) && !defined(NFSCLIENT)
156221066Srmacklemstruct nfs_diskless	nfs_diskless = { { { 0 } } };
157221066Srmacklemstruct nfsv3_diskless	nfsv3_diskless = { { { 0 } } };
158221066Srmacklemint			nfs_diskless_valid = 0;
159221066Srmacklem#endif
160221066Srmacklem
161221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
162221040Srmacklem    &nfs_diskless_valid, 0,
163192145Srmacklem    "Has the diskless struct been filled correctly");
164191783Srmacklem
165221973SrmacklemSYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
166221040Srmacklem    nfsv3_diskless.root_hostnam, 0, "Path to nfs root");
167191783Srmacklem
168221973SrmacklemSYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
169221040Srmacklem    &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr),
170192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
171191783Srmacklem
172191783Srmacklem
173191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
174191783Srmacklemstatic int	nfs_mountdiskless(char *,
175191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
176191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
177191783Srmacklemstatic void	nfs_convert_diskless(void);
178191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
179191783Srmacklem		    struct onfs_args *oargs);
180191783Srmacklem
181191783Srmacklemint
182191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
183191783Srmacklem{
184191783Srmacklem	int iosize, maxio;
185191783Srmacklem
186191783Srmacklem	/* First, set the upper limit for iosize */
187191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
188191783Srmacklem		maxio = NFS_MAXBSIZE;
189191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
190191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
191191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
192191783Srmacklem		else
193191783Srmacklem			maxio = NFS_MAXBSIZE;
194191783Srmacklem	} else {
195191783Srmacklem		maxio = NFS_V2MAXDATA;
196191783Srmacklem	}
197191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
198191783Srmacklem		nmp->nm_rsize = maxio;
199282933Srmacklem	if (nmp->nm_rsize > NFS_MAXBSIZE)
200282933Srmacklem		nmp->nm_rsize = NFS_MAXBSIZE;
201191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
202191783Srmacklem		nmp->nm_readdirsize = maxio;
203191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
204191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
205191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
206191783Srmacklem		nmp->nm_wsize = maxio;
207282933Srmacklem	if (nmp->nm_wsize > NFS_MAXBSIZE)
208282933Srmacklem		nmp->nm_wsize = NFS_MAXBSIZE;
209191783Srmacklem
210191783Srmacklem	/*
211191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
212191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
213191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
214291551Srmacklem	 * space.  It must also be at least NFS_DIRBLKSIZ, since
215291551Srmacklem	 * that is the buffer size used for directories.
216191783Srmacklem	 */
217191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
218191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
219291551Srmacklem	iosize = imax(iosize, NFS_DIRBLKSIZ);
220191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
221191783Srmacklem	return (iosize);
222191783Srmacklem}
223191783Srmacklem
224191783Srmacklemstatic void
225191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
226191783Srmacklem{
227191783Srmacklem
228191783Srmacklem	args->version = NFS_ARGSVERSION;
229191783Srmacklem	args->addr = oargs->addr;
230191783Srmacklem	args->addrlen = oargs->addrlen;
231191783Srmacklem	args->sotype = oargs->sotype;
232191783Srmacklem	args->proto = oargs->proto;
233191783Srmacklem	args->fh = oargs->fh;
234191783Srmacklem	args->fhsize = oargs->fhsize;
235191783Srmacklem	args->flags = oargs->flags;
236191783Srmacklem	args->wsize = oargs->wsize;
237191783Srmacklem	args->rsize = oargs->rsize;
238191783Srmacklem	args->readdirsize = oargs->readdirsize;
239191783Srmacklem	args->timeo = oargs->timeo;
240191783Srmacklem	args->retrans = oargs->retrans;
241191783Srmacklem	args->readahead = oargs->readahead;
242191783Srmacklem	args->hostname = oargs->hostname;
243191783Srmacklem}
244191783Srmacklem
245191783Srmacklemstatic void
246191783Srmacklemnfs_convert_diskless(void)
247191783Srmacklem{
248191783Srmacklem
249221040Srmacklem	bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif,
250221040Srmacklem		sizeof(struct ifaliasreq));
251221040Srmacklem	bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway,
252221040Srmacklem		sizeof(struct sockaddr_in));
253221040Srmacklem	nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args);
254221040Srmacklem	if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
255221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_MYFH;
256221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH);
257191783Srmacklem	} else {
258221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_V2FH;
259221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH);
260191783Srmacklem	}
261221040Srmacklem	bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr,
262221040Srmacklem		sizeof(struct sockaddr_in));
263221040Srmacklem	bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN);
264221040Srmacklem	nfsv3_diskless.root_time = nfs_diskless.root_time;
265221040Srmacklem	bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam,
266221040Srmacklem		MAXHOSTNAMELEN);
267221040Srmacklem	nfs_diskless_valid = 3;
268191783Srmacklem}
269191783Srmacklem
270191783Srmacklem/*
271191783Srmacklem * nfs statfs call
272191783Srmacklem */
273191783Srmacklemstatic int
274191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
275191783Srmacklem{
276191783Srmacklem	struct vnode *vp;
277191990Sattilio	struct thread *td;
278191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
279191783Srmacklem	struct nfsvattr nfsva;
280191783Srmacklem	struct nfsfsinfo fs;
281191783Srmacklem	struct nfsstatfs sb;
282191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
283191783Srmacklem	struct nfsnode *np;
284191783Srmacklem
285191990Sattilio	td = curthread;
286191990Sattilio
287191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
288191783Srmacklem	if (error)
289191783Srmacklem		return (error);
290220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
291191783Srmacklem	if (error) {
292191783Srmacklem		vfs_unbusy(mp);
293191783Srmacklem		return (error);
294191783Srmacklem	}
295191783Srmacklem	vp = NFSTOV(np);
296191783Srmacklem	mtx_lock(&nmp->nm_mtx);
297191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
298191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
299191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
300191783Srmacklem		    &attrflag, NULL);
301191783Srmacklem		if (!error)
302191783Srmacklem			gotfsinfo = 1;
303191783Srmacklem	} else
304191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
305191783Srmacklem	if (!error)
306191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
307191783Srmacklem		    &attrflag, NULL);
308244042Srmacklem	if (error != 0)
309244042Srmacklem		NFSCL_DEBUG(2, "statfs=%d\n", error);
310191783Srmacklem	if (attrflag == 0) {
311191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
312244042Srmacklem		    td->td_ucred, td, &nfsva, NULL, NULL);
313191783Srmacklem		if (ret) {
314191783Srmacklem			/*
315191783Srmacklem			 * Just set default values to get things going.
316191783Srmacklem			 */
317191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
318191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
319191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
320191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
321191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
322191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
323191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
324191783Srmacklem			nfsva.na_vattr.va_gen = 1;
325191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
326191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
327191783Srmacklem		}
328191783Srmacklem	}
329191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
330191783Srmacklem	if (!error) {
331191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
332191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
333191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
334191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
335191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
336191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
337191783Srmacklem	    if (sbp != &mp->mnt_stat) {
338191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
339191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
340191783Srmacklem	    }
341191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
342191783Srmacklem	} else if (NFS_ISV4(vp)) {
343191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
344191783Srmacklem	}
345191783Srmacklem	vput(vp);
346191783Srmacklem	vfs_unbusy(mp);
347191783Srmacklem	return (error);
348191783Srmacklem}
349191783Srmacklem
350191783Srmacklem/*
351191783Srmacklem * nfs version 3 fsinfo rpc call
352191783Srmacklem */
353191783Srmacklemint
354191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
355191783Srmacklem    struct thread *td)
356191783Srmacklem{
357191783Srmacklem	struct nfsfsinfo fs;
358191783Srmacklem	struct nfsvattr nfsva;
359191783Srmacklem	int error, attrflag;
360191783Srmacklem
361191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
362191783Srmacklem	if (!error) {
363191783Srmacklem		if (attrflag)
364191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
365191783Srmacklem			    1);
366191783Srmacklem		mtx_lock(&nmp->nm_mtx);
367191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
368191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
369191783Srmacklem	}
370191783Srmacklem	return (error);
371191783Srmacklem}
372191783Srmacklem
373191783Srmacklem/*
374191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
375221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary
376191783Srmacklem * bootstrap.
377191783Srmacklem * It goes something like this:
378191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
379191783Srmacklem *   can talk to the server
380221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as
381191783Srmacklem *   a default gateway.
382191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
383191783Srmacklem *
384191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
385191783Srmacklem * structure, as well as other global NFS client variables here, as
386192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
387191783Srmacklem * client activity occurs.
388191783Srmacklem */
389221040Srmacklemstatic int
390221040Srmacklemnfs_mountroot(struct mount *mp)
391191783Srmacklem{
392192145Srmacklem	struct thread *td = curthread;
393221040Srmacklem	struct nfsv3_diskless *nd = &nfsv3_diskless;
394191783Srmacklem	struct socket *so;
395191783Srmacklem	struct vnode *vp;
396191783Srmacklem	struct ifreq ir;
397193066Sjamie	int error;
398191783Srmacklem	u_long l;
399191783Srmacklem	char buf[128];
400191783Srmacklem	char *cp;
401191783Srmacklem
402191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
403192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
404191783Srmacklem#elif defined(NFS_ROOT)
405191783Srmacklem	nfs_setup_diskless();
406191783Srmacklem#endif
407191783Srmacklem
408221040Srmacklem	if (nfs_diskless_valid == 0)
409191783Srmacklem		return (-1);
410221040Srmacklem	if (nfs_diskless_valid == 1)
411191783Srmacklem		nfs_convert_diskless();
412191783Srmacklem
413191783Srmacklem	/*
414191783Srmacklem	 * XXX splnet, so networks will receive...
415191783Srmacklem	 */
416191783Srmacklem	splnet();
417191783Srmacklem
418191783Srmacklem	/*
419191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
420191783Srmacklem	 * talk to the server.
421191783Srmacklem	 */
422191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
423191783Srmacklem	    td->td_ucred, td);
424191783Srmacklem	if (error)
425192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
426191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
427191783Srmacklem
428191783Srmacklem#if 0 /* XXX Bad idea */
429191783Srmacklem	/*
430191783Srmacklem	 * We might not have been told the right interface, so we pass
431191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
432191783Srmacklem	 * one of them configured.
433191783Srmacklem	 */
434191783Srmacklem
435191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
436191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
437191783Srmacklem		nd->myif.ifra_name[i] <= '9';
438191783Srmacklem		nd->myif.ifra_name[i] ++) {
439191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
440191783Srmacklem		if(!error)
441191783Srmacklem			break;
442191783Srmacklem	}
443191783Srmacklem#endif
444191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
445191783Srmacklem	if (error)
446192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
447191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
448191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
449191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
450191783Srmacklem		freeenv(cp);
451191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
452191783Srmacklem		if (error)
453192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
454191783Srmacklem	}
455191783Srmacklem	soclose(so);
456191783Srmacklem
457191783Srmacklem	/*
458191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
459191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
460191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
461191783Srmacklem	 * to avoid panicking inappropriately in that situation.
462191783Srmacklem	 */
463191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
464191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
465191783Srmacklem		struct sockaddr_in mask, sin;
466191783Srmacklem
467191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
468191783Srmacklem		sin = mask;
469191783Srmacklem		sin.sin_family = AF_INET;
470191783Srmacklem		sin.sin_len = sizeof(sin);
471192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
472218757Sbz		CURVNET_SET(TD_TO_VNET(td));
473231852Sbz		error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&sin,
474191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
475191783Srmacklem		    (struct sockaddr *)&mask,
476231852Sbz		    RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB);
477218757Sbz		CURVNET_RESTORE();
478191783Srmacklem		if (error)
479192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
480191783Srmacklem	}
481191783Srmacklem
482191783Srmacklem	/*
483191783Srmacklem	 * Create the rootfs mount point.
484191783Srmacklem	 */
485191783Srmacklem	nd->root_args.fh = nd->root_fh;
486191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
487191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
488191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
489191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
490191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
491191783Srmacklem	printf("NFS ROOT: %s\n", buf);
492192145Srmacklem	nd->root_args.hostname = buf;
493191783Srmacklem	if ((error = nfs_mountdiskless(buf,
494191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
495191783Srmacklem		return (error);
496191783Srmacklem	}
497191783Srmacklem
498191783Srmacklem	/*
499191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
500191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
501191783Srmacklem	 * mount the right /var based upon its preset value.
502191783Srmacklem	 */
503193066Sjamie	mtx_lock(&prison0.pr_mtx);
504194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
505194118Sjamie	    sizeof(prison0.pr_hostname));
506193066Sjamie	mtx_unlock(&prison0.pr_mtx);
507191783Srmacklem	inittodr(ntohl(nd->root_time));
508191783Srmacklem	return (0);
509191783Srmacklem}
510191783Srmacklem
511191783Srmacklem/*
512191783Srmacklem * Internal version of mount system call for diskless setup.
513191783Srmacklem */
514191783Srmacklemstatic int
515191783Srmacklemnfs_mountdiskless(char *path,
516191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
517191783Srmacklem    struct vnode **vpp, struct mount *mp)
518191783Srmacklem{
519191783Srmacklem	struct sockaddr *nam;
520221014Srmacklem	int dirlen, error;
521221014Srmacklem	char *dirpath;
522191783Srmacklem
523221014Srmacklem	/*
524221014Srmacklem	 * Find the directory path in "path", which also has the server's
525221014Srmacklem	 * name/ip address in it.
526221014Srmacklem	 */
527221014Srmacklem	dirpath = strchr(path, ':');
528221014Srmacklem	if (dirpath != NULL)
529221014Srmacklem		dirlen = strlen(++dirpath);
530221014Srmacklem	else
531221014Srmacklem		dirlen = 0;
532191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
533221014Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
534230547Sjhb	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO,
535244042Srmacklem	    NFS_DEFAULT_NEGNAMETIMEO, 0)) != 0) {
536192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
537191783Srmacklem		return (error);
538191783Srmacklem	}
539191783Srmacklem	return (0);
540191783Srmacklem}
541191783Srmacklem
542191783Srmacklemstatic void
543192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
544192585Srmacklem{
545192585Srmacklem	if (!strcmp(sec, "krb5"))
546192585Srmacklem		*flagsp |= NFSMNT_KERB;
547192585Srmacklem	else if (!strcmp(sec, "krb5i"))
548192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
549192585Srmacklem	else if (!strcmp(sec, "krb5p"))
550192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
551192585Srmacklem}
552192585Srmacklem
553192585Srmacklemstatic void
554191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
555214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
556191783Srmacklem{
557191783Srmacklem	int s;
558191783Srmacklem	int adjsock;
559214048Srmacklem	char *p;
560191783Srmacklem
561191783Srmacklem	s = splnet();
562191783Srmacklem
563191783Srmacklem	/*
564191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
565191783Srmacklem	 * an update.  If this is not an update, then either the read-only
566191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
567191783Srmacklem	 * intentionally at some previous point.
568191783Srmacklem	 */
569191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
570191783Srmacklem		MNT_ILOCK(mp);
571191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
572191783Srmacklem		MNT_IUNLOCK(mp);
573191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
574191783Srmacklem		MNT_ILOCK(mp);
575191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
576191783Srmacklem		MNT_IUNLOCK(mp);
577191783Srmacklem	}
578191783Srmacklem
579191783Srmacklem	/*
580191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
581191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
582191783Srmacklem	 * and soft timeout behavior.
583191783Srmacklem	 */
584191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
585191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
586191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
587220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
588220739Srmacklem			nmp->nm_retry = INT_MAX;
589220739Srmacklem		else
590220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
591191783Srmacklem	}
592191783Srmacklem
593220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
594220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
595220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
596191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
597220739Srmacklem	}
598191783Srmacklem
599220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
600220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
601220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
602191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
603220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
604191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
605191783Srmacklem
606191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
607191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
608191783Srmacklem	splx(s);
609191783Srmacklem
610191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
611191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
612191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
613191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
614191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
615191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
616191783Srmacklem	}
617191783Srmacklem
618191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
619191783Srmacklem		nmp->nm_retry = argp->retrans;
620191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
621191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
622191783Srmacklem	}
623191783Srmacklem
624191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
625191783Srmacklem		nmp->nm_wsize = argp->wsize;
626274150Srmacklem		/*
627274150Srmacklem		 * Clip at the power of 2 below the size. There is an
628274150Srmacklem		 * issue (not isolated) that causes intermittent page
629274150Srmacklem		 * faults if this is not done.
630274150Srmacklem		 */
631274150Srmacklem		if (nmp->nm_wsize > NFS_FABLKSIZE)
632274150Srmacklem			nmp->nm_wsize = 1 << (fls(nmp->nm_wsize) - 1);
633274150Srmacklem		else
634191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
635191783Srmacklem	}
636191783Srmacklem
637191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
638191783Srmacklem		nmp->nm_rsize = argp->rsize;
639274150Srmacklem		/*
640274150Srmacklem		 * Clip at the power of 2 below the size. There is an
641274150Srmacklem		 * issue (not isolated) that causes intermittent page
642274150Srmacklem		 * faults if this is not done.
643274150Srmacklem		 */
644274150Srmacklem		if (nmp->nm_rsize > NFS_FABLKSIZE)
645274150Srmacklem			nmp->nm_rsize = 1 << (fls(nmp->nm_rsize) - 1);
646274150Srmacklem		else
647191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
648191783Srmacklem	}
649191783Srmacklem
650191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
651191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
652191783Srmacklem	}
653191783Srmacklem
654191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
655191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
656191783Srmacklem	else
657191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
658191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
659191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
660191783Srmacklem	else
661191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
662191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
663191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
664191783Srmacklem	else
665191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
666191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
667191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
668191783Srmacklem	else
669191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
670191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
671191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
672191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
673191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
674191783Srmacklem
675191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
676191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
677191783Srmacklem			nmp->nm_readahead = argp->readahead;
678191783Srmacklem		else
679191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
680191783Srmacklem	}
681191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
682191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
683191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
684191783Srmacklem		else
685191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
686191783Srmacklem	}
687191783Srmacklem
688191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
689191783Srmacklem		    (nmp->nm_soproto != argp->proto));
690191783Srmacklem
691191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
692191783Srmacklem		int haslock = 0, error = 0;
693191783Srmacklem
694191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
695191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
696191783Srmacklem			if (!error)
697191783Srmacklem				haslock = 1;
698191783Srmacklem		}
699191783Srmacklem		if (!error) {
700191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
701191783Srmacklem		    if (haslock)
702191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
703191783Srmacklem		    nmp->nm_sotype = argp->sotype;
704191783Srmacklem		    nmp->nm_soproto = argp->proto;
705191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
706191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
707191783Srmacklem			    cred, td, 0)) {
708191783Srmacklem				printf("newnfs_args: retrying connect\n");
709207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
710191783Srmacklem			}
711191783Srmacklem		}
712191783Srmacklem	} else {
713191783Srmacklem		nmp->nm_sotype = argp->sotype;
714191783Srmacklem		nmp->nm_soproto = argp->proto;
715191783Srmacklem	}
716214048Srmacklem
717214048Srmacklem	if (hostname != NULL) {
718214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
719214048Srmacklem		    sizeof(nmp->nm_hostname));
720214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
721214048Srmacklem		if (p != NULL)
722214048Srmacklem			*p = '\0';
723214048Srmacklem	}
724191783Srmacklem}
725191783Srmacklem
726221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
727275249Strasz    "noac", "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
728191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
729192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
730192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
731275249Strasz    "retrans", "actimeo", "acregmin", "acregmax", "acdirmin", "acdirmax",
732275249Strasz    "resvport", "readahead", "hostname", "timeo", "timeout", "addr", "fh",
733275249Strasz    "nfsv3", "sec", "principal", "nfsv4", "gssname", "allgssname", "dirpath",
734275249Strasz    "minorversion", "nametimeo", "negnametimeo", "nocto", "noncontigwr",
735275249Strasz    "pnfs", "wcommitsize",
736191783Srmacklem    NULL };
737191783Srmacklem
738191783Srmacklem/*
739191783Srmacklem * VFS Operations.
740191783Srmacklem *
741191783Srmacklem * mount system call
742191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
743191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
744191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
745191783Srmacklem * an error after that means that I have to release the mbuf.
746191783Srmacklem */
747191783Srmacklem/* ARGSUSED */
748191783Srmacklemstatic int
749191990Sattilionfs_mount(struct mount *mp)
750191783Srmacklem{
751191783Srmacklem	struct nfs_args args = {
752191783Srmacklem	    .version = NFS_ARGSVERSION,
753191783Srmacklem	    .addr = NULL,
754191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
755191783Srmacklem	    .sotype = SOCK_STREAM,
756191783Srmacklem	    .proto = 0,
757191783Srmacklem	    .fh = NULL,
758191783Srmacklem	    .fhsize = 0,
759220739Srmacklem	    .flags = NFSMNT_RESVPORT,
760191783Srmacklem	    .wsize = NFS_WSIZE,
761191783Srmacklem	    .rsize = NFS_RSIZE,
762191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
763191783Srmacklem	    .timeo = 10,
764191783Srmacklem	    .retrans = NFS_RETRANS,
765191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
766191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
767191783Srmacklem	    .hostname = NULL,
768191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
769191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
770191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
771191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
772191783Srmacklem	};
773192585Srmacklem	int error = 0, ret, len;
774192585Srmacklem	struct sockaddr *nam = NULL;
775191783Srmacklem	struct vnode *vp;
776191990Sattilio	struct thread *td;
777191783Srmacklem	char hst[MNAMELEN];
778191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
779286141Srmacklem	char *cp, *opt, *name, *secname;
780230547Sjhb	int nametimeo = NFS_DEFAULT_NAMETIMEO;
781203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
782244042Srmacklem	int minvers = 0;
783221190Srmacklem	int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
784221205Srmacklem	size_t hstlen;
785191783Srmacklem
786221190Srmacklem	has_nfs_args_opt = 0;
787191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
788191783Srmacklem		error = EINVAL;
789191783Srmacklem		goto out;
790191783Srmacklem	}
791191783Srmacklem
792191990Sattilio	td = curthread;
793191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
794221040Srmacklem		error = nfs_mountroot(mp);
795191783Srmacklem		goto out;
796191783Srmacklem	}
797191783Srmacklem
798192585Srmacklem	nfscl_init();
799191783Srmacklem
800221190Srmacklem	/*
801221190Srmacklem	 * The old mount_nfs program passed the struct nfs_args
802221190Srmacklem	 * from userspace to kernel.  The new mount_nfs program
803221190Srmacklem	 * passes string options via nmount() from userspace to kernel
804221190Srmacklem	 * and we populate the struct nfs_args in the kernel.
805221190Srmacklem	 */
806221190Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) {
807221190Srmacklem		error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args,
808221190Srmacklem		    sizeof(args));
809221190Srmacklem		if (error != 0)
810221190Srmacklem			goto out;
811221190Srmacklem
812221190Srmacklem		if (args.version != NFS_ARGSVERSION) {
813221190Srmacklem			error = EPROGMISMATCH;
814221190Srmacklem			goto out;
815221190Srmacklem		}
816221190Srmacklem		has_nfs_args_opt = 1;
817221190Srmacklem	}
818221190Srmacklem
819192585Srmacklem	/* Handle the new style options. */
820275249Strasz	if (vfs_getopt(mp->mnt_optnew, "noac", NULL, NULL) == 0) {
821275249Strasz		args.acdirmin = args.acdirmax =
822275249Strasz		    args.acregmin = args.acregmax = 0;
823275249Strasz		args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX |
824275249Strasz		    NFSMNT_ACREGMIN | NFSMNT_ACREGMAX;
825275249Strasz	}
826192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
827192585Srmacklem		args.flags |= NFSMNT_NOCONN;
828192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
829275252Strasz		args.flags &= ~NFSMNT_NOCONN;
830192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
831192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
832192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
833192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
834192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
835192585Srmacklem		args.flags |= NFSMNT_INT;
836192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
837192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
838192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
839192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
840192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
841192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
842192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
843192585Srmacklem		args.flags |= NFSMNT_SOFT;
844192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
845192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
846192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
847192585Srmacklem		args.sotype = SOCK_DGRAM;
848192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
849192585Srmacklem		args.sotype = SOCK_DGRAM;
850192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
851192585Srmacklem		args.sotype = SOCK_STREAM;
852192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
853192585Srmacklem		args.flags |= NFSMNT_NFSV3;
854192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
855192585Srmacklem		args.flags |= NFSMNT_NFSV4;
856192585Srmacklem		args.sotype = SOCK_STREAM;
857191783Srmacklem	}
858192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
859192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
860221436Sru	if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
861221436Sru		args.flags |= NFSMNT_NOCTO;
862260107Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noncontigwr", NULL, NULL) == 0)
863260107Srmacklem		args.flags |= NFSMNT_NONCONTIGWR;
864244042Srmacklem	if (vfs_getopt(mp->mnt_optnew, "pnfs", NULL, NULL) == 0)
865244042Srmacklem		args.flags |= NFSMNT_PNFS;
866192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
867192585Srmacklem		if (opt == NULL) {
868192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
869192585Srmacklem			error = EINVAL;
870192585Srmacklem			goto out;
871192585Srmacklem		}
872192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
873192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
874192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
875192585Srmacklem			    opt);
876192585Srmacklem			error = EINVAL;
877192585Srmacklem			goto out;
878192585Srmacklem		}
879192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
880192585Srmacklem	}
881192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
882192585Srmacklem		if (opt == NULL) {
883192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
884192585Srmacklem			error = EINVAL;
885192585Srmacklem			goto out;
886192585Srmacklem		}
887192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
888192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
889192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
890192585Srmacklem			    opt);
891192585Srmacklem			error = EINVAL;
892192585Srmacklem			goto out;
893192585Srmacklem		}
894192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
895192585Srmacklem	}
896192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
897192585Srmacklem		if (opt == NULL) {
898192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
899192585Srmacklem			error = EINVAL;
900192585Srmacklem			goto out;
901192585Srmacklem		}
902192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
903192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
904192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
905192585Srmacklem			    opt);
906192585Srmacklem			error = EINVAL;
907192585Srmacklem			goto out;
908192585Srmacklem		}
909192585Srmacklem		args.flags |= NFSMNT_WSIZE;
910192585Srmacklem	}
911192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
912192585Srmacklem		if (opt == NULL) {
913192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
914192585Srmacklem			error = EINVAL;
915192585Srmacklem			goto out;
916192585Srmacklem		}
917192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
918192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
919192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
920192585Srmacklem			    opt);
921192585Srmacklem			error = EINVAL;
922192585Srmacklem			goto out;
923192585Srmacklem		}
924192585Srmacklem		args.flags |= NFSMNT_RSIZE;
925192585Srmacklem	}
926192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
927192585Srmacklem		if (opt == NULL) {
928192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
929192585Srmacklem			error = EINVAL;
930192585Srmacklem			goto out;
931192585Srmacklem		}
932192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
933192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
934192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
935192585Srmacklem			    opt);
936192585Srmacklem			error = EINVAL;
937192585Srmacklem			goto out;
938192585Srmacklem		}
939192585Srmacklem		args.flags |= NFSMNT_RETRANS;
940192585Srmacklem	}
941275249Strasz	if (vfs_getopt(mp->mnt_optnew, "actimeo", (void **)&opt, NULL) == 0) {
942275249Strasz		ret = sscanf(opt, "%d", &args.acregmin);
943275249Strasz		if (ret != 1 || args.acregmin < 0) {
944275249Strasz			vfs_mount_error(mp, "illegal actimeo: %s",
945275249Strasz			    opt);
946275249Strasz			error = EINVAL;
947275249Strasz			goto out;
948275249Strasz		}
949275249Strasz		args.acdirmin = args.acdirmax = args.acregmax = args.acregmin;
950275249Strasz		args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX |
951275249Strasz		    NFSMNT_ACREGMIN | NFSMNT_ACREGMAX;
952275249Strasz	}
953192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
954192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
955192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
956192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
957192585Srmacklem			    opt);
958192585Srmacklem			error = EINVAL;
959192585Srmacklem			goto out;
960192585Srmacklem		}
961192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
962192585Srmacklem	}
963192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
964192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
965192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
966192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
967192585Srmacklem			    opt);
968192585Srmacklem			error = EINVAL;
969192585Srmacklem			goto out;
970192585Srmacklem		}
971192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
972192585Srmacklem	}
973192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
974192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
975192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
976192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
977192585Srmacklem			    opt);
978192585Srmacklem			error = EINVAL;
979192585Srmacklem			goto out;
980192585Srmacklem		}
981192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
982192585Srmacklem	}
983192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
984192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
985192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
986192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
987192585Srmacklem			    opt);
988192585Srmacklem			error = EINVAL;
989192585Srmacklem			goto out;
990192585Srmacklem		}
991192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
992192585Srmacklem	}
993227507Sjhb	if (vfs_getopt(mp->mnt_optnew, "wcommitsize", (void **)&opt, NULL) == 0) {
994227507Sjhb		ret = sscanf(opt, "%d", &args.wcommitsize);
995227507Sjhb		if (ret != 1 || args.wcommitsize < 0) {
996227507Sjhb			vfs_mount_error(mp, "illegal wcommitsize: %s", opt);
997227507Sjhb			error = EINVAL;
998227507Sjhb			goto out;
999227507Sjhb		}
1000227507Sjhb		args.flags |= NFSMNT_WCOMMITSIZE;
1001227507Sjhb	}
1002275249Strasz	if (vfs_getopt(mp->mnt_optnew, "timeo", (void **)&opt, NULL) == 0) {
1003275249Strasz		ret = sscanf(opt, "%d", &args.timeo);
1004275249Strasz		if (ret != 1 || args.timeo <= 0) {
1005275249Strasz			vfs_mount_error(mp, "illegal timeo: %s",
1006275249Strasz			    opt);
1007275249Strasz			error = EINVAL;
1008275249Strasz			goto out;
1009275249Strasz		}
1010275249Strasz		args.flags |= NFSMNT_TIMEO;
1011275249Strasz	}
1012192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
1013192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
1014192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
1015192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
1016192585Srmacklem			    opt);
1017192585Srmacklem			error = EINVAL;
1018192585Srmacklem			goto out;
1019192585Srmacklem		}
1020192585Srmacklem		args.flags |= NFSMNT_TIMEO;
1021192585Srmacklem	}
1022230547Sjhb	if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) {
1023230547Sjhb		ret = sscanf(opt, "%d", &nametimeo);
1024230547Sjhb		if (ret != 1 || nametimeo < 0) {
1025230547Sjhb			vfs_mount_error(mp, "illegal nametimeo: %s", opt);
1026230547Sjhb			error = EINVAL;
1027230547Sjhb			goto out;
1028230547Sjhb		}
1029230547Sjhb	}
1030203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
1031203303Srmacklem	    == 0) {
1032203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
1033203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
1034203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
1035203303Srmacklem			    opt);
1036203303Srmacklem			error = EINVAL;
1037203303Srmacklem			goto out;
1038203303Srmacklem		}
1039203303Srmacklem	}
1040244042Srmacklem	if (vfs_getopt(mp->mnt_optnew, "minorversion", (void **)&opt, NULL) ==
1041244042Srmacklem	    0) {
1042244042Srmacklem		ret = sscanf(opt, "%d", &minvers);
1043244042Srmacklem		if (ret != 1 || minvers < 0 || minvers > 1 ||
1044244042Srmacklem		    (args.flags & NFSMNT_NFSV4) == 0) {
1045244042Srmacklem			vfs_mount_error(mp, "illegal minorversion: %s", opt);
1046244042Srmacklem			error = EINVAL;
1047244042Srmacklem			goto out;
1048244042Srmacklem		}
1049244042Srmacklem	}
1050192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
1051192585Srmacklem		(void **) &secname, NULL) == 0)
1052192585Srmacklem		nfs_sec_name(secname, &args.flags);
1053191783Srmacklem
1054191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1055191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
1056191783Srmacklem
1057191783Srmacklem		if (nmp == NULL) {
1058191783Srmacklem			error = EIO;
1059191783Srmacklem			goto out;
1060191783Srmacklem		}
1061230803Srmacklem
1062191783Srmacklem		/*
1063230803Srmacklem		 * If a change from TCP->UDP is done and there are thread(s)
1064230803Srmacklem		 * that have I/O RPC(s) in progress with a tranfer size
1065230803Srmacklem		 * greater than NFS_MAXDGRAMDATA, those thread(s) will be
1066230803Srmacklem		 * hung, retrying the RPC(s) forever. Usually these threads
1067230803Srmacklem		 * will be seen doing an uninterruptible sleep on wait channel
1068230803Srmacklem		 * "newnfsreq" (truncated to "newnfsre" by procstat).
1069230803Srmacklem		 */
1070230803Srmacklem		if (args.sotype == SOCK_DGRAM && nmp->nm_sotype == SOCK_STREAM)
1071230803Srmacklem			tprintf(td->td_proc, LOG_WARNING,
1072230803Srmacklem	"Warning: mount -u that changes TCP->UDP can result in hung threads\n");
1073230803Srmacklem
1074230803Srmacklem		/*
1075191783Srmacklem		 * When doing an update, we can't change version,
1076191783Srmacklem		 * security, switch lockd strategies or change cookie
1077191783Srmacklem		 * translation
1078191783Srmacklem		 */
1079191783Srmacklem		args.flags = (args.flags &
1080191783Srmacklem		    ~(NFSMNT_NFSV3 |
1081191783Srmacklem		      NFSMNT_NFSV4 |
1082191783Srmacklem		      NFSMNT_KERB |
1083191783Srmacklem		      NFSMNT_INTEGRITY |
1084191783Srmacklem		      NFSMNT_PRIVACY |
1085191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
1086191783Srmacklem		    (nmp->nm_flag &
1087191783Srmacklem			(NFSMNT_NFSV3 |
1088191783Srmacklem			 NFSMNT_NFSV4 |
1089191783Srmacklem			 NFSMNT_KERB |
1090191783Srmacklem			 NFSMNT_INTEGRITY |
1091191783Srmacklem			 NFSMNT_PRIVACY |
1092191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
1093214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
1094191783Srmacklem		goto out;
1095191783Srmacklem	}
1096191783Srmacklem
1097191783Srmacklem	/*
1098191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
1099191783Srmacklem	 * or no-connection mode for those protocols that support
1100191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
1101191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
1102191783Srmacklem	 * to receive replies from a different IP then the request was
1103191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
1104191783Srmacklem	 * not 0.
1105191783Srmacklem	 */
1106191783Srmacklem	if (nfs_ip_paranoia == 0)
1107191783Srmacklem		args.flags |= NFSMNT_NOCONN;
1108192585Srmacklem
1109221190Srmacklem	if (has_nfs_args_opt != 0) {
1110221190Srmacklem		/*
1111221190Srmacklem		 * In the 'nfs_args' case, the pointers in the args
1112221190Srmacklem		 * structure are in userland - we copy them in here.
1113221190Srmacklem		 */
1114221190Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) {
1115192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
1116191783Srmacklem			error = EINVAL;
1117191783Srmacklem			goto out;
1118191783Srmacklem		}
1119221190Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1120221190Srmacklem		    args.fhsize);
1121221190Srmacklem		if (error != 0)
1122221190Srmacklem			goto out;
1123221205Srmacklem		error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen);
1124221190Srmacklem		if (error != 0)
1125221190Srmacklem			goto out;
1126221205Srmacklem		bzero(&hst[hstlen], MNAMELEN - hstlen);
1127221190Srmacklem		args.hostname = hst;
1128221190Srmacklem		/* sockargs() call must be after above copyin() calls */
1129221190Srmacklem		error = getsockaddr(&nam, (caddr_t)args.addr,
1130221190Srmacklem		    args.addrlen);
1131221190Srmacklem		if (error != 0)
1132221190Srmacklem			goto out;
1133191783Srmacklem	} else {
1134221190Srmacklem		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
1135221190Srmacklem		    &args.fhsize) == 0) {
1136221190Srmacklem			if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
1137221190Srmacklem				vfs_mount_error(mp, "Bad file handle");
1138221190Srmacklem				error = EINVAL;
1139221190Srmacklem				goto out;
1140221190Srmacklem			}
1141221190Srmacklem			bcopy(args.fh, nfh, args.fhsize);
1142221190Srmacklem		} else {
1143221190Srmacklem			args.fhsize = 0;
1144221190Srmacklem		}
1145221190Srmacklem		(void) vfs_getopt(mp->mnt_optnew, "hostname",
1146221190Srmacklem		    (void **)&args.hostname, &len);
1147221190Srmacklem		if (args.hostname == NULL) {
1148221190Srmacklem			vfs_mount_error(mp, "Invalid hostname");
1149221190Srmacklem			error = EINVAL;
1150221190Srmacklem			goto out;
1151221190Srmacklem		}
1152221190Srmacklem		bcopy(args.hostname, hst, MNAMELEN);
1153221190Srmacklem		hst[MNAMELEN - 1] = '\0';
1154192585Srmacklem	}
1155192585Srmacklem
1156192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1157192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1158286141Srmacklem	else {
1159192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1160286141Srmacklem		cp = strchr(srvkrbname, ':');
1161286141Srmacklem		if (cp != NULL)
1162286141Srmacklem			*cp = '\0';
1163286141Srmacklem	}
1164221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1165192585Srmacklem
1166192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1167192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1168192585Srmacklem	else
1169191783Srmacklem		krbname[0] = '\0';
1170221014Srmacklem	krbnamelen = strlen(krbname);
1171192585Srmacklem
1172192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1173192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1174192585Srmacklem	else
1175191783Srmacklem		dirpath[0] = '\0';
1176221014Srmacklem	dirlen = strlen(dirpath);
1177192585Srmacklem
1178222075Srmacklem	if (has_nfs_args_opt == 0) {
1179222075Srmacklem		if (vfs_getopt(mp->mnt_optnew, "addr",
1180222075Srmacklem		    (void **)&args.addr, &args.addrlen) == 0) {
1181222075Srmacklem			if (args.addrlen > SOCK_MAXADDRLEN) {
1182222075Srmacklem				error = ENAMETOOLONG;
1183222075Srmacklem				goto out;
1184222075Srmacklem			}
1185222075Srmacklem			nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1186222075Srmacklem			bcopy(args.addr, nam, args.addrlen);
1187222075Srmacklem			nam->sa_len = args.addrlen;
1188222075Srmacklem		} else {
1189222075Srmacklem			vfs_mount_error(mp, "No server address");
1190222075Srmacklem			error = EINVAL;
1191191783Srmacklem			goto out;
1192191783Srmacklem		}
1193191783Srmacklem	}
1194192585Srmacklem
1195191783Srmacklem	args.fh = nfh;
1196221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1197221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1198244042Srmacklem	    nametimeo, negnametimeo, minvers);
1199191783Srmacklemout:
1200191783Srmacklem	if (!error) {
1201191783Srmacklem		MNT_ILOCK(mp);
1202282270Srmacklem		mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_NO_IOPF |
1203282270Srmacklem		    MNTK_USES_BCACHE;
1204191783Srmacklem		MNT_IUNLOCK(mp);
1205191783Srmacklem	}
1206191783Srmacklem	return (error);
1207191783Srmacklem}
1208191783Srmacklem
1209191783Srmacklem
1210191783Srmacklem/*
1211191783Srmacklem * VFS Operations.
1212191783Srmacklem *
1213191783Srmacklem * mount system call
1214191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1215191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1216191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1217191783Srmacklem * an error after that means that I have to release the mbuf.
1218191783Srmacklem */
1219191783Srmacklem/* ARGSUSED */
1220191783Srmacklemstatic int
1221230249Smckusicknfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
1222191783Srmacklem{
1223191783Srmacklem	int error;
1224191783Srmacklem	struct nfs_args args;
1225191783Srmacklem
1226191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1227191783Srmacklem	if (error)
1228191783Srmacklem		return error;
1229191783Srmacklem
1230191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1231191783Srmacklem
1232191783Srmacklem	error = kernel_mount(ma, flags);
1233191783Srmacklem	return (error);
1234191783Srmacklem}
1235191783Srmacklem
1236191783Srmacklem/*
1237191783Srmacklem * Common code for mount and mountroot
1238191783Srmacklem */
1239191783Srmacklemstatic int
1240191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1241221014Srmacklem    char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
1242221014Srmacklem    u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
1243244042Srmacklem    struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo,
1244244042Srmacklem    int minvers)
1245191783Srmacklem{
1246191783Srmacklem	struct nfsmount *nmp;
1247191783Srmacklem	struct nfsnode *np;
1248195762Srmacklem	int error, trycnt, ret;
1249191783Srmacklem	struct nfsvattr nfsva;
1250244042Srmacklem	struct nfsclclient *clp;
1251244042Srmacklem	struct nfsclds *dsp, *tdsp;
1252244042Srmacklem	uint32_t lease;
1253191783Srmacklem	static u_int64_t clval = 0;
1254191783Srmacklem
1255244042Srmacklem	NFSCL_DEBUG(3, "in mnt\n");
1256244042Srmacklem	clp = NULL;
1257191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1258191783Srmacklem		nmp = VFSTONFS(mp);
1259191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1260191783Srmacklem		FREE(nam, M_SONAME);
1261191783Srmacklem		return (0);
1262191783Srmacklem	} else {
1263191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1264221014Srmacklem		    krbnamelen + dirlen + srvkrbnamelen + 2,
1265221014Srmacklem		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
1266191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1267191783Srmacklem		if (clval == 0)
1268191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1269191783Srmacklem		nmp->nm_clval = clval++;
1270221014Srmacklem		nmp->nm_krbnamelen = krbnamelen;
1271221014Srmacklem		nmp->nm_dirpathlen = dirlen;
1272221014Srmacklem		nmp->nm_srvkrbnamelen = srvkrbnamelen;
1273192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1274191783Srmacklem			/*
1275192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1276192675Srmacklem			 * the nfsv4 state handling operations if there is
1277192675Srmacklem			 * no host based principal set. Use the uid of
1278192675Srmacklem			 * this user if not root, since they are doing the
1279192675Srmacklem			 * mount. I don't think setting this for root will
1280192675Srmacklem			 * work, since root normally does not have user
1281192675Srmacklem			 * credentials in a credentials cache.
1282191783Srmacklem			 */
1283192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1284191783Srmacklem		} else {
1285191783Srmacklem			/*
1286192675Srmacklem			 * Just set to -1, so it won't be used.
1287191783Srmacklem			 */
1288191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1289191783Srmacklem		}
1290191783Srmacklem
1291191783Srmacklem		/* Copy and null terminate all the names */
1292191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1293191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1294191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1295191783Srmacklem		}
1296191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1297191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1298191783Srmacklem			    nmp->nm_dirpathlen);
1299191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1300191783Srmacklem			    + 1] = '\0';
1301191783Srmacklem		}
1302191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1303191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1304191783Srmacklem			    nmp->nm_srvkrbnamelen);
1305191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1306191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1307191783Srmacklem		}
1308191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1309191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1310191783Srmacklem		mp->mnt_data = nmp;
1311214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1312216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1313191783Srmacklem	}
1314191783Srmacklem	vfs_getnewfsid(mp);
1315191783Srmacklem	nmp->nm_mountp = mp;
1316230547Sjhb	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1317227493Srmacklem
1318227493Srmacklem	/*
1319230547Sjhb	 * Since nfs_decode_args() might optionally set them, these
1320230547Sjhb	 * need to be set to defaults before the call, so that the
1321230547Sjhb	 * optional settings aren't overwritten.
1322227493Srmacklem	 */
1323230547Sjhb	nmp->nm_nametimeo = nametimeo;
1324203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1325227493Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1326227493Srmacklem	nmp->nm_retry = NFS_RETRANS;
1327227493Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1328282362Smav
1329282362Smav	/* This is empirical approximation of sqrt(hibufspace) * 256. */
1330282362Smav	nmp->nm_wcommitsize = NFS_MAXBSIZE / 256;
1331282362Smav	while ((long)nmp->nm_wcommitsize * nmp->nm_wcommitsize < hibufspace)
1332282362Smav		nmp->nm_wcommitsize *= 2;
1333282362Smav	nmp->nm_wcommitsize *= 256;
1334282362Smav
1335244042Srmacklem	if ((argp->flags & NFSMNT_NFSV4) != 0)
1336244042Srmacklem		nmp->nm_minorvers = minvers;
1337244042Srmacklem	else
1338244042Srmacklem		nmp->nm_minorvers = 0;
1339191783Srmacklem
1340214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1341192585Srmacklem
1342191783Srmacklem	/*
1343191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1344191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1345191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1346191783Srmacklem	 *
1347191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1348191783Srmacklem	 * that we can handle until we find out otherwise.
1349191783Srmacklem	 */
1350191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1351191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1352191783Srmacklem	else
1353221537Srmacklem		nmp->nm_maxfilesize = OFF_MAX;
1354191783Srmacklem
1355191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1356191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1357191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1358191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1359191783Srmacklem	}
1360191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1361191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1362191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1363191783Srmacklem		nmp->nm_tprintf_delay = 0;
1364191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1365191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1366191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1367191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1368191783Srmacklem	if (nmp->nm_fhsize > 0)
1369191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1370191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1371191783Srmacklem	nmp->nm_nam = nam;
1372191783Srmacklem	/* Set up the sockets and per-host congestion */
1373191783Srmacklem	nmp->nm_sotype = argp->sotype;
1374191783Srmacklem	nmp->nm_soproto = argp->proto;
1375191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1376191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1377191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1378191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1379191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1380191783Srmacklem	else
1381191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1382191783Srmacklem
1383191783Srmacklem
1384191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1385191783Srmacklem		goto bad;
1386244042Srmacklem	/* For NFSv4.1, get the clientid now. */
1387244042Srmacklem	if (nmp->nm_minorvers > 0) {
1388244042Srmacklem		NFSCL_DEBUG(3, "at getcl\n");
1389244042Srmacklem		error = nfscl_getcl(mp, cred, td, 0, &clp);
1390244042Srmacklem		NFSCL_DEBUG(3, "aft getcl=%d\n", error);
1391244042Srmacklem		if (error != 0)
1392244042Srmacklem			goto bad;
1393244042Srmacklem	}
1394191783Srmacklem
1395191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1396191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1397244042Srmacklem		NFSCL_DEBUG(3, "in dirp\n");
1398191783Srmacklem		/*
1399191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1400191783Srmacklem		 * path needs to be looked up.
1401191783Srmacklem		 */
1402191783Srmacklem		trycnt = 3;
1403191783Srmacklem		do {
1404191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1405191783Srmacklem			    cred, td);
1406244042Srmacklem			NFSCL_DEBUG(3, "aft dirp=%d\n", error);
1407191783Srmacklem			if (error)
1408207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1409191783Srmacklem		} while (error && --trycnt > 0);
1410191783Srmacklem		if (error) {
1411191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1412191783Srmacklem			goto bad;
1413191783Srmacklem		}
1414191783Srmacklem	}
1415244042Srmacklem
1416244042Srmacklem	/*
1417244042Srmacklem	 * A reference count is needed on the nfsnode representing the
1418244042Srmacklem	 * remote root.  If this object is not persistent, then backward
1419244042Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1420244042Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1421244042Srmacklem	 * this problem, because one can identify root inodes by their
1422244042Srmacklem	 * number == ROOTINO (2).
1423244042Srmacklem	 */
1424191783Srmacklem	if (nmp->nm_fhsize > 0) {
1425195762Srmacklem		/*
1426195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1427195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1428195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1429195762Srmacklem		 */
1430195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1431220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1432220732Srmacklem		    LK_EXCLUSIVE);
1433191783Srmacklem		if (error)
1434191783Srmacklem			goto bad;
1435191783Srmacklem		*vpp = NFSTOV(np);
1436191783Srmacklem
1437191783Srmacklem		/*
1438191783Srmacklem		 * Get file attributes and transfer parameters for the
1439191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1440191783Srmacklem		 * (*vpp)->v_type with the correct value.
1441191783Srmacklem		 */
1442191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1443244042Srmacklem		    cred, td, &nfsva, NULL, &lease);
1444191783Srmacklem		if (ret) {
1445191783Srmacklem			/*
1446191783Srmacklem			 * Just set default values to get things going.
1447191783Srmacklem			 */
1448191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1449191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1450191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1451191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1452191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1453191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1454191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1455191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1456191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1457191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1458244042Srmacklem			lease = 60;
1459191783Srmacklem		}
1460191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1461244042Srmacklem		if (nmp->nm_minorvers > 0) {
1462244042Srmacklem			NFSCL_DEBUG(3, "lease=%d\n", (int)lease);
1463244042Srmacklem			NFSLOCKCLSTATE();
1464244042Srmacklem			clp->nfsc_renew = NFSCL_RENEW(lease);
1465244042Srmacklem			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
1466244042Srmacklem			clp->nfsc_clientidrev++;
1467244042Srmacklem			if (clp->nfsc_clientidrev == 0)
1468244042Srmacklem				clp->nfsc_clientidrev++;
1469244042Srmacklem			NFSUNLOCKCLSTATE();
1470244042Srmacklem			/*
1471244042Srmacklem			 * Mount will succeed, so the renew thread can be
1472244042Srmacklem			 * started now.
1473244042Srmacklem			 */
1474244042Srmacklem			nfscl_start_renewthread(clp);
1475244042Srmacklem			nfscl_clientrelease(clp);
1476244042Srmacklem		}
1477191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1478191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1479191783Srmacklem
1480222233Srmacklem		/* Mark if the mount point supports NFSv4 ACLs. */
1481222233Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0 && nfsrv_useacl != 0 &&
1482222233Srmacklem		    ret == 0 &&
1483222233Srmacklem		    NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL)) {
1484222233Srmacklem			MNT_ILOCK(mp);
1485222233Srmacklem			mp->mnt_flag |= MNT_NFS4ACLS;
1486222233Srmacklem			MNT_IUNLOCK(mp);
1487222233Srmacklem		}
1488222233Srmacklem
1489191783Srmacklem		/*
1490191783Srmacklem		 * Lose the lock but keep the ref.
1491191783Srmacklem		 */
1492224082Szack		NFSVOPUNLOCK(*vpp, 0);
1493191783Srmacklem		return (0);
1494191783Srmacklem	}
1495191783Srmacklem	error = EIO;
1496191783Srmacklem
1497191783Srmacklembad:
1498244042Srmacklem	if (clp != NULL)
1499244042Srmacklem		nfscl_clientrelease(clp);
1500191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1501191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1502253049Srmacklem	if (nmp->nm_sockreq.nr_auth != NULL)
1503253049Srmacklem		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
1504191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1505191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1506244042Srmacklem	if (nmp->nm_clp != NULL) {
1507244042Srmacklem		NFSLOCKCLSTATE();
1508244042Srmacklem		LIST_REMOVE(nmp->nm_clp, nfsc_list);
1509244042Srmacklem		NFSUNLOCKCLSTATE();
1510244042Srmacklem		free(nmp->nm_clp, M_NFSCLCLIENT);
1511244042Srmacklem	}
1512244042Srmacklem	TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
1513244042Srmacklem		nfscl_freenfsclds(dsp);
1514191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1515191783Srmacklem	FREE(nam, M_SONAME);
1516191783Srmacklem	return (error);
1517191783Srmacklem}
1518191783Srmacklem
1519191783Srmacklem/*
1520191783Srmacklem * unmount system call
1521191783Srmacklem */
1522191783Srmacklemstatic int
1523191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1524191783Srmacklem{
1525191990Sattilio	struct thread *td;
1526191783Srmacklem	struct nfsmount *nmp;
1527249630Srmacklem	int error, flags = 0, i, trycnt = 0;
1528244042Srmacklem	struct nfsclds *dsp, *tdsp;
1529191783Srmacklem
1530191990Sattilio	td = curthread;
1531191990Sattilio
1532191783Srmacklem	if (mntflags & MNT_FORCE)
1533191783Srmacklem		flags |= FORCECLOSE;
1534191783Srmacklem	nmp = VFSTONFS(mp);
1535191783Srmacklem	/*
1536191783Srmacklem	 * Goes something like this..
1537191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1538191783Srmacklem	 * - Close the socket
1539191783Srmacklem	 * - Free up the data structures
1540191783Srmacklem	 */
1541191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1542191783Srmacklem	if (mntflags & MNT_FORCE) {
1543191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1544191783Srmacklem		if (error)
1545191783Srmacklem			goto out;
1546191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1547191783Srmacklem		nfscl_umount(nmp, td);
1548191783Srmacklem	}
1549191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1550191783Srmacklem	do {
1551191783Srmacklem		error = vflush(mp, 1, flags, td);
1552191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1553207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1554191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1555191783Srmacklem	if (error)
1556191783Srmacklem		goto out;
1557191783Srmacklem
1558191783Srmacklem	/*
1559191783Srmacklem	 * We are now committed to the unmount.
1560191783Srmacklem	 */
1561191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1562191783Srmacklem		nfscl_umount(nmp, td);
1563249630Srmacklem	/* Make sure no nfsiods are assigned to this mount. */
1564249630Srmacklem	mtx_lock(&ncl_iod_mutex);
1565249630Srmacklem	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
1566249630Srmacklem		if (ncl_iodmount[i] == nmp) {
1567249630Srmacklem			ncl_iodwant[i] = NFSIOD_AVAILABLE;
1568249630Srmacklem			ncl_iodmount[i] = NULL;
1569249630Srmacklem		}
1570249630Srmacklem	mtx_unlock(&ncl_iod_mutex);
1571191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1572191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1573191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1574253049Srmacklem	if (nmp->nm_sockreq.nr_auth != NULL)
1575253049Srmacklem		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
1576191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1577191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1578244042Srmacklem	TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
1579244042Srmacklem		nfscl_freenfsclds(dsp);
1580191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1581191783Srmacklemout:
1582191783Srmacklem	return (error);
1583191783Srmacklem}
1584191783Srmacklem
1585191783Srmacklem/*
1586191783Srmacklem * Return root of a filesystem
1587191783Srmacklem */
1588191783Srmacklemstatic int
1589191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1590191783Srmacklem{
1591191783Srmacklem	struct vnode *vp;
1592191783Srmacklem	struct nfsmount *nmp;
1593191783Srmacklem	struct nfsnode *np;
1594191783Srmacklem	int error;
1595191783Srmacklem
1596191783Srmacklem	nmp = VFSTONFS(mp);
1597220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1598191783Srmacklem	if (error)
1599191783Srmacklem		return error;
1600191783Srmacklem	vp = NFSTOV(np);
1601191783Srmacklem	/*
1602191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1603191783Srmacklem	 */
1604191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1605191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1606191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1607191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1608191783Srmacklem	} else
1609191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1610191783Srmacklem	if (vp->v_type == VNON)
1611191783Srmacklem	    vp->v_type = VDIR;
1612191783Srmacklem	vp->v_vflag |= VV_ROOT;
1613191783Srmacklem	*vpp = vp;
1614191783Srmacklem	return (0);
1615191783Srmacklem}
1616191783Srmacklem
1617191783Srmacklem/*
1618191783Srmacklem * Flush out the buffer cache
1619191783Srmacklem */
1620191783Srmacklem/* ARGSUSED */
1621191783Srmacklemstatic int
1622191990Sattilionfs_sync(struct mount *mp, int waitfor)
1623191783Srmacklem{
1624191783Srmacklem	struct vnode *vp, *mvp;
1625191990Sattilio	struct thread *td;
1626191783Srmacklem	int error, allerror = 0;
1627191783Srmacklem
1628191990Sattilio	td = curthread;
1629191990Sattilio
1630222329Srmacklem	MNT_ILOCK(mp);
1631191783Srmacklem	/*
1632222329Srmacklem	 * If a forced dismount is in progress, return from here so that
1633222329Srmacklem	 * the umount(2) syscall doesn't get stuck in VFS_SYNC() before
1634222329Srmacklem	 * calling VFS_UNMOUNT().
1635222329Srmacklem	 */
1636222329Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1637222329Srmacklem		MNT_IUNLOCK(mp);
1638222329Srmacklem		return (EBADF);
1639222329Srmacklem	}
1640234386Smckusick	MNT_IUNLOCK(mp);
1641222329Srmacklem
1642222329Srmacklem	/*
1643191783Srmacklem	 * Force stale buffer cache information to be flushed.
1644191783Srmacklem	 */
1645191783Srmacklemloop:
1646234386Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
1647191783Srmacklem		/* XXX Racy bv_cnt check. */
1648224083Szack		if (NFSVOPISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1649191783Srmacklem		    waitfor == MNT_LAZY) {
1650191783Srmacklem			VI_UNLOCK(vp);
1651191783Srmacklem			continue;
1652191783Srmacklem		}
1653191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1654234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
1655191783Srmacklem			goto loop;
1656191783Srmacklem		}
1657191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1658191783Srmacklem		if (error)
1659191783Srmacklem			allerror = error;
1660224082Szack		NFSVOPUNLOCK(vp, 0);
1661191783Srmacklem		vrele(vp);
1662191783Srmacklem	}
1663191783Srmacklem	return (allerror);
1664191783Srmacklem}
1665191783Srmacklem
1666191783Srmacklemstatic int
1667191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1668191783Srmacklem{
1669191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1670191783Srmacklem	struct vfsquery vq;
1671191783Srmacklem	int error;
1672191783Srmacklem
1673191783Srmacklem	bzero(&vq, sizeof(vq));
1674191783Srmacklem	switch (op) {
1675191783Srmacklem#if 0
1676191783Srmacklem	case VFS_CTL_NOLOCKS:
1677191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1678191783Srmacklem 		if (req->oldptr != NULL) {
1679191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1680191783Srmacklem 			if (error)
1681191783Srmacklem 				return (error);
1682191783Srmacklem 		}
1683191783Srmacklem 		if (req->newptr != NULL) {
1684191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1685191783Srmacklem 			if (error)
1686191783Srmacklem 				return (error);
1687191783Srmacklem			if (val)
1688191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1689191783Srmacklem			else
1690191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1691191783Srmacklem 		}
1692191783Srmacklem		break;
1693191783Srmacklem#endif
1694191783Srmacklem	case VFS_CTL_QUERY:
1695191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1696191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1697191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1698191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1699191783Srmacklem#if 0
1700191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1701191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1702191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1703191783Srmacklem#endif
1704191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1705191783Srmacklem		break;
1706191783Srmacklem 	case VFS_CTL_TIMEO:
1707191783Srmacklem 		if (req->oldptr != NULL) {
1708191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1709191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1710191783Srmacklem 			if (error)
1711191783Srmacklem 				return (error);
1712191783Srmacklem 		}
1713191783Srmacklem 		if (req->newptr != NULL) {
1714191783Srmacklem			error = vfs_suser(mp, req->td);
1715191783Srmacklem			if (error)
1716191783Srmacklem				return (error);
1717191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1718191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1719191783Srmacklem 			if (error)
1720191783Srmacklem 				return (error);
1721191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1722191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1723191783Srmacklem 		}
1724191783Srmacklem		break;
1725191783Srmacklem	default:
1726191783Srmacklem		return (ENOTSUP);
1727191783Srmacklem	}
1728191783Srmacklem	return (0);
1729191783Srmacklem}
1730191783Srmacklem
1731214048Srmacklem/*
1732255136Srmacklem * Purge any RPCs in progress, so that they will all return errors.
1733255136Srmacklem * This allows dounmount() to continue as far as VFS_UNMOUNT() for a
1734255136Srmacklem * forced dismount.
1735255136Srmacklem */
1736255136Srmacklemstatic void
1737255136Srmacklemnfs_purge(struct mount *mp)
1738255136Srmacklem{
1739255136Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1740255136Srmacklem
1741255136Srmacklem	newnfs_nmcancelreqs(nmp);
1742255136Srmacklem}
1743255136Srmacklem
1744255136Srmacklem/*
1745214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1746214048Srmacklem */
1747214048Srmacklemstatic void
1748214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1749216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1750216931Srmacklem    struct timeval *timeop)
1751214048Srmacklem{
1752214048Srmacklem	struct nfsmount *nmp;
1753214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1754214048Srmacklem
1755214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1756214048Srmacklem	if (fhlenp != NULL)
1757214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1758214048Srmacklem	if (fhp != NULL)
1759214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1760214048Srmacklem	if (sp != NULL)
1761214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1762214048Srmacklem	if (is_v3p != NULL)
1763214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1764214048Srmacklem	if (sizep != NULL)
1765214048Srmacklem		*sizep = np->n_size;
1766216931Srmacklem	if (timeop != NULL) {
1767216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1768216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1769216931Srmacklem	}
1770214048Srmacklem}
1771214048Srmacklem
1772243782Srmacklem/*
1773243782Srmacklem * This function prints out an option name, based on the conditional
1774243782Srmacklem * argument.
1775243782Srmacklem */
1776243782Srmacklemstatic __inline void nfscl_printopt(struct nfsmount *nmp, int testval,
1777243782Srmacklem    char *opt, char **buf, size_t *blen)
1778243782Srmacklem{
1779243782Srmacklem	int len;
1780243782Srmacklem
1781243782Srmacklem	if (testval != 0 && *blen > strlen(opt)) {
1782243782Srmacklem		len = snprintf(*buf, *blen, "%s", opt);
1783243782Srmacklem		if (len != strlen(opt))
1784243782Srmacklem			printf("EEK!!\n");
1785243782Srmacklem		*buf += len;
1786243782Srmacklem		*blen -= len;
1787243782Srmacklem	}
1788243782Srmacklem}
1789243782Srmacklem
1790243782Srmacklem/*
1791243782Srmacklem * This function printf out an options integer value.
1792243782Srmacklem */
1793243782Srmacklemstatic __inline void nfscl_printoptval(struct nfsmount *nmp, int optval,
1794243782Srmacklem    char *opt, char **buf, size_t *blen)
1795243782Srmacklem{
1796243782Srmacklem	int len;
1797243782Srmacklem
1798243782Srmacklem	if (*blen > strlen(opt) + 1) {
1799243782Srmacklem		/* Could result in truncated output string. */
1800243782Srmacklem		len = snprintf(*buf, *blen, "%s=%d", opt, optval);
1801243782Srmacklem		if (len < *blen) {
1802243782Srmacklem			*buf += len;
1803243782Srmacklem			*blen -= len;
1804243782Srmacklem		}
1805243782Srmacklem	}
1806243782Srmacklem}
1807243782Srmacklem
1808243782Srmacklem/*
1809243782Srmacklem * Load the option flags and values into the buffer.
1810243782Srmacklem */
1811243782Srmacklemvoid nfscl_retopts(struct nfsmount *nmp, char *buffer, size_t buflen)
1812243782Srmacklem{
1813243782Srmacklem	char *buf;
1814243782Srmacklem	size_t blen;
1815243782Srmacklem
1816243782Srmacklem	buf = buffer;
1817243782Srmacklem	blen = buflen;
1818243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV4) != 0, "nfsv4", &buf,
1819243782Srmacklem	    &blen);
1820244056Srmacklem	if ((nmp->nm_flag & NFSMNT_NFSV4) != 0) {
1821244056Srmacklem		nfscl_printoptval(nmp, nmp->nm_minorvers, ",minorversion", &buf,
1822244056Srmacklem		    &blen);
1823244056Srmacklem		nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_PNFS) != 0, ",pnfs",
1824244056Srmacklem		    &buf, &blen);
1825244056Srmacklem	}
1826243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV3) != 0, "nfsv3", &buf,
1827243782Srmacklem	    &blen);
1828243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0,
1829243782Srmacklem	    "nfsv2", &buf, &blen);
1830243782Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype == SOCK_STREAM, ",tcp", &buf, &blen);
1831243782Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype != SOCK_STREAM, ",udp", &buf, &blen);
1832243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RESVPORT) != 0, ",resvport",
1833243782Srmacklem	    &buf, &blen);
1834243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCONN) != 0, ",noconn",
1835243782Srmacklem	    &buf, &blen);
1836243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) == 0, ",hard", &buf,
1837243782Srmacklem	    &blen);
1838243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) != 0, ",soft", &buf,
1839243782Srmacklem	    &blen);
1840243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_INT) != 0, ",intr", &buf,
1841243782Srmacklem	    &blen);
1842243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) == 0, ",cto", &buf,
1843243782Srmacklem	    &blen);
1844243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) != 0, ",nocto", &buf,
1845243782Srmacklem	    &blen);
1846260107Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NONCONTIGWR) != 0,
1847260107Srmacklem	    ",noncontigwr", &buf, &blen);
1848243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1849243782Srmacklem	    0, ",lockd", &buf, &blen);
1850243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1851243782Srmacklem	    NFSMNT_NOLOCKD, ",nolockd", &buf, &blen);
1852243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RDIRPLUS) != 0, ",rdirplus",
1853243782Srmacklem	    &buf, &blen);
1854243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_KERB) == 0, ",sec=sys",
1855243782Srmacklem	    &buf, &blen);
1856243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1857243782Srmacklem	    NFSMNT_PRIVACY)) == NFSMNT_KERB, ",sec=krb5", &buf, &blen);
1858243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1859243782Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_INTEGRITY), ",sec=krb5i",
1860243782Srmacklem	    &buf, &blen);
1861243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1862243782Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_PRIVACY), ",sec=krb5p",
1863243782Srmacklem	    &buf, &blen);
1864243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmin, ",acdirmin", &buf, &blen);
1865243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmax, ",acdirmax", &buf, &blen);
1866243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmin, ",acregmin", &buf, &blen);
1867243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmax, ",acregmax", &buf, &blen);
1868243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_nametimeo, ",nametimeo", &buf, &blen);
1869243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_negnametimeo, ",negnametimeo", &buf,
1870243782Srmacklem	    &blen);
1871243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_rsize, ",rsize", &buf, &blen);
1872243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_wsize, ",wsize", &buf, &blen);
1873243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_readdirsize, ",readdirsize", &buf,
1874243782Srmacklem	    &blen);
1875243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_readahead, ",readahead", &buf, &blen);
1876243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_wcommitsize, ",wcommitsize", &buf,
1877243782Srmacklem	    &blen);
1878243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_timeo, ",timeout", &buf, &blen);
1879243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_retry, ",retrans", &buf, &blen);
1880243782Srmacklem}
1881243782Srmacklem
1882