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/11.0/sys/fs/nfsclient/nfs_clvfsops.c 301566 2016-06-07 20:16:01Z cem $");
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
89281725StraszMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "NFS request header");
90281725StraszMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "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, "");
103276140Srmacklem#ifdef NFS_DEBUG
104276140Srmacklemint nfs_debug;
105276140SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, debug, CTLFLAG_RW, &nfs_debug, 0,
106276140Srmacklem    "Toggle debug flag");
107276140Srmacklem#endif
108191783Srmacklem
109221040Srmacklemstatic int	nfs_mountroot(struct mount *);
110192585Srmacklemstatic void	nfs_sec_name(char *, int *);
111191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
112214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
113214048Srmacklem		    struct thread *);
114191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
115221014Srmacklem		    struct sockaddr *, char *, u_char *, int, u_char *, int,
116221014Srmacklem		    u_char *, int, struct vnode **, struct ucred *,
117244042Srmacklem		    struct thread *, int, int, int);
118214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
119216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
120216931Srmacklem		    struct timeval *);
121191783Srmacklemstatic vfs_mount_t nfs_mount;
122191783Srmacklemstatic vfs_cmount_t nfs_cmount;
123191783Srmacklemstatic vfs_unmount_t nfs_unmount;
124191783Srmacklemstatic vfs_root_t nfs_root;
125191783Srmacklemstatic vfs_statfs_t nfs_statfs;
126191783Srmacklemstatic vfs_sync_t nfs_sync;
127191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
128255136Srmacklemstatic vfs_purge_t nfs_purge;
129191783Srmacklem
130191783Srmacklem/*
131191783Srmacklem * nfs vfs operations.
132191783Srmacklem */
133191783Srmacklemstatic struct vfsops nfs_vfsops = {
134191783Srmacklem	.vfs_init =		ncl_init,
135191783Srmacklem	.vfs_mount =		nfs_mount,
136191783Srmacklem	.vfs_cmount =		nfs_cmount,
137191783Srmacklem	.vfs_root =		nfs_root,
138191783Srmacklem	.vfs_statfs =		nfs_statfs,
139191783Srmacklem	.vfs_sync =		nfs_sync,
140191783Srmacklem	.vfs_uninit =		ncl_uninit,
141191783Srmacklem	.vfs_unmount =		nfs_unmount,
142191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
143255136Srmacklem	.vfs_purge =		nfs_purge,
144191783Srmacklem};
145247116SjhbVFS_SET(nfs_vfsops, nfs, VFCF_NETWORK | VFCF_SBDRY);
146191783Srmacklem
147191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
148221139SrmacklemMODULE_VERSION(nfs, 1);
149221139SrmacklemMODULE_DEPEND(nfs, nfscommon, 1, 1, 1);
150221139SrmacklemMODULE_DEPEND(nfs, krpc, 1, 1, 1);
151221139SrmacklemMODULE_DEPEND(nfs, nfssvc, 1, 1, 1);
152221139SrmacklemMODULE_DEPEND(nfs, nfslock, 1, 1, 1);
153191783Srmacklem
154191783Srmacklem/*
155221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it
156221066Srmacklem * can be shared by both NFS clients. It is declared here so that it
157221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it
158221066Srmacklem * isn't used in that case.
159191783Srmacklem */
160276096Srmacklem#if !defined(NFS_ROOT)
161221066Srmacklemstruct nfs_diskless	nfs_diskless = { { { 0 } } };
162221066Srmacklemstruct nfsv3_diskless	nfsv3_diskless = { { { 0 } } };
163221066Srmacklemint			nfs_diskless_valid = 0;
164221066Srmacklem#endif
165221066Srmacklem
166221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
167221040Srmacklem    &nfs_diskless_valid, 0,
168192145Srmacklem    "Has the diskless struct been filled correctly");
169191783Srmacklem
170221973SrmacklemSYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
171221040Srmacklem    nfsv3_diskless.root_hostnam, 0, "Path to nfs root");
172191783Srmacklem
173221973SrmacklemSYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
174221040Srmacklem    &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr),
175192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
176191783Srmacklem
177191783Srmacklem
178191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
179191783Srmacklemstatic int	nfs_mountdiskless(char *,
180191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
181191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
182191783Srmacklemstatic void	nfs_convert_diskless(void);
183191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
184191783Srmacklem		    struct onfs_args *oargs);
185191783Srmacklem
186191783Srmacklemint
187191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
188191783Srmacklem{
189191783Srmacklem	int iosize, maxio;
190191783Srmacklem
191191783Srmacklem	/* First, set the upper limit for iosize */
192191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
193191783Srmacklem		maxio = NFS_MAXBSIZE;
194191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
195191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
196191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
197191783Srmacklem		else
198191783Srmacklem			maxio = NFS_MAXBSIZE;
199191783Srmacklem	} else {
200191783Srmacklem		maxio = NFS_V2MAXDATA;
201191783Srmacklem	}
202191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
203191783Srmacklem		nmp->nm_rsize = maxio;
204281960Srmacklem	if (nmp->nm_rsize > NFS_MAXBSIZE)
205281960Srmacklem		nmp->nm_rsize = NFS_MAXBSIZE;
206191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
207191783Srmacklem		nmp->nm_readdirsize = maxio;
208191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
209191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
210191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
211191783Srmacklem		nmp->nm_wsize = maxio;
212281960Srmacklem	if (nmp->nm_wsize > NFS_MAXBSIZE)
213281960Srmacklem		nmp->nm_wsize = NFS_MAXBSIZE;
214191783Srmacklem
215191783Srmacklem	/*
216191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
217191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
218191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
219290970Srmacklem	 * space.  It must also be at least NFS_DIRBLKSIZ, since
220290970Srmacklem	 * that is the buffer size used for directories.
221191783Srmacklem	 */
222191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
223191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
224290970Srmacklem	iosize = imax(iosize, NFS_DIRBLKSIZ);
225191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
226191783Srmacklem	return (iosize);
227191783Srmacklem}
228191783Srmacklem
229191783Srmacklemstatic void
230191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
231191783Srmacklem{
232191783Srmacklem
233191783Srmacklem	args->version = NFS_ARGSVERSION;
234191783Srmacklem	args->addr = oargs->addr;
235191783Srmacklem	args->addrlen = oargs->addrlen;
236191783Srmacklem	args->sotype = oargs->sotype;
237191783Srmacklem	args->proto = oargs->proto;
238191783Srmacklem	args->fh = oargs->fh;
239191783Srmacklem	args->fhsize = oargs->fhsize;
240191783Srmacklem	args->flags = oargs->flags;
241191783Srmacklem	args->wsize = oargs->wsize;
242191783Srmacklem	args->rsize = oargs->rsize;
243191783Srmacklem	args->readdirsize = oargs->readdirsize;
244191783Srmacklem	args->timeo = oargs->timeo;
245191783Srmacklem	args->retrans = oargs->retrans;
246191783Srmacklem	args->readahead = oargs->readahead;
247191783Srmacklem	args->hostname = oargs->hostname;
248191783Srmacklem}
249191783Srmacklem
250191783Srmacklemstatic void
251191783Srmacklemnfs_convert_diskless(void)
252191783Srmacklem{
253191783Srmacklem
254221040Srmacklem	bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif,
255221040Srmacklem		sizeof(struct ifaliasreq));
256221040Srmacklem	bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway,
257221040Srmacklem		sizeof(struct sockaddr_in));
258221040Srmacklem	nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args);
259221040Srmacklem	if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
260221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_MYFH;
261221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH);
262191783Srmacklem	} else {
263221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_V2FH;
264221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH);
265191783Srmacklem	}
266221040Srmacklem	bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr,
267221040Srmacklem		sizeof(struct sockaddr_in));
268221040Srmacklem	bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN);
269221040Srmacklem	nfsv3_diskless.root_time = nfs_diskless.root_time;
270221040Srmacklem	bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam,
271221040Srmacklem		MAXHOSTNAMELEN);
272221040Srmacklem	nfs_diskless_valid = 3;
273191783Srmacklem}
274191783Srmacklem
275191783Srmacklem/*
276191783Srmacklem * nfs statfs call
277191783Srmacklem */
278191783Srmacklemstatic int
279191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
280191783Srmacklem{
281191783Srmacklem	struct vnode *vp;
282191990Sattilio	struct thread *td;
283191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
284191783Srmacklem	struct nfsvattr nfsva;
285191783Srmacklem	struct nfsfsinfo fs;
286191783Srmacklem	struct nfsstatfs sb;
287191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
288191783Srmacklem	struct nfsnode *np;
289191783Srmacklem
290191990Sattilio	td = curthread;
291191990Sattilio
292191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
293191783Srmacklem	if (error)
294191783Srmacklem		return (error);
295220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
296191783Srmacklem	if (error) {
297191783Srmacklem		vfs_unbusy(mp);
298191783Srmacklem		return (error);
299191783Srmacklem	}
300191783Srmacklem	vp = NFSTOV(np);
301191783Srmacklem	mtx_lock(&nmp->nm_mtx);
302191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
303191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
304191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
305191783Srmacklem		    &attrflag, NULL);
306191783Srmacklem		if (!error)
307191783Srmacklem			gotfsinfo = 1;
308191783Srmacklem	} else
309191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
310191783Srmacklem	if (!error)
311191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
312191783Srmacklem		    &attrflag, NULL);
313244042Srmacklem	if (error != 0)
314244042Srmacklem		NFSCL_DEBUG(2, "statfs=%d\n", error);
315191783Srmacklem	if (attrflag == 0) {
316191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
317244042Srmacklem		    td->td_ucred, td, &nfsva, NULL, NULL);
318191783Srmacklem		if (ret) {
319191783Srmacklem			/*
320191783Srmacklem			 * Just set default values to get things going.
321191783Srmacklem			 */
322191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
323191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
324191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
325191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
326191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
327191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
328191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
329191783Srmacklem			nfsva.na_vattr.va_gen = 1;
330191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
331191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
332191783Srmacklem		}
333191783Srmacklem	}
334191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
335191783Srmacklem	if (!error) {
336191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
337191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
338191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
339191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
340191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
341191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
342191783Srmacklem	    if (sbp != &mp->mnt_stat) {
343191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
344191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
345191783Srmacklem	    }
346191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
347191783Srmacklem	} else if (NFS_ISV4(vp)) {
348191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
349191783Srmacklem	}
350191783Srmacklem	vput(vp);
351191783Srmacklem	vfs_unbusy(mp);
352191783Srmacklem	return (error);
353191783Srmacklem}
354191783Srmacklem
355191783Srmacklem/*
356191783Srmacklem * nfs version 3 fsinfo rpc call
357191783Srmacklem */
358191783Srmacklemint
359191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
360191783Srmacklem    struct thread *td)
361191783Srmacklem{
362191783Srmacklem	struct nfsfsinfo fs;
363191783Srmacklem	struct nfsvattr nfsva;
364191783Srmacklem	int error, attrflag;
365191783Srmacklem
366191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
367191783Srmacklem	if (!error) {
368191783Srmacklem		if (attrflag)
369191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
370191783Srmacklem			    1);
371191783Srmacklem		mtx_lock(&nmp->nm_mtx);
372191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
373191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
374191783Srmacklem	}
375191783Srmacklem	return (error);
376191783Srmacklem}
377191783Srmacklem
378191783Srmacklem/*
379191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
380221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary
381191783Srmacklem * bootstrap.
382191783Srmacklem * It goes something like this:
383191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
384191783Srmacklem *   can talk to the server
385221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as
386191783Srmacklem *   a default gateway.
387191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
388191783Srmacklem *
389191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
390191783Srmacklem * structure, as well as other global NFS client variables here, as
391192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
392191783Srmacklem * client activity occurs.
393191783Srmacklem */
394221040Srmacklemstatic int
395221040Srmacklemnfs_mountroot(struct mount *mp)
396191783Srmacklem{
397192145Srmacklem	struct thread *td = curthread;
398221040Srmacklem	struct nfsv3_diskless *nd = &nfsv3_diskless;
399191783Srmacklem	struct socket *so;
400191783Srmacklem	struct vnode *vp;
401191783Srmacklem	struct ifreq ir;
402193066Sjamie	int error;
403191783Srmacklem	u_long l;
404191783Srmacklem	char buf[128];
405191783Srmacklem	char *cp;
406191783Srmacklem
407191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
408192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
409191783Srmacklem#elif defined(NFS_ROOT)
410191783Srmacklem	nfs_setup_diskless();
411191783Srmacklem#endif
412191783Srmacklem
413221040Srmacklem	if (nfs_diskless_valid == 0)
414191783Srmacklem		return (-1);
415221040Srmacklem	if (nfs_diskless_valid == 1)
416191783Srmacklem		nfs_convert_diskless();
417191783Srmacklem
418191783Srmacklem	/*
419191783Srmacklem	 * XXX splnet, so networks will receive...
420191783Srmacklem	 */
421191783Srmacklem	splnet();
422191783Srmacklem
423191783Srmacklem	/*
424191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
425191783Srmacklem	 * talk to the server.
426191783Srmacklem	 */
427191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
428191783Srmacklem	    td->td_ucred, td);
429191783Srmacklem	if (error)
430192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
431191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
432191783Srmacklem
433191783Srmacklem#if 0 /* XXX Bad idea */
434191783Srmacklem	/*
435191783Srmacklem	 * We might not have been told the right interface, so we pass
436191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
437191783Srmacklem	 * one of them configured.
438191783Srmacklem	 */
439191783Srmacklem
440191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
441191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
442191783Srmacklem		nd->myif.ifra_name[i] <= '9';
443191783Srmacklem		nd->myif.ifra_name[i] ++) {
444191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
445191783Srmacklem		if(!error)
446191783Srmacklem			break;
447191783Srmacklem	}
448191783Srmacklem#endif
449191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
450191783Srmacklem	if (error)
451192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
452273174Sdavide	if ((cp = kern_getenv("boot.netif.mtu")) != NULL) {
453191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
454191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
455191783Srmacklem		freeenv(cp);
456191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
457191783Srmacklem		if (error)
458192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
459191783Srmacklem	}
460191783Srmacklem	soclose(so);
461191783Srmacklem
462191783Srmacklem	/*
463191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
464191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
465191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
466191783Srmacklem	 * to avoid panicking inappropriately in that situation.
467191783Srmacklem	 */
468191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
469191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
470191783Srmacklem		struct sockaddr_in mask, sin;
471191783Srmacklem
472191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
473191783Srmacklem		sin = mask;
474191783Srmacklem		sin.sin_family = AF_INET;
475191783Srmacklem		sin.sin_len = sizeof(sin);
476192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
477218757Sbz		CURVNET_SET(TD_TO_VNET(td));
478231852Sbz		error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&sin,
479191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
480191783Srmacklem		    (struct sockaddr *)&mask,
481231852Sbz		    RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB);
482218757Sbz		CURVNET_RESTORE();
483191783Srmacklem		if (error)
484192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
485191783Srmacklem	}
486191783Srmacklem
487191783Srmacklem	/*
488191783Srmacklem	 * Create the rootfs mount point.
489191783Srmacklem	 */
490191783Srmacklem	nd->root_args.fh = nd->root_fh;
491191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
492191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
493191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
494191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
495191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
496191783Srmacklem	printf("NFS ROOT: %s\n", buf);
497192145Srmacklem	nd->root_args.hostname = buf;
498191783Srmacklem	if ((error = nfs_mountdiskless(buf,
499191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
500191783Srmacklem		return (error);
501191783Srmacklem	}
502191783Srmacklem
503191783Srmacklem	/*
504191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
505191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
506191783Srmacklem	 * mount the right /var based upon its preset value.
507191783Srmacklem	 */
508193066Sjamie	mtx_lock(&prison0.pr_mtx);
509194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
510194118Sjamie	    sizeof(prison0.pr_hostname));
511193066Sjamie	mtx_unlock(&prison0.pr_mtx);
512191783Srmacklem	inittodr(ntohl(nd->root_time));
513191783Srmacklem	return (0);
514191783Srmacklem}
515191783Srmacklem
516191783Srmacklem/*
517191783Srmacklem * Internal version of mount system call for diskless setup.
518191783Srmacklem */
519191783Srmacklemstatic int
520191783Srmacklemnfs_mountdiskless(char *path,
521191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
522191783Srmacklem    struct vnode **vpp, struct mount *mp)
523191783Srmacklem{
524191783Srmacklem	struct sockaddr *nam;
525221014Srmacklem	int dirlen, error;
526221014Srmacklem	char *dirpath;
527191783Srmacklem
528221014Srmacklem	/*
529221014Srmacklem	 * Find the directory path in "path", which also has the server's
530221014Srmacklem	 * name/ip address in it.
531221014Srmacklem	 */
532221014Srmacklem	dirpath = strchr(path, ':');
533221014Srmacklem	if (dirpath != NULL)
534221014Srmacklem		dirlen = strlen(++dirpath);
535221014Srmacklem	else
536221014Srmacklem		dirlen = 0;
537191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
538221014Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
539230547Sjhb	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO,
540244042Srmacklem	    NFS_DEFAULT_NEGNAMETIMEO, 0)) != 0) {
541192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
542191783Srmacklem		return (error);
543191783Srmacklem	}
544191783Srmacklem	return (0);
545191783Srmacklem}
546191783Srmacklem
547191783Srmacklemstatic void
548192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
549192585Srmacklem{
550192585Srmacklem	if (!strcmp(sec, "krb5"))
551192585Srmacklem		*flagsp |= NFSMNT_KERB;
552192585Srmacklem	else if (!strcmp(sec, "krb5i"))
553192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
554192585Srmacklem	else if (!strcmp(sec, "krb5p"))
555192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
556192585Srmacklem}
557192585Srmacklem
558192585Srmacklemstatic void
559191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
560214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
561191783Srmacklem{
562273485Srmacklem	int s;
563191783Srmacklem	int adjsock;
564214048Srmacklem	char *p;
565191783Srmacklem
566191783Srmacklem	s = splnet();
567191783Srmacklem
568191783Srmacklem	/*
569191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
570191783Srmacklem	 * an update.  If this is not an update, then either the read-only
571191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
572191783Srmacklem	 * intentionally at some previous point.
573191783Srmacklem	 */
574191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
575191783Srmacklem		MNT_ILOCK(mp);
576191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
577191783Srmacklem		MNT_IUNLOCK(mp);
578191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
579191783Srmacklem		MNT_ILOCK(mp);
580191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
581191783Srmacklem		MNT_IUNLOCK(mp);
582191783Srmacklem	}
583191783Srmacklem
584191783Srmacklem	/*
585191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
586191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
587191783Srmacklem	 * and soft timeout behavior.
588191783Srmacklem	 */
589191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
590191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
591191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
592220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
593220739Srmacklem			nmp->nm_retry = INT_MAX;
594220739Srmacklem		else
595220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
596191783Srmacklem	}
597191783Srmacklem
598220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
599220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
600220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
601191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
602220739Srmacklem	}
603191783Srmacklem
604220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
605220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
606220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
607191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
608220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
609191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
610191783Srmacklem
611191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
612191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
613191783Srmacklem	splx(s);
614191783Srmacklem
615191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
616191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
617191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
618191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
619191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
620191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
621191783Srmacklem	}
622191783Srmacklem
623191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
624191783Srmacklem		nmp->nm_retry = argp->retrans;
625191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
626191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
627191783Srmacklem	}
628191783Srmacklem
629191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
630191783Srmacklem		nmp->nm_wsize = argp->wsize;
631273486Srmacklem		/*
632273486Srmacklem		 * Clip at the power of 2 below the size. There is an
633273486Srmacklem		 * issue (not isolated) that causes intermittent page
634273486Srmacklem		 * faults if this is not done.
635273486Srmacklem		 */
636273486Srmacklem		if (nmp->nm_wsize > NFS_FABLKSIZE)
637273486Srmacklem			nmp->nm_wsize = 1 << (fls(nmp->nm_wsize) - 1);
638273486Srmacklem		else
639273485Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
640191783Srmacklem	}
641191783Srmacklem
642191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
643191783Srmacklem		nmp->nm_rsize = argp->rsize;
644273486Srmacklem		/*
645273486Srmacklem		 * Clip at the power of 2 below the size. There is an
646273486Srmacklem		 * issue (not isolated) that causes intermittent page
647273486Srmacklem		 * faults if this is not done.
648273486Srmacklem		 */
649273486Srmacklem		if (nmp->nm_rsize > NFS_FABLKSIZE)
650273486Srmacklem			nmp->nm_rsize = 1 << (fls(nmp->nm_rsize) - 1);
651273486Srmacklem		else
652273485Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
653191783Srmacklem	}
654191783Srmacklem
655191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
656191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
657191783Srmacklem	}
658191783Srmacklem
659191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
660191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
661191783Srmacklem	else
662191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
663191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
664191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
665191783Srmacklem	else
666191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
667191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
668191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
669191783Srmacklem	else
670191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
671191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
672191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
673191783Srmacklem	else
674191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
675191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
676191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
677191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
678191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
679191783Srmacklem
680191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
681191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
682191783Srmacklem			nmp->nm_readahead = argp->readahead;
683191783Srmacklem		else
684191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
685191783Srmacklem	}
686191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
687191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
688191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
689191783Srmacklem		else
690191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
691191783Srmacklem	}
692191783Srmacklem
693191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
694191783Srmacklem		    (nmp->nm_soproto != argp->proto));
695191783Srmacklem
696191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
697191783Srmacklem		int haslock = 0, error = 0;
698191783Srmacklem
699191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
700191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
701191783Srmacklem			if (!error)
702191783Srmacklem				haslock = 1;
703191783Srmacklem		}
704191783Srmacklem		if (!error) {
705191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
706191783Srmacklem		    if (haslock)
707191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
708191783Srmacklem		    nmp->nm_sotype = argp->sotype;
709191783Srmacklem		    nmp->nm_soproto = argp->proto;
710191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
711191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
712191783Srmacklem			    cred, td, 0)) {
713191783Srmacklem				printf("newnfs_args: retrying connect\n");
714276096Srmacklem				(void) nfs_catnap(PSOCK, 0, "nfscon");
715191783Srmacklem			}
716191783Srmacklem		}
717191783Srmacklem	} else {
718191783Srmacklem		nmp->nm_sotype = argp->sotype;
719191783Srmacklem		nmp->nm_soproto = argp->proto;
720191783Srmacklem	}
721214048Srmacklem
722214048Srmacklem	if (hostname != NULL) {
723214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
724214048Srmacklem		    sizeof(nmp->nm_hostname));
725214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
726214048Srmacklem		if (p != NULL)
727214048Srmacklem			*p = '\0';
728214048Srmacklem	}
729191783Srmacklem}
730191783Srmacklem
731221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
732273849Strasz    "noac", "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
733191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
734192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
735192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
736273849Strasz    "retrans", "actimeo", "acregmin", "acregmax", "acdirmin", "acdirmax",
737273849Strasz    "resvport", "readahead", "hostname", "timeo", "timeout", "addr", "fh",
738273849Strasz    "nfsv3", "sec", "principal", "nfsv4", "gssname", "allgssname", "dirpath",
739273849Strasz    "minorversion", "nametimeo", "negnametimeo", "nocto", "noncontigwr",
740273849Strasz    "pnfs", "wcommitsize",
741191783Srmacklem    NULL };
742191783Srmacklem
743191783Srmacklem/*
744299848Strasz * Parse the "from" mountarg, passed by the generic mount(8) program
745299848Strasz * or the mountroot code.  This is used when rerooting into NFS.
746299848Strasz *
747299848Strasz * Note that the "hostname" is actually a "hostname:/share/path" string.
748299848Strasz */
749299848Straszstatic int
750299848Strasznfs_mount_parse_from(struct vfsoptlist *opts, char **hostnamep,
751299848Strasz    struct sockaddr_in **sinp, char *dirpath, size_t dirpathsize, int *dirlenp)
752299848Strasz{
753299848Strasz	char nam[MNAMELEN + 1];
754299848Strasz	char *delimp, *hostp, *spec;
755299848Strasz	int error, have_bracket = 0, offset, rv, speclen;
756299848Strasz	struct sockaddr_in *sin;
757299848Strasz	size_t len;
758299848Strasz
759299848Strasz	error = vfs_getopt(opts, "from", (void **)&spec, &speclen);
760299848Strasz	if (error != 0)
761299848Strasz		return (error);
762299848Strasz
763299848Strasz	/*
764299848Strasz	 * This part comes from sbin/mount_nfs/mount_nfs.c:getnfsargs().
765299848Strasz	 */
766301566Scem	if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL &&
767301566Scem	    *(delimp + 1) == ':') {
768301566Scem		hostp = spec + 1;
769301566Scem		spec = delimp + 2;
770301566Scem		have_bracket = 1;
771301566Scem	} else if ((delimp = strrchr(spec, ':')) != NULL) {
772301566Scem		hostp = spec;
773301566Scem		spec = delimp + 1;
774301566Scem	} else if ((delimp = strrchr(spec, '@')) != NULL) {
775301566Scem		printf("%s: path@server syntax is deprecated, "
776299848Strasz		    "use server:path\n", __func__);
777301566Scem		hostp = delimp + 1;
778301566Scem	} else {
779301566Scem		printf("%s: no <host>:<dirpath> nfs-name\n", __func__);
780301566Scem		return (EINVAL);
781301566Scem	}
782301566Scem	*delimp = '\0';
783299848Strasz
784301566Scem	/*
785301566Scem	 * If there has been a trailing slash at mounttime it seems
786301566Scem	 * that some mountd implementations fail to remove the mount
787301566Scem	 * entries from their mountlist while unmounting.
788301566Scem	 */
789301566Scem	for (speclen = strlen(spec);
790301566Scem	    speclen > 1 && spec[speclen - 1] == '/';
791301566Scem	    speclen--)
792301566Scem		spec[speclen - 1] = '\0';
793301566Scem	if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
794301566Scem		printf("%s: %s:%s: name too long", __func__, hostp, spec);
795301566Scem		return (EINVAL);
796301566Scem	}
797299848Strasz	/* Make both '@' and ':' notations equal */
798299848Strasz	if (*hostp != '\0') {
799299848Strasz		len = strlen(hostp);
800299848Strasz		offset = 0;
801299848Strasz		if (have_bracket)
802299848Strasz			nam[offset++] = '[';
803299848Strasz		memmove(nam + offset, hostp, len);
804299848Strasz		if (have_bracket)
805299848Strasz			nam[len + offset++] = ']';
806299848Strasz		nam[len + offset++] = ':';
807299848Strasz		memmove(nam + len + offset, spec, speclen);
808299848Strasz		nam[len + speclen + offset] = '\0';
809301564Scem	} else
810301564Scem		nam[0] = '\0';
811299848Strasz
812299848Strasz	/*
813299848Strasz	 * XXX: IPv6
814299848Strasz	 */
815299848Strasz	sin = malloc(sizeof(*sin), M_SONAME, M_WAITOK);
816299848Strasz	rv = inet_pton(AF_INET, hostp, &sin->sin_addr);
817299848Strasz	if (rv != 1) {
818299848Strasz		printf("%s: cannot parse '%s', inet_pton() returned %d\n",
819299848Strasz		    __func__, hostp, rv);
820299848Strasz		free(sin, M_SONAME);
821299848Strasz		return (EINVAL);
822299848Strasz	}
823299848Strasz
824299848Strasz	sin->sin_len = sizeof(*sin);
825299848Strasz	sin->sin_family = AF_INET;
826299848Strasz	/*
827299848Strasz	 * XXX: hardcoded port number.
828299848Strasz	 */
829299848Strasz	sin->sin_port = htons(2049);
830299848Strasz
831299848Strasz	*hostnamep = strdup(nam, M_NEWNFSMNT);
832299848Strasz	*sinp = sin;
833299848Strasz	strlcpy(dirpath, spec, dirpathsize);
834299848Strasz	*dirlenp = strlen(dirpath);
835299848Strasz
836299848Strasz	return (0);
837299848Strasz}
838299848Strasz
839299848Strasz/*
840191783Srmacklem * VFS Operations.
841191783Srmacklem *
842191783Srmacklem * mount system call
843191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
844191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
845300160Sglebius * doing the getsockaddr() call because getsockaddr() allocates an mbuf and
846191783Srmacklem * an error after that means that I have to release the mbuf.
847191783Srmacklem */
848191783Srmacklem/* ARGSUSED */
849191783Srmacklemstatic int
850191990Sattilionfs_mount(struct mount *mp)
851191783Srmacklem{
852191783Srmacklem	struct nfs_args args = {
853191783Srmacklem	    .version = NFS_ARGSVERSION,
854191783Srmacklem	    .addr = NULL,
855191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
856191783Srmacklem	    .sotype = SOCK_STREAM,
857191783Srmacklem	    .proto = 0,
858191783Srmacklem	    .fh = NULL,
859191783Srmacklem	    .fhsize = 0,
860220739Srmacklem	    .flags = NFSMNT_RESVPORT,
861191783Srmacklem	    .wsize = NFS_WSIZE,
862191783Srmacklem	    .rsize = NFS_RSIZE,
863191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
864191783Srmacklem	    .timeo = 10,
865191783Srmacklem	    .retrans = NFS_RETRANS,
866191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
867191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
868191783Srmacklem	    .hostname = NULL,
869191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
870191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
871191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
872191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
873191783Srmacklem	};
874192585Srmacklem	int error = 0, ret, len;
875192585Srmacklem	struct sockaddr *nam = NULL;
876191783Srmacklem	struct vnode *vp;
877191990Sattilio	struct thread *td;
878191783Srmacklem	char hst[MNAMELEN];
879191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
880285113Srmacklem	char *cp, *opt, *name, *secname;
881230547Sjhb	int nametimeo = NFS_DEFAULT_NAMETIMEO;
882203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
883244042Srmacklem	int minvers = 0;
884299848Strasz	int dirlen, has_nfs_args_opt, has_nfs_from_opt,
885299848Strasz	    krbnamelen, srvkrbnamelen;
886221205Srmacklem	size_t hstlen;
887191783Srmacklem
888221190Srmacklem	has_nfs_args_opt = 0;
889299848Strasz	has_nfs_from_opt = 0;
890191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
891191783Srmacklem		error = EINVAL;
892191783Srmacklem		goto out;
893191783Srmacklem	}
894191783Srmacklem
895191990Sattilio	td = curthread;
896299848Strasz	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS &&
897299848Strasz	    nfs_diskless_valid != 0) {
898221040Srmacklem		error = nfs_mountroot(mp);
899191783Srmacklem		goto out;
900191783Srmacklem	}
901191783Srmacklem
902192585Srmacklem	nfscl_init();
903191783Srmacklem
904221190Srmacklem	/*
905221190Srmacklem	 * The old mount_nfs program passed the struct nfs_args
906221190Srmacklem	 * from userspace to kernel.  The new mount_nfs program
907221190Srmacklem	 * passes string options via nmount() from userspace to kernel
908221190Srmacklem	 * and we populate the struct nfs_args in the kernel.
909221190Srmacklem	 */
910221190Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) {
911221190Srmacklem		error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args,
912221190Srmacklem		    sizeof(args));
913221190Srmacklem		if (error != 0)
914221190Srmacklem			goto out;
915221190Srmacklem
916221190Srmacklem		if (args.version != NFS_ARGSVERSION) {
917221190Srmacklem			error = EPROGMISMATCH;
918221190Srmacklem			goto out;
919221190Srmacklem		}
920221190Srmacklem		has_nfs_args_opt = 1;
921221190Srmacklem	}
922221190Srmacklem
923192585Srmacklem	/* Handle the new style options. */
924273849Strasz	if (vfs_getopt(mp->mnt_optnew, "noac", NULL, NULL) == 0) {
925273849Strasz		args.acdirmin = args.acdirmax =
926273849Strasz		    args.acregmin = args.acregmax = 0;
927273849Strasz		args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX |
928273849Strasz		    NFSMNT_ACREGMIN | NFSMNT_ACREGMAX;
929273849Strasz	}
930192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
931192585Srmacklem		args.flags |= NFSMNT_NOCONN;
932192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
933273852Strasz		args.flags &= ~NFSMNT_NOCONN;
934192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
935192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
936192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
937192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
938192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
939192585Srmacklem		args.flags |= NFSMNT_INT;
940192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
941192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
942192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
943192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
944192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
945192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
946192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
947192585Srmacklem		args.flags |= NFSMNT_SOFT;
948192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
949192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
950192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
951192585Srmacklem		args.sotype = SOCK_DGRAM;
952192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
953192585Srmacklem		args.sotype = SOCK_DGRAM;
954192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
955192585Srmacklem		args.sotype = SOCK_STREAM;
956192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
957192585Srmacklem		args.flags |= NFSMNT_NFSV3;
958192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
959192585Srmacklem		args.flags |= NFSMNT_NFSV4;
960192585Srmacklem		args.sotype = SOCK_STREAM;
961191783Srmacklem	}
962192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
963192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
964221436Sru	if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
965221436Sru		args.flags |= NFSMNT_NOCTO;
966259084Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noncontigwr", NULL, NULL) == 0)
967259084Srmacklem		args.flags |= NFSMNT_NONCONTIGWR;
968244042Srmacklem	if (vfs_getopt(mp->mnt_optnew, "pnfs", NULL, NULL) == 0)
969244042Srmacklem		args.flags |= NFSMNT_PNFS;
970192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
971192585Srmacklem		if (opt == NULL) {
972192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
973192585Srmacklem			error = EINVAL;
974192585Srmacklem			goto out;
975192585Srmacklem		}
976192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
977192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
978192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
979192585Srmacklem			    opt);
980192585Srmacklem			error = EINVAL;
981192585Srmacklem			goto out;
982192585Srmacklem		}
983192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
984192585Srmacklem	}
985192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
986192585Srmacklem		if (opt == NULL) {
987192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
988192585Srmacklem			error = EINVAL;
989192585Srmacklem			goto out;
990192585Srmacklem		}
991192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
992192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
993192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
994192585Srmacklem			    opt);
995192585Srmacklem			error = EINVAL;
996192585Srmacklem			goto out;
997192585Srmacklem		}
998192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
999192585Srmacklem	}
1000192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
1001192585Srmacklem		if (opt == NULL) {
1002192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
1003192585Srmacklem			error = EINVAL;
1004192585Srmacklem			goto out;
1005192585Srmacklem		}
1006192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
1007192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
1008192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
1009192585Srmacklem			    opt);
1010192585Srmacklem			error = EINVAL;
1011192585Srmacklem			goto out;
1012192585Srmacklem		}
1013192585Srmacklem		args.flags |= NFSMNT_WSIZE;
1014192585Srmacklem	}
1015192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
1016192585Srmacklem		if (opt == NULL) {
1017192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
1018192585Srmacklem			error = EINVAL;
1019192585Srmacklem			goto out;
1020192585Srmacklem		}
1021192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
1022192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
1023192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
1024192585Srmacklem			    opt);
1025192585Srmacklem			error = EINVAL;
1026192585Srmacklem			goto out;
1027192585Srmacklem		}
1028192585Srmacklem		args.flags |= NFSMNT_RSIZE;
1029192585Srmacklem	}
1030192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
1031192585Srmacklem		if (opt == NULL) {
1032192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
1033192585Srmacklem			error = EINVAL;
1034192585Srmacklem			goto out;
1035192585Srmacklem		}
1036192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
1037192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
1038192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
1039192585Srmacklem			    opt);
1040192585Srmacklem			error = EINVAL;
1041192585Srmacklem			goto out;
1042192585Srmacklem		}
1043192585Srmacklem		args.flags |= NFSMNT_RETRANS;
1044192585Srmacklem	}
1045273849Strasz	if (vfs_getopt(mp->mnt_optnew, "actimeo", (void **)&opt, NULL) == 0) {
1046273849Strasz		ret = sscanf(opt, "%d", &args.acregmin);
1047273849Strasz		if (ret != 1 || args.acregmin < 0) {
1048273849Strasz			vfs_mount_error(mp, "illegal actimeo: %s",
1049273849Strasz			    opt);
1050273849Strasz			error = EINVAL;
1051273849Strasz			goto out;
1052273849Strasz		}
1053273849Strasz		args.acdirmin = args.acdirmax = args.acregmax = args.acregmin;
1054273849Strasz		args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX |
1055273849Strasz		    NFSMNT_ACREGMIN | NFSMNT_ACREGMAX;
1056273849Strasz	}
1057192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
1058192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
1059192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
1060192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
1061192585Srmacklem			    opt);
1062192585Srmacklem			error = EINVAL;
1063192585Srmacklem			goto out;
1064192585Srmacklem		}
1065192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
1066192585Srmacklem	}
1067192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
1068192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
1069192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
1070192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
1071192585Srmacklem			    opt);
1072192585Srmacklem			error = EINVAL;
1073192585Srmacklem			goto out;
1074192585Srmacklem		}
1075192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
1076192585Srmacklem	}
1077192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
1078192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
1079192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
1080192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
1081192585Srmacklem			    opt);
1082192585Srmacklem			error = EINVAL;
1083192585Srmacklem			goto out;
1084192585Srmacklem		}
1085192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
1086192585Srmacklem	}
1087192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
1088192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
1089192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
1090192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
1091192585Srmacklem			    opt);
1092192585Srmacklem			error = EINVAL;
1093192585Srmacklem			goto out;
1094192585Srmacklem		}
1095192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
1096192585Srmacklem	}
1097227507Sjhb	if (vfs_getopt(mp->mnt_optnew, "wcommitsize", (void **)&opt, NULL) == 0) {
1098227507Sjhb		ret = sscanf(opt, "%d", &args.wcommitsize);
1099227507Sjhb		if (ret != 1 || args.wcommitsize < 0) {
1100227507Sjhb			vfs_mount_error(mp, "illegal wcommitsize: %s", opt);
1101227507Sjhb			error = EINVAL;
1102227507Sjhb			goto out;
1103227507Sjhb		}
1104227507Sjhb		args.flags |= NFSMNT_WCOMMITSIZE;
1105227507Sjhb	}
1106273849Strasz	if (vfs_getopt(mp->mnt_optnew, "timeo", (void **)&opt, NULL) == 0) {
1107273849Strasz		ret = sscanf(opt, "%d", &args.timeo);
1108273849Strasz		if (ret != 1 || args.timeo <= 0) {
1109273849Strasz			vfs_mount_error(mp, "illegal timeo: %s",
1110273849Strasz			    opt);
1111273849Strasz			error = EINVAL;
1112273849Strasz			goto out;
1113273849Strasz		}
1114273849Strasz		args.flags |= NFSMNT_TIMEO;
1115273849Strasz	}
1116192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
1117192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
1118192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
1119192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
1120192585Srmacklem			    opt);
1121192585Srmacklem			error = EINVAL;
1122192585Srmacklem			goto out;
1123192585Srmacklem		}
1124192585Srmacklem		args.flags |= NFSMNT_TIMEO;
1125192585Srmacklem	}
1126230547Sjhb	if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) {
1127230547Sjhb		ret = sscanf(opt, "%d", &nametimeo);
1128230547Sjhb		if (ret != 1 || nametimeo < 0) {
1129230547Sjhb			vfs_mount_error(mp, "illegal nametimeo: %s", opt);
1130230547Sjhb			error = EINVAL;
1131230547Sjhb			goto out;
1132230547Sjhb		}
1133230547Sjhb	}
1134203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
1135203303Srmacklem	    == 0) {
1136203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
1137203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
1138203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
1139203303Srmacklem			    opt);
1140203303Srmacklem			error = EINVAL;
1141203303Srmacklem			goto out;
1142203303Srmacklem		}
1143203303Srmacklem	}
1144244042Srmacklem	if (vfs_getopt(mp->mnt_optnew, "minorversion", (void **)&opt, NULL) ==
1145244042Srmacklem	    0) {
1146244042Srmacklem		ret = sscanf(opt, "%d", &minvers);
1147244042Srmacklem		if (ret != 1 || minvers < 0 || minvers > 1 ||
1148244042Srmacklem		    (args.flags & NFSMNT_NFSV4) == 0) {
1149244042Srmacklem			vfs_mount_error(mp, "illegal minorversion: %s", opt);
1150244042Srmacklem			error = EINVAL;
1151244042Srmacklem			goto out;
1152244042Srmacklem		}
1153244042Srmacklem	}
1154192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
1155192585Srmacklem		(void **) &secname, NULL) == 0)
1156192585Srmacklem		nfs_sec_name(secname, &args.flags);
1157191783Srmacklem
1158191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1159191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
1160191783Srmacklem
1161191783Srmacklem		if (nmp == NULL) {
1162191783Srmacklem			error = EIO;
1163191783Srmacklem			goto out;
1164191783Srmacklem		}
1165230803Srmacklem
1166191783Srmacklem		/*
1167230803Srmacklem		 * If a change from TCP->UDP is done and there are thread(s)
1168298788Spfg		 * that have I/O RPC(s) in progress with a transfer size
1169230803Srmacklem		 * greater than NFS_MAXDGRAMDATA, those thread(s) will be
1170230803Srmacklem		 * hung, retrying the RPC(s) forever. Usually these threads
1171230803Srmacklem		 * will be seen doing an uninterruptible sleep on wait channel
1172276096Srmacklem		 * "nfsreq".
1173230803Srmacklem		 */
1174230803Srmacklem		if (args.sotype == SOCK_DGRAM && nmp->nm_sotype == SOCK_STREAM)
1175230803Srmacklem			tprintf(td->td_proc, LOG_WARNING,
1176230803Srmacklem	"Warning: mount -u that changes TCP->UDP can result in hung threads\n");
1177230803Srmacklem
1178230803Srmacklem		/*
1179191783Srmacklem		 * When doing an update, we can't change version,
1180191783Srmacklem		 * security, switch lockd strategies or change cookie
1181191783Srmacklem		 * translation
1182191783Srmacklem		 */
1183191783Srmacklem		args.flags = (args.flags &
1184191783Srmacklem		    ~(NFSMNT_NFSV3 |
1185191783Srmacklem		      NFSMNT_NFSV4 |
1186191783Srmacklem		      NFSMNT_KERB |
1187191783Srmacklem		      NFSMNT_INTEGRITY |
1188191783Srmacklem		      NFSMNT_PRIVACY |
1189191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
1190191783Srmacklem		    (nmp->nm_flag &
1191191783Srmacklem			(NFSMNT_NFSV3 |
1192191783Srmacklem			 NFSMNT_NFSV4 |
1193191783Srmacklem			 NFSMNT_KERB |
1194191783Srmacklem			 NFSMNT_INTEGRITY |
1195191783Srmacklem			 NFSMNT_PRIVACY |
1196191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
1197214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
1198191783Srmacklem		goto out;
1199191783Srmacklem	}
1200191783Srmacklem
1201191783Srmacklem	/*
1202191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
1203191783Srmacklem	 * or no-connection mode for those protocols that support
1204191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
1205191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
1206191783Srmacklem	 * to receive replies from a different IP then the request was
1207191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
1208191783Srmacklem	 * not 0.
1209191783Srmacklem	 */
1210191783Srmacklem	if (nfs_ip_paranoia == 0)
1211191783Srmacklem		args.flags |= NFSMNT_NOCONN;
1212192585Srmacklem
1213221190Srmacklem	if (has_nfs_args_opt != 0) {
1214221190Srmacklem		/*
1215221190Srmacklem		 * In the 'nfs_args' case, the pointers in the args
1216221190Srmacklem		 * structure are in userland - we copy them in here.
1217221190Srmacklem		 */
1218221190Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) {
1219192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
1220191783Srmacklem			error = EINVAL;
1221191783Srmacklem			goto out;
1222191783Srmacklem		}
1223221190Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1224221190Srmacklem		    args.fhsize);
1225221190Srmacklem		if (error != 0)
1226221190Srmacklem			goto out;
1227221205Srmacklem		error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen);
1228221190Srmacklem		if (error != 0)
1229221190Srmacklem			goto out;
1230221205Srmacklem		bzero(&hst[hstlen], MNAMELEN - hstlen);
1231221190Srmacklem		args.hostname = hst;
1232300160Sglebius		/* getsockaddr() call must be after above copyin() calls */
1233221190Srmacklem		error = getsockaddr(&nam, (caddr_t)args.addr,
1234221190Srmacklem		    args.addrlen);
1235221190Srmacklem		if (error != 0)
1236221190Srmacklem			goto out;
1237299848Strasz	} else if (nfs_mount_parse_from(mp->mnt_optnew,
1238299848Strasz	    &args.hostname, (struct sockaddr_in **)&nam, dirpath,
1239299848Strasz	    sizeof(dirpath), &dirlen) == 0) {
1240299848Strasz		has_nfs_from_opt = 1;
1241299848Strasz		bcopy(args.hostname, hst, MNAMELEN);
1242299848Strasz		hst[MNAMELEN - 1] = '\0';
1243299848Strasz
1244299848Strasz		/*
1245299848Strasz		 * This only works with NFSv4 for now.
1246299848Strasz		 */
1247299848Strasz		args.fhsize = 0;
1248299848Strasz		args.flags |= NFSMNT_NFSV4;
1249299848Strasz		args.sotype = SOCK_STREAM;
1250191783Srmacklem	} else {
1251221190Srmacklem		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
1252221190Srmacklem		    &args.fhsize) == 0) {
1253221190Srmacklem			if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
1254221190Srmacklem				vfs_mount_error(mp, "Bad file handle");
1255221190Srmacklem				error = EINVAL;
1256221190Srmacklem				goto out;
1257221190Srmacklem			}
1258221190Srmacklem			bcopy(args.fh, nfh, args.fhsize);
1259221190Srmacklem		} else {
1260221190Srmacklem			args.fhsize = 0;
1261221190Srmacklem		}
1262221190Srmacklem		(void) vfs_getopt(mp->mnt_optnew, "hostname",
1263221190Srmacklem		    (void **)&args.hostname, &len);
1264221190Srmacklem		if (args.hostname == NULL) {
1265221190Srmacklem			vfs_mount_error(mp, "Invalid hostname");
1266221190Srmacklem			error = EINVAL;
1267221190Srmacklem			goto out;
1268221190Srmacklem		}
1269221190Srmacklem		bcopy(args.hostname, hst, MNAMELEN);
1270221190Srmacklem		hst[MNAMELEN - 1] = '\0';
1271192585Srmacklem	}
1272192585Srmacklem
1273192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1274192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1275285113Srmacklem	else {
1276192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1277285113Srmacklem		cp = strchr(srvkrbname, ':');
1278285113Srmacklem		if (cp != NULL)
1279285113Srmacklem			*cp = '\0';
1280285113Srmacklem	}
1281221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1282192585Srmacklem
1283192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1284192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1285192585Srmacklem	else
1286191783Srmacklem		krbname[0] = '\0';
1287221014Srmacklem	krbnamelen = strlen(krbname);
1288192585Srmacklem
1289299848Strasz	if (has_nfs_from_opt == 0) {
1290299848Strasz		if (vfs_getopt(mp->mnt_optnew,
1291299848Strasz		    "dirpath", (void **)&name, NULL) == 0)
1292299848Strasz			strlcpy(dirpath, name, sizeof (dirpath));
1293299848Strasz		else
1294299848Strasz			dirpath[0] = '\0';
1295299848Strasz		dirlen = strlen(dirpath);
1296299848Strasz	}
1297192585Srmacklem
1298299848Strasz	if (has_nfs_args_opt == 0 && has_nfs_from_opt == 0) {
1299222075Srmacklem		if (vfs_getopt(mp->mnt_optnew, "addr",
1300222075Srmacklem		    (void **)&args.addr, &args.addrlen) == 0) {
1301222075Srmacklem			if (args.addrlen > SOCK_MAXADDRLEN) {
1302222075Srmacklem				error = ENAMETOOLONG;
1303222075Srmacklem				goto out;
1304222075Srmacklem			}
1305222075Srmacklem			nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1306222075Srmacklem			bcopy(args.addr, nam, args.addrlen);
1307222075Srmacklem			nam->sa_len = args.addrlen;
1308222075Srmacklem		} else {
1309222075Srmacklem			vfs_mount_error(mp, "No server address");
1310222075Srmacklem			error = EINVAL;
1311191783Srmacklem			goto out;
1312191783Srmacklem		}
1313191783Srmacklem	}
1314192585Srmacklem
1315191783Srmacklem	args.fh = nfh;
1316221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1317221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1318244042Srmacklem	    nametimeo, negnametimeo, minvers);
1319191783Srmacklemout:
1320191783Srmacklem	if (!error) {
1321191783Srmacklem		MNT_ILOCK(mp);
1322281562Srmacklem		mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_NO_IOPF |
1323281562Srmacklem		    MNTK_USES_BCACHE;
1324191783Srmacklem		MNT_IUNLOCK(mp);
1325191783Srmacklem	}
1326191783Srmacklem	return (error);
1327191783Srmacklem}
1328191783Srmacklem
1329191783Srmacklem
1330191783Srmacklem/*
1331191783Srmacklem * VFS Operations.
1332191783Srmacklem *
1333191783Srmacklem * mount system call
1334191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1335191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1336300160Sglebius * doing the getsockaddr() call because getsockaddr() allocates an mbuf and
1337191783Srmacklem * an error after that means that I have to release the mbuf.
1338191783Srmacklem */
1339191783Srmacklem/* ARGSUSED */
1340191783Srmacklemstatic int
1341230249Smckusicknfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
1342191783Srmacklem{
1343191783Srmacklem	int error;
1344191783Srmacklem	struct nfs_args args;
1345191783Srmacklem
1346191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1347191783Srmacklem	if (error)
1348191783Srmacklem		return error;
1349191783Srmacklem
1350191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1351191783Srmacklem
1352191783Srmacklem	error = kernel_mount(ma, flags);
1353191783Srmacklem	return (error);
1354191783Srmacklem}
1355191783Srmacklem
1356191783Srmacklem/*
1357191783Srmacklem * Common code for mount and mountroot
1358191783Srmacklem */
1359191783Srmacklemstatic int
1360191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1361221014Srmacklem    char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
1362221014Srmacklem    u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
1363244042Srmacklem    struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo,
1364244042Srmacklem    int minvers)
1365191783Srmacklem{
1366191783Srmacklem	struct nfsmount *nmp;
1367191783Srmacklem	struct nfsnode *np;
1368195762Srmacklem	int error, trycnt, ret;
1369191783Srmacklem	struct nfsvattr nfsva;
1370244042Srmacklem	struct nfsclclient *clp;
1371244042Srmacklem	struct nfsclds *dsp, *tdsp;
1372244042Srmacklem	uint32_t lease;
1373191783Srmacklem	static u_int64_t clval = 0;
1374191783Srmacklem
1375244042Srmacklem	NFSCL_DEBUG(3, "in mnt\n");
1376244042Srmacklem	clp = NULL;
1377191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1378191783Srmacklem		nmp = VFSTONFS(mp);
1379191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1380191783Srmacklem		FREE(nam, M_SONAME);
1381191783Srmacklem		return (0);
1382191783Srmacklem	} else {
1383191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1384221014Srmacklem		    krbnamelen + dirlen + srvkrbnamelen + 2,
1385221014Srmacklem		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
1386191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1387191783Srmacklem		if (clval == 0)
1388191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1389191783Srmacklem		nmp->nm_clval = clval++;
1390221014Srmacklem		nmp->nm_krbnamelen = krbnamelen;
1391221014Srmacklem		nmp->nm_dirpathlen = dirlen;
1392221014Srmacklem		nmp->nm_srvkrbnamelen = srvkrbnamelen;
1393192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1394191783Srmacklem			/*
1395192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1396192675Srmacklem			 * the nfsv4 state handling operations if there is
1397192675Srmacklem			 * no host based principal set. Use the uid of
1398192675Srmacklem			 * this user if not root, since they are doing the
1399192675Srmacklem			 * mount. I don't think setting this for root will
1400192675Srmacklem			 * work, since root normally does not have user
1401192675Srmacklem			 * credentials in a credentials cache.
1402191783Srmacklem			 */
1403192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1404191783Srmacklem		} else {
1405191783Srmacklem			/*
1406192675Srmacklem			 * Just set to -1, so it won't be used.
1407191783Srmacklem			 */
1408191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1409191783Srmacklem		}
1410191783Srmacklem
1411191783Srmacklem		/* Copy and null terminate all the names */
1412191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1413191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1414191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1415191783Srmacklem		}
1416191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1417191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1418191783Srmacklem			    nmp->nm_dirpathlen);
1419191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1420191783Srmacklem			    + 1] = '\0';
1421191783Srmacklem		}
1422191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1423191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1424191783Srmacklem			    nmp->nm_srvkrbnamelen);
1425191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1426191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1427191783Srmacklem		}
1428191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1429191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1430191783Srmacklem		mp->mnt_data = nmp;
1431214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1432216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1433191783Srmacklem	}
1434191783Srmacklem	vfs_getnewfsid(mp);
1435191783Srmacklem	nmp->nm_mountp = mp;
1436230547Sjhb	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1437227493Srmacklem
1438227493Srmacklem	/*
1439230547Sjhb	 * Since nfs_decode_args() might optionally set them, these
1440230547Sjhb	 * need to be set to defaults before the call, so that the
1441230547Sjhb	 * optional settings aren't overwritten.
1442227493Srmacklem	 */
1443230547Sjhb	nmp->nm_nametimeo = nametimeo;
1444203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1445227493Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1446227493Srmacklem	nmp->nm_retry = NFS_RETRANS;
1447227493Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1448281738Smav
1449281738Smav	/* This is empirical approximation of sqrt(hibufspace) * 256. */
1450281738Smav	nmp->nm_wcommitsize = NFS_MAXBSIZE / 256;
1451281738Smav	while ((long)nmp->nm_wcommitsize * nmp->nm_wcommitsize < hibufspace)
1452281738Smav		nmp->nm_wcommitsize *= 2;
1453281738Smav	nmp->nm_wcommitsize *= 256;
1454281738Smav
1455244042Srmacklem	if ((argp->flags & NFSMNT_NFSV4) != 0)
1456244042Srmacklem		nmp->nm_minorvers = minvers;
1457244042Srmacklem	else
1458244042Srmacklem		nmp->nm_minorvers = 0;
1459191783Srmacklem
1460214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1461192585Srmacklem
1462191783Srmacklem	/*
1463191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1464191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1465191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1466191783Srmacklem	 *
1467191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1468191783Srmacklem	 * that we can handle until we find out otherwise.
1469191783Srmacklem	 */
1470191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1471191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1472191783Srmacklem	else
1473221537Srmacklem		nmp->nm_maxfilesize = OFF_MAX;
1474191783Srmacklem
1475191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1476191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1477191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1478191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1479191783Srmacklem	}
1480191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1481191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1482191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1483191783Srmacklem		nmp->nm_tprintf_delay = 0;
1484191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1485191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1486191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1487191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1488191783Srmacklem	if (nmp->nm_fhsize > 0)
1489191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1490191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1491191783Srmacklem	nmp->nm_nam = nam;
1492191783Srmacklem	/* Set up the sockets and per-host congestion */
1493191783Srmacklem	nmp->nm_sotype = argp->sotype;
1494191783Srmacklem	nmp->nm_soproto = argp->proto;
1495191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1496191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1497191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1498191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1499191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1500191783Srmacklem	else
1501191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1502191783Srmacklem
1503191783Srmacklem
1504191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1505191783Srmacklem		goto bad;
1506244042Srmacklem	/* For NFSv4.1, get the clientid now. */
1507244042Srmacklem	if (nmp->nm_minorvers > 0) {
1508244042Srmacklem		NFSCL_DEBUG(3, "at getcl\n");
1509244042Srmacklem		error = nfscl_getcl(mp, cred, td, 0, &clp);
1510244042Srmacklem		NFSCL_DEBUG(3, "aft getcl=%d\n", error);
1511244042Srmacklem		if (error != 0)
1512244042Srmacklem			goto bad;
1513244042Srmacklem	}
1514191783Srmacklem
1515191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1516191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1517244042Srmacklem		NFSCL_DEBUG(3, "in dirp\n");
1518191783Srmacklem		/*
1519191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1520191783Srmacklem		 * path needs to be looked up.
1521191783Srmacklem		 */
1522191783Srmacklem		trycnt = 3;
1523191783Srmacklem		do {
1524191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1525191783Srmacklem			    cred, td);
1526244042Srmacklem			NFSCL_DEBUG(3, "aft dirp=%d\n", error);
1527191783Srmacklem			if (error)
1528207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1529191783Srmacklem		} while (error && --trycnt > 0);
1530191783Srmacklem		if (error) {
1531191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1532191783Srmacklem			goto bad;
1533191783Srmacklem		}
1534191783Srmacklem	}
1535244042Srmacklem
1536244042Srmacklem	/*
1537244042Srmacklem	 * A reference count is needed on the nfsnode representing the
1538244042Srmacklem	 * remote root.  If this object is not persistent, then backward
1539244042Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1540244042Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1541244042Srmacklem	 * this problem, because one can identify root inodes by their
1542244042Srmacklem	 * number == ROOTINO (2).
1543244042Srmacklem	 */
1544191783Srmacklem	if (nmp->nm_fhsize > 0) {
1545195762Srmacklem		/*
1546195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1547195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1548195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1549195762Srmacklem		 */
1550195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1551220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1552220732Srmacklem		    LK_EXCLUSIVE);
1553191783Srmacklem		if (error)
1554191783Srmacklem			goto bad;
1555191783Srmacklem		*vpp = NFSTOV(np);
1556191783Srmacklem
1557191783Srmacklem		/*
1558191783Srmacklem		 * Get file attributes and transfer parameters for the
1559191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1560191783Srmacklem		 * (*vpp)->v_type with the correct value.
1561191783Srmacklem		 */
1562191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1563244042Srmacklem		    cred, td, &nfsva, NULL, &lease);
1564191783Srmacklem		if (ret) {
1565191783Srmacklem			/*
1566191783Srmacklem			 * Just set default values to get things going.
1567191783Srmacklem			 */
1568191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1569191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1570191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1571191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1572191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1573191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1574191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1575191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1576191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1577191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1578244042Srmacklem			lease = 60;
1579191783Srmacklem		}
1580191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1581244042Srmacklem		if (nmp->nm_minorvers > 0) {
1582244042Srmacklem			NFSCL_DEBUG(3, "lease=%d\n", (int)lease);
1583244042Srmacklem			NFSLOCKCLSTATE();
1584244042Srmacklem			clp->nfsc_renew = NFSCL_RENEW(lease);
1585244042Srmacklem			clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
1586244042Srmacklem			clp->nfsc_clientidrev++;
1587244042Srmacklem			if (clp->nfsc_clientidrev == 0)
1588244042Srmacklem				clp->nfsc_clientidrev++;
1589244042Srmacklem			NFSUNLOCKCLSTATE();
1590244042Srmacklem			/*
1591244042Srmacklem			 * Mount will succeed, so the renew thread can be
1592244042Srmacklem			 * started now.
1593244042Srmacklem			 */
1594244042Srmacklem			nfscl_start_renewthread(clp);
1595244042Srmacklem			nfscl_clientrelease(clp);
1596244042Srmacklem		}
1597191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1598191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1599191783Srmacklem
1600222233Srmacklem		/* Mark if the mount point supports NFSv4 ACLs. */
1601222233Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0 && nfsrv_useacl != 0 &&
1602222233Srmacklem		    ret == 0 &&
1603222233Srmacklem		    NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL)) {
1604222233Srmacklem			MNT_ILOCK(mp);
1605222233Srmacklem			mp->mnt_flag |= MNT_NFS4ACLS;
1606222233Srmacklem			MNT_IUNLOCK(mp);
1607222233Srmacklem		}
1608222233Srmacklem
1609191783Srmacklem		/*
1610191783Srmacklem		 * Lose the lock but keep the ref.
1611191783Srmacklem		 */
1612224082Szack		NFSVOPUNLOCK(*vpp, 0);
1613191783Srmacklem		return (0);
1614191783Srmacklem	}
1615191783Srmacklem	error = EIO;
1616191783Srmacklem
1617191783Srmacklembad:
1618244042Srmacklem	if (clp != NULL)
1619244042Srmacklem		nfscl_clientrelease(clp);
1620191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1621191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1622253049Srmacklem	if (nmp->nm_sockreq.nr_auth != NULL)
1623253049Srmacklem		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
1624191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1625191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1626244042Srmacklem	if (nmp->nm_clp != NULL) {
1627244042Srmacklem		NFSLOCKCLSTATE();
1628244042Srmacklem		LIST_REMOVE(nmp->nm_clp, nfsc_list);
1629244042Srmacklem		NFSUNLOCKCLSTATE();
1630244042Srmacklem		free(nmp->nm_clp, M_NFSCLCLIENT);
1631244042Srmacklem	}
1632244042Srmacklem	TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
1633244042Srmacklem		nfscl_freenfsclds(dsp);
1634191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1635191783Srmacklem	FREE(nam, M_SONAME);
1636191783Srmacklem	return (error);
1637191783Srmacklem}
1638191783Srmacklem
1639191783Srmacklem/*
1640191783Srmacklem * unmount system call
1641191783Srmacklem */
1642191783Srmacklemstatic int
1643191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1644191783Srmacklem{
1645191990Sattilio	struct thread *td;
1646191783Srmacklem	struct nfsmount *nmp;
1647249630Srmacklem	int error, flags = 0, i, trycnt = 0;
1648244042Srmacklem	struct nfsclds *dsp, *tdsp;
1649191783Srmacklem
1650191990Sattilio	td = curthread;
1651191990Sattilio
1652191783Srmacklem	if (mntflags & MNT_FORCE)
1653191783Srmacklem		flags |= FORCECLOSE;
1654191783Srmacklem	nmp = VFSTONFS(mp);
1655191783Srmacklem	/*
1656191783Srmacklem	 * Goes something like this..
1657191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1658191783Srmacklem	 * - Close the socket
1659191783Srmacklem	 * - Free up the data structures
1660191783Srmacklem	 */
1661191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1662191783Srmacklem	if (mntflags & MNT_FORCE) {
1663191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1664191783Srmacklem		if (error)
1665191783Srmacklem			goto out;
1666191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1667191783Srmacklem		nfscl_umount(nmp, td);
1668191783Srmacklem	}
1669191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1670191783Srmacklem	do {
1671191783Srmacklem		error = vflush(mp, 1, flags, td);
1672191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1673207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1674191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1675191783Srmacklem	if (error)
1676191783Srmacklem		goto out;
1677191783Srmacklem
1678191783Srmacklem	/*
1679191783Srmacklem	 * We are now committed to the unmount.
1680191783Srmacklem	 */
1681191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1682191783Srmacklem		nfscl_umount(nmp, td);
1683249630Srmacklem	/* Make sure no nfsiods are assigned to this mount. */
1684249630Srmacklem	mtx_lock(&ncl_iod_mutex);
1685249630Srmacklem	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
1686249630Srmacklem		if (ncl_iodmount[i] == nmp) {
1687249630Srmacklem			ncl_iodwant[i] = NFSIOD_AVAILABLE;
1688249630Srmacklem			ncl_iodmount[i] = NULL;
1689249630Srmacklem		}
1690249630Srmacklem	mtx_unlock(&ncl_iod_mutex);
1691191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1692191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1693191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1694253049Srmacklem	if (nmp->nm_sockreq.nr_auth != NULL)
1695253049Srmacklem		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
1696191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1697191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1698244042Srmacklem	TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
1699244042Srmacklem		nfscl_freenfsclds(dsp);
1700191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1701191783Srmacklemout:
1702191783Srmacklem	return (error);
1703191783Srmacklem}
1704191783Srmacklem
1705191783Srmacklem/*
1706191783Srmacklem * Return root of a filesystem
1707191783Srmacklem */
1708191783Srmacklemstatic int
1709191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1710191783Srmacklem{
1711191783Srmacklem	struct vnode *vp;
1712191783Srmacklem	struct nfsmount *nmp;
1713191783Srmacklem	struct nfsnode *np;
1714191783Srmacklem	int error;
1715191783Srmacklem
1716191783Srmacklem	nmp = VFSTONFS(mp);
1717220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1718191783Srmacklem	if (error)
1719191783Srmacklem		return error;
1720191783Srmacklem	vp = NFSTOV(np);
1721191783Srmacklem	/*
1722191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1723191783Srmacklem	 */
1724191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1725191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1726191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1727191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1728191783Srmacklem	} else
1729191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1730191783Srmacklem	if (vp->v_type == VNON)
1731191783Srmacklem	    vp->v_type = VDIR;
1732191783Srmacklem	vp->v_vflag |= VV_ROOT;
1733191783Srmacklem	*vpp = vp;
1734191783Srmacklem	return (0);
1735191783Srmacklem}
1736191783Srmacklem
1737191783Srmacklem/*
1738191783Srmacklem * Flush out the buffer cache
1739191783Srmacklem */
1740191783Srmacklem/* ARGSUSED */
1741191783Srmacklemstatic int
1742191990Sattilionfs_sync(struct mount *mp, int waitfor)
1743191783Srmacklem{
1744191783Srmacklem	struct vnode *vp, *mvp;
1745191990Sattilio	struct thread *td;
1746191783Srmacklem	int error, allerror = 0;
1747191783Srmacklem
1748191990Sattilio	td = curthread;
1749191990Sattilio
1750222329Srmacklem	MNT_ILOCK(mp);
1751191783Srmacklem	/*
1752222329Srmacklem	 * If a forced dismount is in progress, return from here so that
1753222329Srmacklem	 * the umount(2) syscall doesn't get stuck in VFS_SYNC() before
1754222329Srmacklem	 * calling VFS_UNMOUNT().
1755222329Srmacklem	 */
1756222329Srmacklem	if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1757222329Srmacklem		MNT_IUNLOCK(mp);
1758222329Srmacklem		return (EBADF);
1759222329Srmacklem	}
1760234386Smckusick	MNT_IUNLOCK(mp);
1761222329Srmacklem
1762222329Srmacklem	/*
1763191783Srmacklem	 * Force stale buffer cache information to be flushed.
1764191783Srmacklem	 */
1765191783Srmacklemloop:
1766234386Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
1767191783Srmacklem		/* XXX Racy bv_cnt check. */
1768224083Szack		if (NFSVOPISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1769191783Srmacklem		    waitfor == MNT_LAZY) {
1770191783Srmacklem			VI_UNLOCK(vp);
1771191783Srmacklem			continue;
1772191783Srmacklem		}
1773191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1774234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
1775191783Srmacklem			goto loop;
1776191783Srmacklem		}
1777191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1778191783Srmacklem		if (error)
1779191783Srmacklem			allerror = error;
1780224082Szack		NFSVOPUNLOCK(vp, 0);
1781191783Srmacklem		vrele(vp);
1782191783Srmacklem	}
1783191783Srmacklem	return (allerror);
1784191783Srmacklem}
1785191783Srmacklem
1786191783Srmacklemstatic int
1787191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1788191783Srmacklem{
1789191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1790191783Srmacklem	struct vfsquery vq;
1791191783Srmacklem	int error;
1792191783Srmacklem
1793191783Srmacklem	bzero(&vq, sizeof(vq));
1794191783Srmacklem	switch (op) {
1795191783Srmacklem#if 0
1796191783Srmacklem	case VFS_CTL_NOLOCKS:
1797191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1798191783Srmacklem 		if (req->oldptr != NULL) {
1799191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1800191783Srmacklem 			if (error)
1801191783Srmacklem 				return (error);
1802191783Srmacklem 		}
1803191783Srmacklem 		if (req->newptr != NULL) {
1804191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1805191783Srmacklem 			if (error)
1806191783Srmacklem 				return (error);
1807191783Srmacklem			if (val)
1808191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1809191783Srmacklem			else
1810191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1811191783Srmacklem 		}
1812191783Srmacklem		break;
1813191783Srmacklem#endif
1814191783Srmacklem	case VFS_CTL_QUERY:
1815191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1816191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1817191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1818191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1819191783Srmacklem#if 0
1820191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1821191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1822191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1823191783Srmacklem#endif
1824191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1825191783Srmacklem		break;
1826191783Srmacklem 	case VFS_CTL_TIMEO:
1827191783Srmacklem 		if (req->oldptr != NULL) {
1828191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1829191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1830191783Srmacklem 			if (error)
1831191783Srmacklem 				return (error);
1832191783Srmacklem 		}
1833191783Srmacklem 		if (req->newptr != NULL) {
1834191783Srmacklem			error = vfs_suser(mp, req->td);
1835191783Srmacklem			if (error)
1836191783Srmacklem				return (error);
1837191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1838191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1839191783Srmacklem 			if (error)
1840191783Srmacklem 				return (error);
1841191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1842191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1843191783Srmacklem 		}
1844191783Srmacklem		break;
1845191783Srmacklem	default:
1846191783Srmacklem		return (ENOTSUP);
1847191783Srmacklem	}
1848191783Srmacklem	return (0);
1849191783Srmacklem}
1850191783Srmacklem
1851214048Srmacklem/*
1852255136Srmacklem * Purge any RPCs in progress, so that they will all return errors.
1853255136Srmacklem * This allows dounmount() to continue as far as VFS_UNMOUNT() for a
1854255136Srmacklem * forced dismount.
1855255136Srmacklem */
1856255136Srmacklemstatic void
1857255136Srmacklemnfs_purge(struct mount *mp)
1858255136Srmacklem{
1859255136Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1860255136Srmacklem
1861255136Srmacklem	newnfs_nmcancelreqs(nmp);
1862255136Srmacklem}
1863255136Srmacklem
1864255136Srmacklem/*
1865214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1866214048Srmacklem */
1867214048Srmacklemstatic void
1868214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1869216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1870216931Srmacklem    struct timeval *timeop)
1871214048Srmacklem{
1872214048Srmacklem	struct nfsmount *nmp;
1873214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1874214048Srmacklem
1875214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1876214048Srmacklem	if (fhlenp != NULL)
1877214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1878214048Srmacklem	if (fhp != NULL)
1879214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1880214048Srmacklem	if (sp != NULL)
1881214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1882214048Srmacklem	if (is_v3p != NULL)
1883214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1884214048Srmacklem	if (sizep != NULL)
1885214048Srmacklem		*sizep = np->n_size;
1886216931Srmacklem	if (timeop != NULL) {
1887216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1888216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1889216931Srmacklem	}
1890214048Srmacklem}
1891214048Srmacklem
1892243782Srmacklem/*
1893243782Srmacklem * This function prints out an option name, based on the conditional
1894243782Srmacklem * argument.
1895243782Srmacklem */
1896243782Srmacklemstatic __inline void nfscl_printopt(struct nfsmount *nmp, int testval,
1897243782Srmacklem    char *opt, char **buf, size_t *blen)
1898243782Srmacklem{
1899243782Srmacklem	int len;
1900243782Srmacklem
1901243782Srmacklem	if (testval != 0 && *blen > strlen(opt)) {
1902243782Srmacklem		len = snprintf(*buf, *blen, "%s", opt);
1903243782Srmacklem		if (len != strlen(opt))
1904243782Srmacklem			printf("EEK!!\n");
1905243782Srmacklem		*buf += len;
1906243782Srmacklem		*blen -= len;
1907243782Srmacklem	}
1908243782Srmacklem}
1909243782Srmacklem
1910243782Srmacklem/*
1911243782Srmacklem * This function printf out an options integer value.
1912243782Srmacklem */
1913243782Srmacklemstatic __inline void nfscl_printoptval(struct nfsmount *nmp, int optval,
1914243782Srmacklem    char *opt, char **buf, size_t *blen)
1915243782Srmacklem{
1916243782Srmacklem	int len;
1917243782Srmacklem
1918243782Srmacklem	if (*blen > strlen(opt) + 1) {
1919243782Srmacklem		/* Could result in truncated output string. */
1920243782Srmacklem		len = snprintf(*buf, *blen, "%s=%d", opt, optval);
1921243782Srmacklem		if (len < *blen) {
1922243782Srmacklem			*buf += len;
1923243782Srmacklem			*blen -= len;
1924243782Srmacklem		}
1925243782Srmacklem	}
1926243782Srmacklem}
1927243782Srmacklem
1928243782Srmacklem/*
1929243782Srmacklem * Load the option flags and values into the buffer.
1930243782Srmacklem */
1931243782Srmacklemvoid nfscl_retopts(struct nfsmount *nmp, char *buffer, size_t buflen)
1932243782Srmacklem{
1933243782Srmacklem	char *buf;
1934243782Srmacklem	size_t blen;
1935243782Srmacklem
1936243782Srmacklem	buf = buffer;
1937243782Srmacklem	blen = buflen;
1938243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV4) != 0, "nfsv4", &buf,
1939243782Srmacklem	    &blen);
1940244056Srmacklem	if ((nmp->nm_flag & NFSMNT_NFSV4) != 0) {
1941244056Srmacklem		nfscl_printoptval(nmp, nmp->nm_minorvers, ",minorversion", &buf,
1942244056Srmacklem		    &blen);
1943244056Srmacklem		nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_PNFS) != 0, ",pnfs",
1944244056Srmacklem		    &buf, &blen);
1945244056Srmacklem	}
1946243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV3) != 0, "nfsv3", &buf,
1947243782Srmacklem	    &blen);
1948243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0,
1949243782Srmacklem	    "nfsv2", &buf, &blen);
1950243782Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype == SOCK_STREAM, ",tcp", &buf, &blen);
1951243782Srmacklem	nfscl_printopt(nmp, nmp->nm_sotype != SOCK_STREAM, ",udp", &buf, &blen);
1952243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RESVPORT) != 0, ",resvport",
1953243782Srmacklem	    &buf, &blen);
1954243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCONN) != 0, ",noconn",
1955243782Srmacklem	    &buf, &blen);
1956243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) == 0, ",hard", &buf,
1957243782Srmacklem	    &blen);
1958243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) != 0, ",soft", &buf,
1959243782Srmacklem	    &blen);
1960243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_INT) != 0, ",intr", &buf,
1961243782Srmacklem	    &blen);
1962243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) == 0, ",cto", &buf,
1963243782Srmacklem	    &blen);
1964243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) != 0, ",nocto", &buf,
1965243782Srmacklem	    &blen);
1966259084Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NONCONTIGWR) != 0,
1967259084Srmacklem	    ",noncontigwr", &buf, &blen);
1968243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1969243782Srmacklem	    0, ",lockd", &buf, &blen);
1970243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) ==
1971243782Srmacklem	    NFSMNT_NOLOCKD, ",nolockd", &buf, &blen);
1972243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RDIRPLUS) != 0, ",rdirplus",
1973243782Srmacklem	    &buf, &blen);
1974243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_KERB) == 0, ",sec=sys",
1975243782Srmacklem	    &buf, &blen);
1976243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1977243782Srmacklem	    NFSMNT_PRIVACY)) == NFSMNT_KERB, ",sec=krb5", &buf, &blen);
1978243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1979243782Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_INTEGRITY), ",sec=krb5i",
1980243782Srmacklem	    &buf, &blen);
1981243782Srmacklem	nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY |
1982243782Srmacklem	    NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_PRIVACY), ",sec=krb5p",
1983243782Srmacklem	    &buf, &blen);
1984243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmin, ",acdirmin", &buf, &blen);
1985243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acdirmax, ",acdirmax", &buf, &blen);
1986243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmin, ",acregmin", &buf, &blen);
1987243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_acregmax, ",acregmax", &buf, &blen);
1988243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_nametimeo, ",nametimeo", &buf, &blen);
1989243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_negnametimeo, ",negnametimeo", &buf,
1990243782Srmacklem	    &blen);
1991243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_rsize, ",rsize", &buf, &blen);
1992243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_wsize, ",wsize", &buf, &blen);
1993243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_readdirsize, ",readdirsize", &buf,
1994243782Srmacklem	    &blen);
1995243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_readahead, ",readahead", &buf, &blen);
1996243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_wcommitsize, ",wcommitsize", &buf,
1997243782Srmacklem	    &blen);
1998243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_timeo, ",timeout", &buf, &blen);
1999243782Srmacklem	nfscl_printoptval(nmp, nmp->nm_retry, ",retrans", &buf, &blen);
2000243782Srmacklem}
2001243782Srmacklem
2002