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.2/sys/fs/nfsclient/nfs_clvfsops.c 282933 2015-05-14 22:50:07Z 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
214191783Srmacklem	 * space.
215191783Srmacklem	 */
216191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
217191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
218191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
219191783Srmacklem	return (iosize);
220191783Srmacklem}
221191783Srmacklem
222191783Srmacklemstatic void
223191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
224191783Srmacklem{
225191783Srmacklem
226191783Srmacklem	args->version = NFS_ARGSVERSION;
227191783Srmacklem	args->addr = oargs->addr;
228191783Srmacklem	args->addrlen = oargs->addrlen;
229191783Srmacklem	args->sotype = oargs->sotype;
230191783Srmacklem	args->proto = oargs->proto;
231191783Srmacklem	args->fh = oargs->fh;
232191783Srmacklem	args->fhsize = oargs->fhsize;
233191783Srmacklem	args->flags = oargs->flags;
234191783Srmacklem	args->wsize = oargs->wsize;
235191783Srmacklem	args->rsize = oargs->rsize;
236191783Srmacklem	args->readdirsize = oargs->readdirsize;
237191783Srmacklem	args->timeo = oargs->timeo;
238191783Srmacklem	args->retrans = oargs->retrans;
239191783Srmacklem	args->readahead = oargs->readahead;
240191783Srmacklem	args->hostname = oargs->hostname;
241191783Srmacklem}
242191783Srmacklem
243191783Srmacklemstatic void
244191783Srmacklemnfs_convert_diskless(void)
245191783Srmacklem{
246191783Srmacklem
247221040Srmacklem	bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif,
248221040Srmacklem		sizeof(struct ifaliasreq));
249221040Srmacklem	bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway,
250221040Srmacklem		sizeof(struct sockaddr_in));
251221040Srmacklem	nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args);
252221040Srmacklem	if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
253221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_MYFH;
254221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH);
255191783Srmacklem	} else {
256221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_V2FH;
257221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH);
258191783Srmacklem	}
259221040Srmacklem	bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr,
260221040Srmacklem		sizeof(struct sockaddr_in));
261221040Srmacklem	bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN);
262221040Srmacklem	nfsv3_diskless.root_time = nfs_diskless.root_time;
263221040Srmacklem	bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam,
264221040Srmacklem		MAXHOSTNAMELEN);
265221040Srmacklem	nfs_diskless_valid = 3;
266191783Srmacklem}
267191783Srmacklem
268191783Srmacklem/*
269191783Srmacklem * nfs statfs call
270191783Srmacklem */
271191783Srmacklemstatic int
272191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
273191783Srmacklem{
274191783Srmacklem	struct vnode *vp;
275191990Sattilio	struct thread *td;
276191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
277191783Srmacklem	struct nfsvattr nfsva;
278191783Srmacklem	struct nfsfsinfo fs;
279191783Srmacklem	struct nfsstatfs sb;
280191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
281191783Srmacklem	struct nfsnode *np;
282191783Srmacklem
283191990Sattilio	td = curthread;
284191990Sattilio
285191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
286191783Srmacklem	if (error)
287191783Srmacklem		return (error);
288220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
289191783Srmacklem	if (error) {
290191783Srmacklem		vfs_unbusy(mp);
291191783Srmacklem		return (error);
292191783Srmacklem	}
293191783Srmacklem	vp = NFSTOV(np);
294191783Srmacklem	mtx_lock(&nmp->nm_mtx);
295191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
296191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
297191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
298191783Srmacklem		    &attrflag, NULL);
299191783Srmacklem		if (!error)
300191783Srmacklem			gotfsinfo = 1;
301191783Srmacklem	} else
302191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
303191783Srmacklem	if (!error)
304191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
305191783Srmacklem		    &attrflag, NULL);
306244042Srmacklem	if (error != 0)
307244042Srmacklem		NFSCL_DEBUG(2, "statfs=%d\n", error);
308191783Srmacklem	if (attrflag == 0) {
309191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
310244042Srmacklem		    td->td_ucred, td, &nfsva, NULL, NULL);
311191783Srmacklem		if (ret) {
312191783Srmacklem			/*
313191783Srmacklem			 * Just set default values to get things going.
314191783Srmacklem			 */
315191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
316191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
317191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
318191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
319191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
320191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
321191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
322191783Srmacklem			nfsva.na_vattr.va_gen = 1;
323191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
324191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
325191783Srmacklem		}
326191783Srmacklem	}
327191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
328191783Srmacklem	if (!error) {
329191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
330191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
331191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
332191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
333191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
334191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
335191783Srmacklem	    if (sbp != &mp->mnt_stat) {
336191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
337191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
338191783Srmacklem	    }
339191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
340191783Srmacklem	} else if (NFS_ISV4(vp)) {
341191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
342191783Srmacklem	}
343191783Srmacklem	vput(vp);
344191783Srmacklem	vfs_unbusy(mp);
345191783Srmacklem	return (error);
346191783Srmacklem}
347191783Srmacklem
348191783Srmacklem/*
349191783Srmacklem * nfs version 3 fsinfo rpc call
350191783Srmacklem */
351191783Srmacklemint
352191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
353191783Srmacklem    struct thread *td)
354191783Srmacklem{
355191783Srmacklem	struct nfsfsinfo fs;
356191783Srmacklem	struct nfsvattr nfsva;
357191783Srmacklem	int error, attrflag;
358191783Srmacklem
359191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
360191783Srmacklem	if (!error) {
361191783Srmacklem		if (attrflag)
362191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
363191783Srmacklem			    1);
364191783Srmacklem		mtx_lock(&nmp->nm_mtx);
365191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
366191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
367191783Srmacklem	}
368191783Srmacklem	return (error);
369191783Srmacklem}
370191783Srmacklem
371191783Srmacklem/*
372191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
373221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary
374191783Srmacklem * bootstrap.
375191783Srmacklem * It goes something like this:
376191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
377191783Srmacklem *   can talk to the server
378221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as
379191783Srmacklem *   a default gateway.
380191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
381191783Srmacklem *
382191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
383191783Srmacklem * structure, as well as other global NFS client variables here, as
384192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
385191783Srmacklem * client activity occurs.
386191783Srmacklem */
387221040Srmacklemstatic int
388221040Srmacklemnfs_mountroot(struct mount *mp)
389191783Srmacklem{
390192145Srmacklem	struct thread *td = curthread;
391221040Srmacklem	struct nfsv3_diskless *nd = &nfsv3_diskless;
392191783Srmacklem	struct socket *so;
393191783Srmacklem	struct vnode *vp;
394191783Srmacklem	struct ifreq ir;
395193066Sjamie	int error;
396191783Srmacklem	u_long l;
397191783Srmacklem	char buf[128];
398191783Srmacklem	char *cp;
399191783Srmacklem
400191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
401192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
402191783Srmacklem#elif defined(NFS_ROOT)
403191783Srmacklem	nfs_setup_diskless();
404191783Srmacklem#endif
405191783Srmacklem
406221040Srmacklem	if (nfs_diskless_valid == 0)
407191783Srmacklem		return (-1);
408221040Srmacklem	if (nfs_diskless_valid == 1)
409191783Srmacklem		nfs_convert_diskless();
410191783Srmacklem
411191783Srmacklem	/*
412191783Srmacklem	 * XXX splnet, so networks will receive...
413191783Srmacklem	 */
414191783Srmacklem	splnet();
415191783Srmacklem
416191783Srmacklem	/*
417191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
418191783Srmacklem	 * talk to the server.
419191783Srmacklem	 */
420191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
421191783Srmacklem	    td->td_ucred, td);
422191783Srmacklem	if (error)
423192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
424191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
425191783Srmacklem
426191783Srmacklem#if 0 /* XXX Bad idea */
427191783Srmacklem	/*
428191783Srmacklem	 * We might not have been told the right interface, so we pass
429191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
430191783Srmacklem	 * one of them configured.
431191783Srmacklem	 */
432191783Srmacklem
433191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
434191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
435191783Srmacklem		nd->myif.ifra_name[i] <= '9';
436191783Srmacklem		nd->myif.ifra_name[i] ++) {
437191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
438191783Srmacklem		if(!error)
439191783Srmacklem			break;
440191783Srmacklem	}
441191783Srmacklem#endif
442191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
443191783Srmacklem	if (error)
444192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
445191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
446191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
447191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
448191783Srmacklem		freeenv(cp);
449191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
450191783Srmacklem		if (error)
451192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
452191783Srmacklem	}
453191783Srmacklem	soclose(so);
454191783Srmacklem
455191783Srmacklem	/*
456191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
457191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
458191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
459191783Srmacklem	 * to avoid panicking inappropriately in that situation.
460191783Srmacklem	 */
461191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
462191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
463191783Srmacklem		struct sockaddr_in mask, sin;
464191783Srmacklem
465191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
466191783Srmacklem		sin = mask;
467191783Srmacklem		sin.sin_family = AF_INET;
468191783Srmacklem		sin.sin_len = sizeof(sin);
469192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
470218757Sbz		CURVNET_SET(TD_TO_VNET(td));
471231852Sbz		error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&sin,
472191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
473191783Srmacklem		    (struct sockaddr *)&mask,
474231852Sbz		    RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB);
475218757Sbz		CURVNET_RESTORE();
476191783Srmacklem		if (error)
477192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
478191783Srmacklem	}
479191783Srmacklem
480191783Srmacklem	/*
481191783Srmacklem	 * Create the rootfs mount point.
482191783Srmacklem	 */
483191783Srmacklem	nd->root_args.fh = nd->root_fh;
484191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
485191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
486191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
487191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
488191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
489191783Srmacklem	printf("NFS ROOT: %s\n", buf);
490192145Srmacklem	nd->root_args.hostname = buf;
491191783Srmacklem	if ((error = nfs_mountdiskless(buf,
492191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
493191783Srmacklem		return (error);
494191783Srmacklem	}
495191783Srmacklem
496191783Srmacklem	/*
497191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
498191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
499191783Srmacklem	 * mount the right /var based upon its preset value.
500191783Srmacklem	 */
501193066Sjamie	mtx_lock(&prison0.pr_mtx);
502194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
503194118Sjamie	    sizeof(prison0.pr_hostname));
504193066Sjamie	mtx_unlock(&prison0.pr_mtx);
505191783Srmacklem	inittodr(ntohl(nd->root_time));
506191783Srmacklem	return (0);
507191783Srmacklem}
508191783Srmacklem
509191783Srmacklem/*
510191783Srmacklem * Internal version of mount system call for diskless setup.
511191783Srmacklem */
512191783Srmacklemstatic int
513191783Srmacklemnfs_mountdiskless(char *path,
514191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
515191783Srmacklem    struct vnode **vpp, struct mount *mp)
516191783Srmacklem{
517191783Srmacklem	struct sockaddr *nam;
518221014Srmacklem	int dirlen, error;
519221014Srmacklem	char *dirpath;
520191783Srmacklem
521221014Srmacklem	/*
522221014Srmacklem	 * Find the directory path in "path", which also has the server's
523221014Srmacklem	 * name/ip address in it.
524221014Srmacklem	 */
525221014Srmacklem	dirpath = strchr(path, ':');
526221014Srmacklem	if (dirpath != NULL)
527221014Srmacklem		dirlen = strlen(++dirpath);
528221014Srmacklem	else
529221014Srmacklem		dirlen = 0;
530191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
531221014Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
532230547Sjhb	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO,
533244042Srmacklem	    NFS_DEFAULT_NEGNAMETIMEO, 0)) != 0) {
534192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
535191783Srmacklem		return (error);
536191783Srmacklem	}
537191783Srmacklem	return (0);
538191783Srmacklem}
539191783Srmacklem
540191783Srmacklemstatic void
541192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
542192585Srmacklem{
543192585Srmacklem	if (!strcmp(sec, "krb5"))
544192585Srmacklem		*flagsp |= NFSMNT_KERB;
545192585Srmacklem	else if (!strcmp(sec, "krb5i"))
546192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
547192585Srmacklem	else if (!strcmp(sec, "krb5p"))
548192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
549192585Srmacklem}
550192585Srmacklem
551192585Srmacklemstatic void
552191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
553214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
554191783Srmacklem{
555191783Srmacklem	int s;
556191783Srmacklem	int adjsock;
557214048Srmacklem	char *p;
558191783Srmacklem
559191783Srmacklem	s = splnet();
560191783Srmacklem
561191783Srmacklem	/*
562191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
563191783Srmacklem	 * an update.  If this is not an update, then either the read-only
564191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
565191783Srmacklem	 * intentionally at some previous point.
566191783Srmacklem	 */
567191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
568191783Srmacklem		MNT_ILOCK(mp);
569191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
570191783Srmacklem		MNT_IUNLOCK(mp);
571191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
572191783Srmacklem		MNT_ILOCK(mp);
573191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
574191783Srmacklem		MNT_IUNLOCK(mp);
575191783Srmacklem	}
576191783Srmacklem
577191783Srmacklem	/*
578191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
579191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
580191783Srmacklem	 * and soft timeout behavior.
581191783Srmacklem	 */
582191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
583191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
584191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
585220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
586220739Srmacklem			nmp->nm_retry = INT_MAX;
587220739Srmacklem		else
588220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
589191783Srmacklem	}
590191783Srmacklem
591220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
592220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
593220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
594191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
595220739Srmacklem	}
596191783Srmacklem
597220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
598220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
599220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
600191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
601220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
602191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
603191783Srmacklem
604191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
605191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
606191783Srmacklem	splx(s);
607191783Srmacklem
608191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
609191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
610191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
611191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
612191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
613191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
614191783Srmacklem	}
615191783Srmacklem
616191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
617191783Srmacklem		nmp->nm_retry = argp->retrans;
618191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
619191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
620191783Srmacklem	}
621191783Srmacklem
622191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
623191783Srmacklem		nmp->nm_wsize = argp->wsize;
624274150Srmacklem		/*
625274150Srmacklem		 * Clip at the power of 2 below the size. There is an
626274150Srmacklem		 * issue (not isolated) that causes intermittent page
627274150Srmacklem		 * faults if this is not done.
628274150Srmacklem		 */
629274150Srmacklem		if (nmp->nm_wsize > NFS_FABLKSIZE)
630274150Srmacklem			nmp->nm_wsize = 1 << (fls(nmp->nm_wsize) - 1);
631274150Srmacklem		else
632191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
633191783Srmacklem	}
634191783Srmacklem
635191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
636191783Srmacklem		nmp->nm_rsize = argp->rsize;
637274150Srmacklem		/*
638274150Srmacklem		 * Clip at the power of 2 below the size. There is an
639274150Srmacklem		 * issue (not isolated) that causes intermittent page
640274150Srmacklem		 * faults if this is not done.
641274150Srmacklem		 */
642274150Srmacklem		if (nmp->nm_rsize > NFS_FABLKSIZE)
643274150Srmacklem			nmp->nm_rsize = 1 << (fls(nmp->nm_rsize) - 1);
644274150Srmacklem		else
645191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
646191783Srmacklem	}
647191783Srmacklem
648191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
649191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
650191783Srmacklem	}
651191783Srmacklem
652191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
653191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
654191783Srmacklem	else
655191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
656191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
657191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
658191783Srmacklem	else
659191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
660191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
661191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
662191783Srmacklem	else
663191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
664191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
665191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
666191783Srmacklem	else
667191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
668191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
669191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
670191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
671191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
672191783Srmacklem
673191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
674191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
675191783Srmacklem			nmp->nm_readahead = argp->readahead;
676191783Srmacklem		else
677191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
678191783Srmacklem	}
679191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
680191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
681191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
682191783Srmacklem		else
683191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
684191783Srmacklem	}
685191783Srmacklem
686191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
687191783Srmacklem		    (nmp->nm_soproto != argp->proto));
688191783Srmacklem
689191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
690191783Srmacklem		int haslock = 0, error = 0;
691191783Srmacklem
692191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
693191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
694191783Srmacklem			if (!error)
695191783Srmacklem				haslock = 1;
696191783Srmacklem		}
697191783Srmacklem		if (!error) {
698191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
699191783Srmacklem		    if (haslock)
700191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
701191783Srmacklem		    nmp->nm_sotype = argp->sotype;
702191783Srmacklem		    nmp->nm_soproto = argp->proto;
703191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
704191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
705191783Srmacklem			    cred, td, 0)) {
706191783Srmacklem				printf("newnfs_args: retrying connect\n");
707207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
708191783Srmacklem			}
709191783Srmacklem		}
710191783Srmacklem	} else {
711191783Srmacklem		nmp->nm_sotype = argp->sotype;
712191783Srmacklem		nmp->nm_soproto = argp->proto;
713191783Srmacklem	}
714214048Srmacklem
715214048Srmacklem	if (hostname != NULL) {
716214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
717214048Srmacklem		    sizeof(nmp->nm_hostname));
718214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
719214048Srmacklem		if (p != NULL)
720214048Srmacklem			*p = '\0';
721214048Srmacklem	}
722191783Srmacklem}
723191783Srmacklem
724221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
725275249Strasz    "noac", "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
726191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
727192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
728192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
729275249Strasz    "retrans", "actimeo", "acregmin", "acregmax", "acdirmin", "acdirmax",
730275249Strasz    "resvport", "readahead", "hostname", "timeo", "timeout", "addr", "fh",
731275249Strasz    "nfsv3", "sec", "principal", "nfsv4", "gssname", "allgssname", "dirpath",
732275249Strasz    "minorversion", "nametimeo", "negnametimeo", "nocto", "noncontigwr",
733275249Strasz    "pnfs", "wcommitsize",
734191783Srmacklem    NULL };
735191783Srmacklem
736191783Srmacklem/*
737191783Srmacklem * VFS Operations.
738191783Srmacklem *
739191783Srmacklem * mount system call
740191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
741191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
742191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
743191783Srmacklem * an error after that means that I have to release the mbuf.
744191783Srmacklem */
745191783Srmacklem/* ARGSUSED */
746191783Srmacklemstatic int
747191990Sattilionfs_mount(struct mount *mp)
748191783Srmacklem{
749191783Srmacklem	struct nfs_args args = {
750191783Srmacklem	    .version = NFS_ARGSVERSION,
751191783Srmacklem	    .addr = NULL,
752191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
753191783Srmacklem	    .sotype = SOCK_STREAM,
754191783Srmacklem	    .proto = 0,
755191783Srmacklem	    .fh = NULL,
756191783Srmacklem	    .fhsize = 0,
757220739Srmacklem	    .flags = NFSMNT_RESVPORT,
758191783Srmacklem	    .wsize = NFS_WSIZE,
759191783Srmacklem	    .rsize = NFS_RSIZE,
760191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
761191783Srmacklem	    .timeo = 10,
762191783Srmacklem	    .retrans = NFS_RETRANS,
763191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
764191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
765191783Srmacklem	    .hostname = NULL,
766191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
767191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
768191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
769191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
770191783Srmacklem	};
771192585Srmacklem	int error = 0, ret, len;
772192585Srmacklem	struct sockaddr *nam = NULL;
773191783Srmacklem	struct vnode *vp;
774191990Sattilio	struct thread *td;
775191783Srmacklem	char hst[MNAMELEN];
776191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
777192585Srmacklem	char *opt, *name, *secname;
778230547Sjhb	int nametimeo = NFS_DEFAULT_NAMETIMEO;
779203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
780244042Srmacklem	int minvers = 0;
781221190Srmacklem	int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
782221205Srmacklem	size_t hstlen;
783191783Srmacklem
784221190Srmacklem	has_nfs_args_opt = 0;
785191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
786191783Srmacklem		error = EINVAL;
787191783Srmacklem		goto out;
788191783Srmacklem	}
789191783Srmacklem
790191990Sattilio	td = curthread;
791191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
792221040Srmacklem		error = nfs_mountroot(mp);
793191783Srmacklem		goto out;
794191783Srmacklem	}
795191783Srmacklem
796192585Srmacklem	nfscl_init();
797191783Srmacklem
798221190Srmacklem	/*
799221190Srmacklem	 * The old mount_nfs program passed the struct nfs_args
800221190Srmacklem	 * from userspace to kernel.  The new mount_nfs program
801221190Srmacklem	 * passes string options via nmount() from userspace to kernel
802221190Srmacklem	 * and we populate the struct nfs_args in the kernel.
803221190Srmacklem	 */
804221190Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) {
805221190Srmacklem		error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args,
806221190Srmacklem		    sizeof(args));
807221190Srmacklem		if (error != 0)
808221190Srmacklem			goto out;
809221190Srmacklem
810221190Srmacklem		if (args.version != NFS_ARGSVERSION) {
811221190Srmacklem			error = EPROGMISMATCH;
812221190Srmacklem			goto out;
813221190Srmacklem		}
814221190Srmacklem		has_nfs_args_opt = 1;
815221190Srmacklem	}
816221190Srmacklem
817192585Srmacklem	/* Handle the new style options. */
818275249Strasz	if (vfs_getopt(mp->mnt_optnew, "noac", NULL, NULL) == 0) {
819275249Strasz		args.acdirmin = args.acdirmax =
820275249Strasz		    args.acregmin = args.acregmax = 0;
821275249Strasz		args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX |
822275249Strasz		    NFSMNT_ACREGMIN | NFSMNT_ACREGMAX;
823275249Strasz	}
824192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
825192585Srmacklem		args.flags |= NFSMNT_NOCONN;
826192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
827275252Strasz		args.flags &= ~NFSMNT_NOCONN;
828192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
829192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
830192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
831192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
832192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
833192585Srmacklem		args.flags |= NFSMNT_INT;
834192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
835192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
836192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
837192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
838192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
839192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
840192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
841192585Srmacklem		args.flags |= NFSMNT_SOFT;
842192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
843192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
844192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
845192585Srmacklem		args.sotype = SOCK_DGRAM;
846192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
847192585Srmacklem		args.sotype = SOCK_DGRAM;
848192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
849192585Srmacklem		args.sotype = SOCK_STREAM;
850192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
851192585Srmacklem		args.flags |= NFSMNT_NFSV3;
852192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
853192585Srmacklem		args.flags |= NFSMNT_NFSV4;
854192585Srmacklem		args.sotype = SOCK_STREAM;
855191783Srmacklem	}
856192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
857192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
858221436Sru	if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
859221436Sru		args.flags |= NFSMNT_NOCTO;
860260107Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noncontigwr", NULL, NULL) == 0)
861260107Srmacklem		args.flags |= NFSMNT_NONCONTIGWR;
862244042Srmacklem	if (vfs_getopt(mp->mnt_optnew, "pnfs", NULL, NULL) == 0)
863244042Srmacklem		args.flags |= NFSMNT_PNFS;
864192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
865192585Srmacklem		if (opt == NULL) {
866192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
867192585Srmacklem			error = EINVAL;
868192585Srmacklem			goto out;
869192585Srmacklem		}
870192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
871192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
872192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
873192585Srmacklem			    opt);
874192585Srmacklem			error = EINVAL;
875192585Srmacklem			goto out;
876192585Srmacklem		}
877192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
878192585Srmacklem	}
879192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
880192585Srmacklem		if (opt == NULL) {
881192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
882192585Srmacklem			error = EINVAL;
883192585Srmacklem			goto out;
884192585Srmacklem		}
885192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
886192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
887192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
888192585Srmacklem			    opt);
889192585Srmacklem			error = EINVAL;
890192585Srmacklem			goto out;
891192585Srmacklem		}
892192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
893192585Srmacklem	}
894192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
895192585Srmacklem		if (opt == NULL) {
896192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
897192585Srmacklem			error = EINVAL;
898192585Srmacklem			goto out;
899192585Srmacklem		}
900192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
901192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
902192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
903192585Srmacklem			    opt);
904192585Srmacklem			error = EINVAL;
905192585Srmacklem			goto out;
906192585Srmacklem		}
907192585Srmacklem		args.flags |= NFSMNT_WSIZE;
908192585Srmacklem	}
909192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
910192585Srmacklem		if (opt == NULL) {
911192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
912192585Srmacklem			error = EINVAL;
913192585Srmacklem			goto out;
914192585Srmacklem		}
915192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
916192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
917192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
918192585Srmacklem			    opt);
919192585Srmacklem			error = EINVAL;
920192585Srmacklem			goto out;
921192585Srmacklem		}
922192585Srmacklem		args.flags |= NFSMNT_RSIZE;
923192585Srmacklem	}
924192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
925192585Srmacklem		if (opt == NULL) {
926192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
927192585Srmacklem			error = EINVAL;
928192585Srmacklem			goto out;
929192585Srmacklem		}
930192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
931192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
932192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
933192585Srmacklem			    opt);
934192585Srmacklem			error = EINVAL;
935192585Srmacklem			goto out;
936192585Srmacklem		}
937192585Srmacklem		args.flags |= NFSMNT_RETRANS;
938192585Srmacklem	}
939275249Strasz	if (vfs_getopt(mp->mnt_optnew, "actimeo", (void **)&opt, NULL) == 0) {
940275249Strasz		ret = sscanf(opt, "%d", &args.acregmin);
941275249Strasz		if (ret != 1 || args.acregmin < 0) {
942275249Strasz			vfs_mount_error(mp, "illegal actimeo: %s",
943275249Strasz			    opt);
944275249Strasz			error = EINVAL;
945275249Strasz			goto out;
946275249Strasz		}
947275249Strasz		args.acdirmin = args.acdirmax = args.acregmax = args.acregmin;
948275249Strasz		args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX |
949275249Strasz		    NFSMNT_ACREGMIN | NFSMNT_ACREGMAX;
950275249Strasz	}
951192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
952192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
953192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
954192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
955192585Srmacklem			    opt);
956192585Srmacklem			error = EINVAL;
957192585Srmacklem			goto out;
958192585Srmacklem		}
959192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
960192585Srmacklem	}
961192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
962192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
963192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
964192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
965192585Srmacklem			    opt);
966192585Srmacklem			error = EINVAL;
967192585Srmacklem			goto out;
968192585Srmacklem		}
969192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
970192585Srmacklem	}
971192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
972192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
973192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
974192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
975192585Srmacklem			    opt);
976192585Srmacklem			error = EINVAL;
977192585Srmacklem			goto out;
978192585Srmacklem		}
979192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
980192585Srmacklem	}
981192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
982192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
983192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
984192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
985192585Srmacklem			    opt);
986192585Srmacklem			error = EINVAL;
987192585Srmacklem			goto out;
988192585Srmacklem		}
989192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
990192585Srmacklem	}
991227507Sjhb	if (vfs_getopt(mp->mnt_optnew, "wcommitsize", (void **)&opt, NULL) == 0) {
992227507Sjhb		ret = sscanf(opt, "%d", &args.wcommitsize);
993227507Sjhb		if (ret != 1 || args.wcommitsize < 0) {
994227507Sjhb			vfs_mount_error(mp, "illegal wcommitsize: %s", opt);
995227507Sjhb			error = EINVAL;
996227507Sjhb			goto out;
997227507Sjhb		}
998227507Sjhb		args.flags |= NFSMNT_WCOMMITSIZE;
999227507Sjhb	}
1000275249Strasz	if (vfs_getopt(mp->mnt_optnew, "timeo", (void **)&opt, NULL) == 0) {
1001275249Strasz		ret = sscanf(opt, "%d", &args.timeo);
1002275249Strasz		if (ret != 1 || args.timeo <= 0) {
1003275249Strasz			vfs_mount_error(mp, "illegal timeo: %s",
1004275249Strasz			    opt);
1005275249Strasz			error = EINVAL;
1006275249Strasz			goto out;
1007275249Strasz		}
1008275249Strasz		args.flags |= NFSMNT_TIMEO;
1009275249Strasz	}
1010192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
1011192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
1012192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
1013192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
1014192585Srmacklem			    opt);
1015192585Srmacklem			error = EINVAL;
1016192585Srmacklem			goto out;
1017192585Srmacklem		}
1018192585Srmacklem		args.flags |= NFSMNT_TIMEO;
1019192585Srmacklem	}
1020230547Sjhb	if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) {
1021230547Sjhb		ret = sscanf(opt, "%d", &nametimeo);
1022230547Sjhb		if (ret != 1 || nametimeo < 0) {
1023230547Sjhb			vfs_mount_error(mp, "illegal nametimeo: %s", opt);
1024230547Sjhb			error = EINVAL;
1025230547Sjhb			goto out;
1026230547Sjhb		}
1027230547Sjhb	}
1028203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
1029203303Srmacklem	    == 0) {
1030203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
1031203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
1032203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
1033203303Srmacklem			    opt);
1034203303Srmacklem			error = EINVAL;
1035203303Srmacklem			goto out;
1036203303Srmacklem		}
1037203303Srmacklem	}
1038244042Srmacklem	if (vfs_getopt(mp->mnt_optnew, "minorversion", (void **)&opt, NULL) ==
1039244042Srmacklem	    0) {
1040244042Srmacklem		ret = sscanf(opt, "%d", &minvers);
1041244042Srmacklem		if (ret != 1 || minvers < 0 || minvers > 1 ||
1042244042Srmacklem		    (args.flags & NFSMNT_NFSV4) == 0) {
1043244042Srmacklem			vfs_mount_error(mp, "illegal minorversion: %s", opt);
1044244042Srmacklem			error = EINVAL;
1045244042Srmacklem			goto out;
1046244042Srmacklem		}
1047244042Srmacklem	}
1048192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
1049192585Srmacklem		(void **) &secname, NULL) == 0)
1050192585Srmacklem		nfs_sec_name(secname, &args.flags);
1051191783Srmacklem
1052191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1053191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
1054191783Srmacklem
1055191783Srmacklem		if (nmp == NULL) {
1056191783Srmacklem			error = EIO;
1057191783Srmacklem			goto out;
1058191783Srmacklem		}
1059230803Srmacklem
1060191783Srmacklem		/*
1061230803Srmacklem		 * If a change from TCP->UDP is done and there are thread(s)
1062230803Srmacklem		 * that have I/O RPC(s) in progress with a tranfer size
1063230803Srmacklem		 * greater than NFS_MAXDGRAMDATA, those thread(s) will be
1064230803Srmacklem		 * hung, retrying the RPC(s) forever. Usually these threads
1065230803Srmacklem		 * will be seen doing an uninterruptible sleep on wait channel
1066230803Srmacklem		 * "newnfsreq" (truncated to "newnfsre" by procstat).
1067230803Srmacklem		 */
1068230803Srmacklem		if (args.sotype == SOCK_DGRAM && nmp->nm_sotype == SOCK_STREAM)
1069230803Srmacklem			tprintf(td->td_proc, LOG_WARNING,
1070230803Srmacklem	"Warning: mount -u that changes TCP->UDP can result in hung threads\n");
1071230803Srmacklem
1072230803Srmacklem		/*
1073191783Srmacklem		 * When doing an update, we can't change version,
1074191783Srmacklem		 * security, switch lockd strategies or change cookie
1075191783Srmacklem		 * translation
1076191783Srmacklem		 */
1077191783Srmacklem		args.flags = (args.flags &
1078191783Srmacklem		    ~(NFSMNT_NFSV3 |
1079191783Srmacklem		      NFSMNT_NFSV4 |
1080191783Srmacklem		      NFSMNT_KERB |
1081191783Srmacklem		      NFSMNT_INTEGRITY |
1082191783Srmacklem		      NFSMNT_PRIVACY |
1083191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
1084191783Srmacklem		    (nmp->nm_flag &
1085191783Srmacklem			(NFSMNT_NFSV3 |
1086191783Srmacklem			 NFSMNT_NFSV4 |
1087191783Srmacklem			 NFSMNT_KERB |
1088191783Srmacklem			 NFSMNT_INTEGRITY |
1089191783Srmacklem			 NFSMNT_PRIVACY |
1090191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
1091214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
1092191783Srmacklem		goto out;
1093191783Srmacklem	}
1094191783Srmacklem
1095191783Srmacklem	/*
1096191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
1097191783Srmacklem	 * or no-connection mode for those protocols that support
1098191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
1099191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
1100191783Srmacklem	 * to receive replies from a different IP then the request was
1101191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
1102191783Srmacklem	 * not 0.
1103191783Srmacklem	 */
1104191783Srmacklem	if (nfs_ip_paranoia == 0)
1105191783Srmacklem		args.flags |= NFSMNT_NOCONN;
1106192585Srmacklem
1107221190Srmacklem	if (has_nfs_args_opt != 0) {
1108221190Srmacklem		/*
1109221190Srmacklem		 * In the 'nfs_args' case, the pointers in the args
1110221190Srmacklem		 * structure are in userland - we copy them in here.
1111221190Srmacklem		 */
1112221190Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) {
1113192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
1114191783Srmacklem			error = EINVAL;
1115191783Srmacklem			goto out;
1116191783Srmacklem		}
1117221190Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1118221190Srmacklem		    args.fhsize);
1119221190Srmacklem		if (error != 0)
1120221190Srmacklem			goto out;
1121221205Srmacklem		error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen);
1122221190Srmacklem		if (error != 0)
1123221190Srmacklem			goto out;
1124221205Srmacklem		bzero(&hst[hstlen], MNAMELEN - hstlen);
1125221190Srmacklem		args.hostname = hst;
1126221190Srmacklem		/* sockargs() call must be after above copyin() calls */
1127221190Srmacklem		error = getsockaddr(&nam, (caddr_t)args.addr,
1128221190Srmacklem		    args.addrlen);
1129221190Srmacklem		if (error != 0)
1130221190Srmacklem			goto out;
1131191783Srmacklem	} else {
1132221190Srmacklem		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
1133221190Srmacklem		    &args.fhsize) == 0) {
1134221190Srmacklem			if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
1135221190Srmacklem				vfs_mount_error(mp, "Bad file handle");
1136221190Srmacklem				error = EINVAL;
1137221190Srmacklem				goto out;
1138221190Srmacklem			}
1139221190Srmacklem			bcopy(args.fh, nfh, args.fhsize);
1140221190Srmacklem		} else {
1141221190Srmacklem			args.fhsize = 0;
1142221190Srmacklem		}
1143221190Srmacklem		(void) vfs_getopt(mp->mnt_optnew, "hostname",
1144221190Srmacklem		    (void **)&args.hostname, &len);
1145221190Srmacklem		if (args.hostname == NULL) {
1146221190Srmacklem			vfs_mount_error(mp, "Invalid hostname");
1147221190Srmacklem			error = EINVAL;
1148221190Srmacklem			goto out;
1149221190Srmacklem		}
1150221190Srmacklem		bcopy(args.hostname, hst, MNAMELEN);
1151221190Srmacklem		hst[MNAMELEN - 1] = '\0';
1152192585Srmacklem	}
1153192585Srmacklem
1154192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1155192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1156192585Srmacklem	else
1157192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1158221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1159192585Srmacklem
1160192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1161192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1162192585Srmacklem	else
1163191783Srmacklem		krbname[0] = '\0';
1164221014Srmacklem	krbnamelen = strlen(krbname);
1165192585Srmacklem
1166192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1167192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1168192585Srmacklem	else
1169191783Srmacklem		dirpath[0] = '\0';
1170221014Srmacklem	dirlen = strlen(dirpath);
1171192585Srmacklem
1172222075Srmacklem	if (has_nfs_args_opt == 0) {
1173222075Srmacklem		if (vfs_getopt(mp->mnt_optnew, "addr",
1174222075Srmacklem		    (void **)&args.addr, &args.addrlen) == 0) {
1175222075Srmacklem			if (args.addrlen > SOCK_MAXADDRLEN) {
1176222075Srmacklem				error = ENAMETOOLONG;
1177222075Srmacklem				goto out;
1178222075Srmacklem			}
1179222075Srmacklem			nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1180222075Srmacklem			bcopy(args.addr, nam, args.addrlen);
1181222075Srmacklem			nam->sa_len = args.addrlen;
1182222075Srmacklem		} else {
1183222075Srmacklem			vfs_mount_error(mp, "No server address");
1184222075Srmacklem			error = EINVAL;
1185191783Srmacklem			goto out;
1186191783Srmacklem		}
1187191783Srmacklem	}
1188192585Srmacklem
1189191783Srmacklem	args.fh = nfh;
1190221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1191221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1192244042Srmacklem	    nametimeo, negnametimeo, minvers);
1193191783Srmacklemout:
1194191783Srmacklem	if (!error) {
1195191783Srmacklem		MNT_ILOCK(mp);
1196282270Srmacklem		mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_NO_IOPF |
1197282270Srmacklem		    MNTK_USES_BCACHE;
1198191783Srmacklem		MNT_IUNLOCK(mp);
1199191783Srmacklem	}
1200191783Srmacklem	return (error);
1201191783Srmacklem}
1202191783Srmacklem
1203191783Srmacklem
1204191783Srmacklem/*
1205191783Srmacklem * VFS Operations.
1206191783Srmacklem *
1207191783Srmacklem * mount system call
1208191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1209191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1210191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1211191783Srmacklem * an error after that means that I have to release the mbuf.
1212191783Srmacklem */
1213191783Srmacklem/* ARGSUSED */
1214191783Srmacklemstatic int
1215230249Smckusicknfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
1216191783Srmacklem{
1217191783Srmacklem	int error;
1218191783Srmacklem	struct nfs_args args;
1219191783Srmacklem
1220191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1221191783Srmacklem	if (error)
1222191783Srmacklem		return error;
1223191783Srmacklem
1224191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1225191783Srmacklem
1226191783Srmacklem	error = kernel_mount(ma, flags);
1227191783Srmacklem	return (error);
1228191783Srmacklem}
1229191783Srmacklem
1230191783Srmacklem/*
1231191783Srmacklem * Common code for mount and mountroot
1232191783Srmacklem */
1233191783Srmacklemstatic int
1234191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1235221014Srmacklem    char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
1236221014Srmacklem    u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
1237244042Srmacklem    struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo,
1238244042Srmacklem    int minvers)
1239191783Srmacklem{
1240191783Srmacklem	struct nfsmount *nmp;
1241191783Srmacklem	struct nfsnode *np;
1242195762Srmacklem	int error, trycnt, ret;
1243191783Srmacklem	struct nfsvattr nfsva;
1244244042Srmacklem	struct nfsclclient *clp;
1245244042Srmacklem	struct nfsclds *dsp, *tdsp;
1246244042Srmacklem	uint32_t lease;
1247191783Srmacklem	static u_int64_t clval = 0;
1248191783Srmacklem
1249244042Srmacklem	NFSCL_DEBUG(3, "in mnt\n");
1250244042Srmacklem	clp = NULL;
1251191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1252191783Srmacklem		nmp = VFSTONFS(mp);
1253191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1254191783Srmacklem		FREE(nam, M_SONAME);
1255191783Srmacklem		return (0);
1256191783Srmacklem	} else {
1257191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1258221014Srmacklem		    krbnamelen + dirlen + srvkrbnamelen + 2,
1259221014Srmacklem		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
1260191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1261191783Srmacklem		if (clval == 0)
1262191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1263191783Srmacklem		nmp->nm_clval = clval++;
1264221014Srmacklem		nmp->nm_krbnamelen = krbnamelen;
1265221014Srmacklem		nmp->nm_dirpathlen = dirlen;
1266221014Srmacklem		nmp->nm_srvkrbnamelen = srvkrbnamelen;
1267192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1268191783Srmacklem			/*
1269192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1270192675Srmacklem			 * the nfsv4 state handling operations if there is
1271192675Srmacklem			 * no host based principal set. Use the uid of
1272192675Srmacklem			 * this user if not root, since they are doing the
1273192675Srmacklem			 * mount. I don't think setting this for root will
1274192675Srmacklem			 * work, since root normally does not have user
1275192675Srmacklem			 * credentials in a credentials cache.
1276191783Srmacklem			 */
1277192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1278191783Srmacklem		} else {
1279191783Srmacklem			/*
1280192675Srmacklem			 * Just set to -1, so it won't be used.
1281191783Srmacklem			 */
1282191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1283191783Srmacklem		}
1284191783Srmacklem
1285191783Srmacklem		/* Copy and null terminate all the names */
1286191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1287191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1288191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1289191783Srmacklem		}
1290191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1291191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1292191783Srmacklem			    nmp->nm_dirpathlen);
1293191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1294191783Srmacklem			    + 1] = '\0';
1295191783Srmacklem		}
1296191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1297191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1298191783Srmacklem			    nmp->nm_srvkrbnamelen);
1299191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1300191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1301191783Srmacklem		}
1302191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1303191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1304191783Srmacklem		mp->mnt_data = nmp;
1305214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1306216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1307191783Srmacklem	}
1308191783Srmacklem	vfs_getnewfsid(mp);
1309191783Srmacklem	nmp->nm_mountp = mp;
1310230547Sjhb	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1311227493Srmacklem
1312227493Srmacklem	/*
1313230547Sjhb	 * Since nfs_decode_args() might optionally set them, these
1314230547Sjhb	 * need to be set to defaults before the call, so that the
1315230547Sjhb	 * optional settings aren't overwritten.
1316227493Srmacklem	 */
1317230547Sjhb	nmp->nm_nametimeo = nametimeo;
1318203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1319227493Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1320227493Srmacklem	nmp->nm_retry = NFS_RETRANS;
1321227493Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1322282362Smav
1323282362Smav	/* This is empirical approximation of sqrt(hibufspace) * 256. */
1324282362Smav	nmp->nm_wcommitsize = NFS_MAXBSIZE / 256;
1325282362Smav	while ((long)nmp->nm_wcommitsize * nmp->nm_wcommitsize < hibufspace)
1326282362Smav		nmp->nm_wcommitsize *= 2;
1327282362Smav	nmp->nm_wcommitsize *= 256;
1328282362Smav
1329244042Srmacklem	if ((argp->flags & NFSMNT_NFSV4) != 0)
1330244042Srmacklem		nmp->nm_minorvers = minvers;
1331244042Srmacklem	else
1332244042Srmacklem		nmp->nm_minorvers = 0;
1333191783Srmacklem
1334214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1335192585Srmacklem
1336191783Srmacklem	/*
1337191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1338191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1339191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1340191783Srmacklem	 *
1341191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1342191783Srmacklem	 * that we can handle until we find out otherwise.
1343191783Srmacklem	 */
1344191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1345191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1346191783Srmacklem	else
1347221537Srmacklem		nmp->nm_maxfilesize = OFF_MAX;
1348191783Srmacklem
1349191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1350191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1351191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1352191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1353191783Srmacklem	}
1354191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1355191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1356191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1357191783Srmacklem		nmp->nm_tprintf_delay = 0;
1358191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1359191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1360191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1361191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1362191783Srmacklem	if (nmp->nm_fhsize > 0)
1363191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1364191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1365191783Srmacklem	nmp->nm_nam = nam;
1366191783Srmacklem	/* Set up the sockets and per-host congestion */
1367191783Srmacklem	nmp->nm_sotype = argp->sotype;
1368191783Srmacklem	nmp->nm_soproto = argp->proto;
1369191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1370191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1371191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1372191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1373191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1374191783Srmacklem	else
1375191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1376191783Srmacklem
1377191783Srmacklem
1378191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1379191783Srmacklem		goto bad;
1380244042Srmacklem	/* For NFSv4.1, get the clientid now. */
1381244042Srmacklem	if (nmp->nm_minorvers > 0) {
1382244042Srmacklem		NFSCL_DEBUG(3, "at getcl\n");
1383244042Srmacklem		error = nfscl_getcl(mp, cred, td, 0, &clp);
1384244042Srmacklem		NFSCL_DEBUG(3, "aft getcl=%d\n", error);
1385244042Srmacklem		if (error != 0)
1386244042Srmacklem			goto bad;
1387244042Srmacklem	}
1388191783Srmacklem
1389191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1390191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1391244042Srmacklem		NFSCL_DEBUG(3, "in dirp\n");
1392191783Srmacklem		/*
1393191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1394191783Srmacklem		 * path needs to be looked up.
1395191783Srmacklem		 */
1396191783Srmacklem		trycnt = 3;
1397191783Srmacklem		do {
1398191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1399191783Srmacklem			    cred, td);
1400244042Srmacklem			NFSCL_DEBUG(3, "aft dirp=%d\n", error);
1401191783Srmacklem			if (error)
1402207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1403191783Srmacklem		} while (error && --trycnt > 0);
1404191783Srmacklem		if (error) {
1405191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1406191783Srmacklem			goto bad;
1407191783Srmacklem		}
1408191783Srmacklem	}
1409244042Srmacklem
1410244042Srmacklem	/*
1411244042Srmacklem	 * A reference count is needed on the nfsnode representing the
1412244042Srmacklem	 * remote root.  If this object is not persistent, then backward
1413244042Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1414244042Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1415244042Srmacklem	 * this problem, because one can identify root inodes by their
1416244042Srmacklem	 * number == ROOTINO (2).
1417244042Srmacklem	 */
1418191783Srmacklem	if (nmp->nm_fhsize > 0) {
1419195762Srmacklem		/*
1420195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1421195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1422195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1423195762Srmacklem		 */
1424195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1425220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1426220732Srmacklem		    LK_EXCLUSIVE);
1427191783Srmacklem		if (error)
1428191783Srmacklem			goto bad;
1429191783Srmacklem		*vpp = NFSTOV(np);
1430191783Srmacklem
1431191783Srmacklem		/*
1432191783Srmacklem		 * Get file attributes and transfer parameters for the
1433191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1434191783Srmacklem		 * (*vpp)->v_type with the correct value.
1435191783Srmacklem		 */
1436191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1437244042Srmacklem		    cred, td, &nfsva, NULL, &lease);
1438191783Srmacklem		if (ret) {
1439191783Srmacklem			/*
1440191783Srmacklem			 * Just set default values to get things going.
1441191783Srmacklem			 */
1442191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1443191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1444191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1445191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1446191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1447191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1448191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1449191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1450191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1451191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1452244042Srmacklem			lease = 60;
1453191783Srmacklem		}
1454191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1455244042Srmacklem		if (nmp->nm_minorvers > 0) {
1456244042Srmacklem			NFSCL_DEBUG(3, "lease=%d\n", (int)lease);
1457244042Srmacklem			NFSLOCKCLSTATE();
1458244042Srmacklem			clp->nfsc_renew = NFSCL_RENEW(lease);
1459244042Srmacklem			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
1460244042Srmacklem			clp->nfsc_clientidrev++;
1461244042Srmacklem			if (clp->nfsc_clientidrev == 0)
1462244042Srmacklem				clp->nfsc_clientidrev++;
1463244042Srmacklem			NFSUNLOCKCLSTATE();
1464244042Srmacklem			/*
1465244042Srmacklem			 * Mount will succeed, so the renew thread can be
1466244042Srmacklem			 * started now.
1467244042Srmacklem			 */
1468244042Srmacklem			nfscl_start_renewthread(clp);
1469244042Srmacklem			nfscl_clientrelease(clp);
1470244042Srmacklem		}
1471191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1472191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1473191783Srmacklem
1474222233Srmacklem		/* Mark if the mount point supports NFSv4 ACLs. */
1475222233Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0 && nfsrv_useacl != 0 &&
1476222233Srmacklem		    ret == 0 &&
1477222233Srmacklem		    NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL)) {
1478222233Srmacklem			MNT_ILOCK(mp);
1479222233Srmacklem			mp->mnt_flag |= MNT_NFS4ACLS;
1480222233Srmacklem			MNT_IUNLOCK(mp);
1481222233Srmacklem		}
1482222233Srmacklem
1483191783Srmacklem		/*
1484191783Srmacklem		 * Lose the lock but keep the ref.
1485191783Srmacklem		 */
1486224082Szack		NFSVOPUNLOCK(*vpp, 0);
1487191783Srmacklem		return (0);
1488191783Srmacklem	}
1489191783Srmacklem	error = EIO;
1490191783Srmacklem
1491191783Srmacklembad:
1492244042Srmacklem	if (clp != NULL)
1493244042Srmacklem		nfscl_clientrelease(clp);
1494191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1495191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1496253049Srmacklem	if (nmp->nm_sockreq.nr_auth != NULL)
1497253049Srmacklem		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
1498191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1499191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1500244042Srmacklem	if (nmp->nm_clp != NULL) {
1501244042Srmacklem		NFSLOCKCLSTATE();
1502244042Srmacklem		LIST_REMOVE(nmp->nm_clp, nfsc_list);
1503244042Srmacklem		NFSUNLOCKCLSTATE();
1504244042Srmacklem		free(nmp->nm_clp, M_NFSCLCLIENT);
1505244042Srmacklem	}
1506244042Srmacklem	TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
1507244042Srmacklem		nfscl_freenfsclds(dsp);
1508191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1509191783Srmacklem	FREE(nam, M_SONAME);
1510191783Srmacklem	return (error);
1511191783Srmacklem}
1512191783Srmacklem
1513191783Srmacklem/*
1514191783Srmacklem * unmount system call
1515191783Srmacklem */
1516191783Srmacklemstatic int
1517191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1518191783Srmacklem{
1519191990Sattilio	struct thread *td;
1520191783Srmacklem	struct nfsmount *nmp;
1521249630Srmacklem	int error, flags = 0, i, trycnt = 0;
1522244042Srmacklem	struct nfsclds *dsp, *tdsp;
1523191783Srmacklem
1524191990Sattilio	td = curthread;
1525191990Sattilio
1526191783Srmacklem	if (mntflags & MNT_FORCE)
1527191783Srmacklem		flags |= FORCECLOSE;
1528191783Srmacklem	nmp = VFSTONFS(mp);
1529191783Srmacklem	/*
1530191783Srmacklem	 * Goes something like this..
1531191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1532191783Srmacklem	 * - Close the socket
1533191783Srmacklem	 * - Free up the data structures
1534191783Srmacklem	 */
1535191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1536191783Srmacklem	if (mntflags & MNT_FORCE) {
1537191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1538191783Srmacklem		if (error)
1539191783Srmacklem			goto out;
1540191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1541191783Srmacklem		nfscl_umount(nmp, td);
1542191783Srmacklem	}
1543191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1544191783Srmacklem	do {
1545191783Srmacklem		error = vflush(mp, 1, flags, td);
1546191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1547207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1548191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1549191783Srmacklem	if (error)
1550191783Srmacklem		goto out;
1551191783Srmacklem
1552191783Srmacklem	/*
1553191783Srmacklem	 * We are now committed to the unmount.
1554191783Srmacklem	 */
1555191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1556191783Srmacklem		nfscl_umount(nmp, td);
1557249630Srmacklem	/* Make sure no nfsiods are assigned to this mount. */
1558249630Srmacklem	mtx_lock(&ncl_iod_mutex);
1559249630Srmacklem	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
1560249630Srmacklem		if (ncl_iodmount[i] == nmp) {
1561249630Srmacklem			ncl_iodwant[i] = NFSIOD_AVAILABLE;
1562249630Srmacklem			ncl_iodmount[i] = NULL;
1563249630Srmacklem		}
1564249630Srmacklem	mtx_unlock(&ncl_iod_mutex);
1565191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1566191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1567191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1568253049Srmacklem	if (nmp->nm_sockreq.nr_auth != NULL)
1569253049Srmacklem		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
1570191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1571191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1572244042Srmacklem	TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
1573244042Srmacklem		nfscl_freenfsclds(dsp);
1574191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1575191783Srmacklemout:
1576191783Srmacklem	return (error);
1577191783Srmacklem}
1578191783Srmacklem
1579191783Srmacklem/*
1580191783Srmacklem * Return root of a filesystem
1581191783Srmacklem */
1582191783Srmacklemstatic int
1583191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1584191783Srmacklem{
1585191783Srmacklem	struct vnode *vp;
1586191783Srmacklem	struct nfsmount *nmp;
1587191783Srmacklem	struct nfsnode *np;
1588191783Srmacklem	int error;
1589191783Srmacklem
1590191783Srmacklem	nmp = VFSTONFS(mp);
1591220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1592191783Srmacklem	if (error)
1593191783Srmacklem		return error;
1594191783Srmacklem	vp = NFSTOV(np);
1595191783Srmacklem	/*
1596191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1597191783Srmacklem	 */
1598191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1599191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1600191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1601191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1602191783Srmacklem	} else
1603191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1604191783Srmacklem	if (vp->v_type == VNON)
1605191783Srmacklem	    vp->v_type = VDIR;
1606191783Srmacklem	vp->v_vflag |= VV_ROOT;
1607191783Srmacklem	*vpp = vp;
1608191783Srmacklem	return (0);
1609191783Srmacklem}
1610191783Srmacklem
1611191783Srmacklem/*
1612191783Srmacklem * Flush out the buffer cache
1613191783Srmacklem */
1614191783Srmacklem/* ARGSUSED */
1615191783Srmacklemstatic int
1616191990Sattilionfs_sync(struct mount *mp, int waitfor)
1617191783Srmacklem{
1618191783Srmacklem	struct vnode *vp, *mvp;
1619191990Sattilio	struct thread *td;
1620191783Srmacklem	int error, allerror = 0;
1621191783Srmacklem
1622191990Sattilio	td = curthread;
1623191990Sattilio
1624222329Srmacklem	MNT_ILOCK(mp);
1625191783Srmacklem	/*
1626222329Srmacklem	 * If a forced dismount is in progress, return from here so that
1627222329Srmacklem	 * the umount(2) syscall doesn't get stuck in VFS_SYNC() before
1628222329Srmacklem	 * calling VFS_UNMOUNT().
1629222329Srmacklem	 */
1630222329Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1631222329Srmacklem		MNT_IUNLOCK(mp);
1632222329Srmacklem		return (EBADF);
1633222329Srmacklem	}
1634234386Smckusick	MNT_IUNLOCK(mp);
1635222329Srmacklem
1636222329Srmacklem	/*
1637191783Srmacklem	 * Force stale buffer cache information to be flushed.
1638191783Srmacklem	 */
1639191783Srmacklemloop:
1640234386Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
1641191783Srmacklem		/* XXX Racy bv_cnt check. */
1642224083Szack		if (NFSVOPISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1643191783Srmacklem		    waitfor == MNT_LAZY) {
1644191783Srmacklem			VI_UNLOCK(vp);
1645191783Srmacklem			continue;
1646191783Srmacklem		}
1647191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1648234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
1649191783Srmacklem			goto loop;
1650191783Srmacklem		}
1651191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1652191783Srmacklem		if (error)
1653191783Srmacklem			allerror = error;
1654224082Szack		NFSVOPUNLOCK(vp, 0);
1655191783Srmacklem		vrele(vp);
1656191783Srmacklem	}
1657191783Srmacklem	return (allerror);
1658191783Srmacklem}
1659191783Srmacklem
1660191783Srmacklemstatic int
1661191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1662191783Srmacklem{
1663191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1664191783Srmacklem	struct vfsquery vq;
1665191783Srmacklem	int error;
1666191783Srmacklem
1667191783Srmacklem	bzero(&vq, sizeof(vq));
1668191783Srmacklem	switch (op) {
1669191783Srmacklem#if 0
1670191783Srmacklem	case VFS_CTL_NOLOCKS:
1671191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1672191783Srmacklem 		if (req->oldptr != NULL) {
1673191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1674191783Srmacklem 			if (error)
1675191783Srmacklem 				return (error);
1676191783Srmacklem 		}
1677191783Srmacklem 		if (req->newptr != NULL) {
1678191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1679191783Srmacklem 			if (error)
1680191783Srmacklem 				return (error);
1681191783Srmacklem			if (val)
1682191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1683191783Srmacklem			else
1684191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1685191783Srmacklem 		}
1686191783Srmacklem		break;
1687191783Srmacklem#endif
1688191783Srmacklem	case VFS_CTL_QUERY:
1689191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1690191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1691191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1692191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1693191783Srmacklem#if 0
1694191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1695191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1696191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1697191783Srmacklem#endif
1698191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1699191783Srmacklem		break;
1700191783Srmacklem 	case VFS_CTL_TIMEO:
1701191783Srmacklem 		if (req->oldptr != NULL) {
1702191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1703191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1704191783Srmacklem 			if (error)
1705191783Srmacklem 				return (error);
1706191783Srmacklem 		}
1707191783Srmacklem 		if (req->newptr != NULL) {
1708191783Srmacklem			error = vfs_suser(mp, req->td);
1709191783Srmacklem			if (error)
1710191783Srmacklem				return (error);
1711191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1712191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1713191783Srmacklem 			if (error)
1714191783Srmacklem 				return (error);
1715191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1716191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1717191783Srmacklem 		}
1718191783Srmacklem		break;
1719191783Srmacklem	default:
1720191783Srmacklem		return (ENOTSUP);
1721191783Srmacklem	}
1722191783Srmacklem	return (0);
1723191783Srmacklem}
1724191783Srmacklem
1725214048Srmacklem/*
1726255136Srmacklem * Purge any RPCs in progress, so that they will all return errors.
1727255136Srmacklem * This allows dounmount() to continue as far as VFS_UNMOUNT() for a
1728255136Srmacklem * forced dismount.
1729255136Srmacklem */
1730255136Srmacklemstatic void
1731255136Srmacklemnfs_purge(struct mount *mp)
1732255136Srmacklem{
1733255136Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1734255136Srmacklem
1735255136Srmacklem	newnfs_nmcancelreqs(nmp);
1736255136Srmacklem}
1737255136Srmacklem
1738255136Srmacklem/*
1739214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1740214048Srmacklem */
1741214048Srmacklemstatic void
1742214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1743216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1744216931Srmacklem    struct timeval *timeop)
1745214048Srmacklem{
1746214048Srmacklem	struct nfsmount *nmp;
1747214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1748214048Srmacklem
1749214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1750214048Srmacklem	if (fhlenp != NULL)
1751214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1752214048Srmacklem	if (fhp != NULL)
1753214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1754214048Srmacklem	if (sp != NULL)
1755214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1756214048Srmacklem	if (is_v3p != NULL)
1757214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1758214048Srmacklem	if (sizep != NULL)
1759214048Srmacklem		*sizep = np->n_size;
1760216931Srmacklem	if (timeop != NULL) {
1761216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1762216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1763216931Srmacklem	}
1764214048Srmacklem}
1765214048Srmacklem
1766243782Srmacklem/*
1767243782Srmacklem * This function prints out an option name, based on the conditional
1768243782Srmacklem * argument.
1769243782Srmacklem */
1770243782Srmacklemstatic __inline void nfscl_printopt(struct nfsmount *nmp, int testval,
1771243782Srmacklem    char *opt, char **buf, size_t *blen)
1772243782Srmacklem{
1773243782Srmacklem	int len;
1774243782Srmacklem
1775243782Srmacklem	if (testval != 0 && *blen > strlen(opt)) {
1776243782Srmacklem		len = snprintf(*buf, *blen, "%s", opt);
1777243782Srmacklem		if (len != strlen(opt))
1778243782Srmacklem			printf("EEK!!\n");
1779243782Srmacklem		*buf += len;
1780243782Srmacklem		*blen -= len;
1781243782Srmacklem	}
1782243782Srmacklem}
1783243782Srmacklem
1784243782Srmacklem/*
1785243782Srmacklem * This function printf out an options integer value.
1786243782Srmacklem */
1787243782Srmacklemstatic __inline void nfscl_printoptval(struct nfsmount *nmp, int optval,
1788243782Srmacklem    char *opt, char **buf, size_t *blen)
1789243782Srmacklem{
1790243782Srmacklem	int len;
1791243782Srmacklem
1792243782Srmacklem	if (*blen > strlen(opt) + 1) {
1793243782Srmacklem		/* Could result in truncated output string. */
1794243782Srmacklem		len = snprintf(*buf, *blen, "%s=%d", opt, optval);
1795243782Srmacklem		if (len < *blen) {
1796243782Srmacklem			*buf += len;
1797243782Srmacklem			*blen -= len;
1798243782Srmacklem		}
1799243782Srmacklem	}
1800243782Srmacklem}
1801243782Srmacklem
1802243782Srmacklem/*
1803243782Srmacklem * Load the option flags and values into the buffer.
1804243782Srmacklem */
1805243782Srmacklemvoid nfscl_retopts(struct nfsmount *nmp, char *buffer, size_t buflen)
1806243782Srmacklem{
1807243782Srmacklem	char *buf;
1808243782Srmacklem	size_t blen;
1809243782Srmacklem
1810243782Srmacklem	buf = buffer;
1811243782Srmacklem	blen = buflen;
1812243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV4) != 0, "nfsv4", &buf,
1813243782Srmacklem	    &blen);
1814244056Srmacklem	if ((nmp->nm_flag & NFSMNT_NFSV4) != 0) {
1815244056Srmacklem		nfscl_printoptval(nmp, nmp->nm_minorvers, ",minorversion", &buf,
1816244056Srmacklem		    &blen);
1817244056Srmacklem		nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_PNFS) != 0, ",pnfs",
1818244056Srmacklem		    &buf, &blen);
1819244056Srmacklem	}
1820243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV3) != 0, "nfsv3", &buf,
1821243782Srmacklem	    &blen);
1822243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0,
1823243782Srmacklem	    "nfsv2", &buf, &blen);
1824243782Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype == SOCK_STREAM, ",tcp", &buf, &blen);
1825243782Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype != SOCK_STREAM, ",udp", &buf, &blen);
1826243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RESVPORT) != 0, ",resvport",
1827243782Srmacklem	    &buf, &blen);
1828243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCONN) != 0, ",noconn",
1829243782Srmacklem	    &buf, &blen);
1830243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) == 0, ",hard", &buf,
1831243782Srmacklem	    &blen);
1832243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) != 0, ",soft", &buf,
1833243782Srmacklem	    &blen);
1834243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_INT) != 0, ",intr", &buf,
1835243782Srmacklem	    &blen);
1836243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) == 0, ",cto", &buf,
1837243782Srmacklem	    &blen);
1838243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) != 0, ",nocto", &buf,
1839243782Srmacklem	    &blen);
1840260107Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NONCONTIGWR) != 0,
1841260107Srmacklem	    ",noncontigwr", &buf, &blen);
1842243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1843243782Srmacklem	    0, ",lockd", &buf, &blen);
1844243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1845243782Srmacklem	    NFSMNT_NOLOCKD, ",nolockd", &buf, &blen);
1846243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RDIRPLUS) != 0, ",rdirplus",
1847243782Srmacklem	    &buf, &blen);
1848243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_KERB) == 0, ",sec=sys",
1849243782Srmacklem	    &buf, &blen);
1850243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1851243782Srmacklem	    NFSMNT_PRIVACY)) == NFSMNT_KERB, ",sec=krb5", &buf, &blen);
1852243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1853243782Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_INTEGRITY), ",sec=krb5i",
1854243782Srmacklem	    &buf, &blen);
1855243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1856243782Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_PRIVACY), ",sec=krb5p",
1857243782Srmacklem	    &buf, &blen);
1858243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmin, ",acdirmin", &buf, &blen);
1859243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmax, ",acdirmax", &buf, &blen);
1860243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmin, ",acregmin", &buf, &blen);
1861243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmax, ",acregmax", &buf, &blen);
1862243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_nametimeo, ",nametimeo", &buf, &blen);
1863243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_negnametimeo, ",negnametimeo", &buf,
1864243782Srmacklem	    &blen);
1865243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_rsize, ",rsize", &buf, &blen);
1866243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_wsize, ",wsize", &buf, &blen);
1867243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_readdirsize, ",readdirsize", &buf,
1868243782Srmacklem	    &blen);
1869243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_readahead, ",readahead", &buf, &blen);
1870243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_wcommitsize, ",wcommitsize", &buf,
1871243782Srmacklem	    &blen);
1872243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_timeo, ",timeout", &buf, &blen);
1873243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_retry, ",retrans", &buf, &blen);
1874243782Srmacklem}
1875243782Srmacklem
1876