nfs_clvfsops.c revision 221537
1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993, 1995
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem *	from nfs_vfsops.c	8.12 (Berkeley) 5/20/95
33191783Srmacklem */
34191783Srmacklem
35191783Srmacklem#include <sys/cdefs.h>
36191783Srmacklem__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clvfsops.c 221537 2011-05-06 17:51:00Z rmacklem $");
37191783Srmacklem
38191783Srmacklem
39191783Srmacklem#include "opt_bootp.h"
40191783Srmacklem#include "opt_nfsroot.h"
41191783Srmacklem
42191783Srmacklem#include <sys/param.h>
43191783Srmacklem#include <sys/systm.h>
44191783Srmacklem#include <sys/kernel.h>
45191783Srmacklem#include <sys/bio.h>
46191783Srmacklem#include <sys/buf.h>
47191783Srmacklem#include <sys/clock.h>
48193066Sjamie#include <sys/jail.h>
49220739Srmacklem#include <sys/limits.h>
50191783Srmacklem#include <sys/lock.h>
51191783Srmacklem#include <sys/malloc.h>
52191783Srmacklem#include <sys/mbuf.h>
53191783Srmacklem#include <sys/module.h>
54191783Srmacklem#include <sys/mount.h>
55191783Srmacklem#include <sys/proc.h>
56191783Srmacklem#include <sys/socket.h>
57191783Srmacklem#include <sys/socketvar.h>
58191783Srmacklem#include <sys/sockio.h>
59191783Srmacklem#include <sys/sysctl.h>
60191783Srmacklem#include <sys/vnode.h>
61191783Srmacklem#include <sys/signalvar.h>
62191783Srmacklem
63191783Srmacklem#include <vm/vm.h>
64191783Srmacklem#include <vm/vm_extern.h>
65191783Srmacklem#include <vm/uma.h>
66191783Srmacklem
67191783Srmacklem#include <net/if.h>
68191783Srmacklem#include <net/route.h>
69191783Srmacklem#include <netinet/in.h>
70191783Srmacklem
71191783Srmacklem#include <fs/nfs/nfsport.h>
72191783Srmacklem#include <fs/nfsclient/nfsnode.h>
73191783Srmacklem#include <fs/nfsclient/nfsmount.h>
74191783Srmacklem#include <fs/nfsclient/nfs.h>
75221040Srmacklem#include <nfs/nfsdiskless.h>
76191783Srmacklem
77219028SnetchildFEATURE(nfscl, "NFSv4 client");
78219028Snetchild
79191783Srmacklemextern int nfscl_ticks;
80191783Srmacklemextern struct timeval nfsboottime;
81191783Srmacklemextern struct nfsstats	newnfsstats;
82191783Srmacklem
83191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
84191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
85191783Srmacklem
86191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
87191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW,
88191783Srmacklem	&newnfsstats, nfsstats, "S,nfsstats");
89191783Srmacklemstatic int nfs_ip_paranoia = 1;
90191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW,
91191783Srmacklem    &nfs_ip_paranoia, 0, "");
92191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
93191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_INITIAL_DELAY,
94191783Srmacklem        downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
95191783Srmacklem/* how long between console messages "nfs server foo not responding" */
96191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
97191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY,
98191783Srmacklem        downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
99191783Srmacklem
100221040Srmacklemstatic int	nfs_mountroot(struct mount *);
101192585Srmacklemstatic void	nfs_sec_name(char *, int *);
102191783Srmacklemstatic void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
103214048Srmacklem		    struct nfs_args *argp, const char *, struct ucred *,
104214048Srmacklem		    struct thread *);
105191783Srmacklemstatic int	mountnfs(struct nfs_args *, struct mount *,
106221014Srmacklem		    struct sockaddr *, char *, u_char *, int, u_char *, int,
107221014Srmacklem		    u_char *, int, struct vnode **, struct ucred *,
108221014Srmacklem		    struct thread *, int);
109214053Srmacklemstatic void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
110216931Srmacklem		    struct sockaddr_storage *, int *, off_t *,
111216931Srmacklem		    struct timeval *);
112191783Srmacklemstatic vfs_mount_t nfs_mount;
113191783Srmacklemstatic vfs_cmount_t nfs_cmount;
114191783Srmacklemstatic vfs_unmount_t nfs_unmount;
115191783Srmacklemstatic vfs_root_t nfs_root;
116191783Srmacklemstatic vfs_statfs_t nfs_statfs;
117191783Srmacklemstatic vfs_sync_t nfs_sync;
118191783Srmacklemstatic vfs_sysctl_t nfs_sysctl;
119191783Srmacklem
120191783Srmacklem/*
121191783Srmacklem * nfs vfs operations.
122191783Srmacklem */
123191783Srmacklemstatic struct vfsops nfs_vfsops = {
124191783Srmacklem	.vfs_init =		ncl_init,
125191783Srmacklem	.vfs_mount =		nfs_mount,
126191783Srmacklem	.vfs_cmount =		nfs_cmount,
127191783Srmacklem	.vfs_root =		nfs_root,
128191783Srmacklem	.vfs_statfs =		nfs_statfs,
129191783Srmacklem	.vfs_sync =		nfs_sync,
130191783Srmacklem	.vfs_uninit =		ncl_uninit,
131191783Srmacklem	.vfs_unmount =		nfs_unmount,
132191783Srmacklem	.vfs_sysctl =		nfs_sysctl,
133191783Srmacklem};
134221124SrmacklemVFS_SET(nfs_vfsops, nfs, VFCF_NETWORK);
135191783Srmacklem
136191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
137221139SrmacklemMODULE_VERSION(nfs, 1);
138221139SrmacklemMODULE_DEPEND(nfs, nfscommon, 1, 1, 1);
139221139SrmacklemMODULE_DEPEND(nfs, krpc, 1, 1, 1);
140221139SrmacklemMODULE_DEPEND(nfs, nfssvc, 1, 1, 1);
141221139SrmacklemMODULE_DEPEND(nfs, nfslock, 1, 1, 1);
142191783Srmacklem
143191783Srmacklem/*
144221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it
145221066Srmacklem * can be shared by both NFS clients. It is declared here so that it
146221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it
147221066Srmacklem * isn't used in that case.
148191783Srmacklem */
149221066Srmacklem#if !defined(NFS_ROOT) && !defined(NFSCLIENT)
150221066Srmacklemstruct nfs_diskless	nfs_diskless = { { { 0 } } };
151221066Srmacklemstruct nfsv3_diskless	nfsv3_diskless = { { { 0 } } };
152221066Srmacklemint			nfs_diskless_valid = 0;
153221066Srmacklem#endif
154221066Srmacklem
155191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
156221040Srmacklem    &nfs_diskless_valid, 0,
157192145Srmacklem    "Has the diskless struct been filled correctly");
158191783Srmacklem
159191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
160221040Srmacklem    nfsv3_diskless.root_hostnam, 0, "Path to nfs root");
161191783Srmacklem
162191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
163221040Srmacklem    &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr),
164192145Srmacklem    "%Ssockaddr_in", "Diskless root nfs address");
165191783Srmacklem
166191783Srmacklem
167191783Srmacklemvoid		newnfsargs_ntoh(struct nfs_args *);
168191783Srmacklemstatic int	nfs_mountdiskless(char *,
169191783Srmacklem		    struct sockaddr_in *, struct nfs_args *,
170191783Srmacklem		    struct thread *, struct vnode **, struct mount *);
171191783Srmacklemstatic void	nfs_convert_diskless(void);
172191783Srmacklemstatic void	nfs_convert_oargs(struct nfs_args *args,
173191783Srmacklem		    struct onfs_args *oargs);
174191783Srmacklem
175191783Srmacklemint
176191783Srmacklemnewnfs_iosize(struct nfsmount *nmp)
177191783Srmacklem{
178191783Srmacklem	int iosize, maxio;
179191783Srmacklem
180191783Srmacklem	/* First, set the upper limit for iosize */
181191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
182191783Srmacklem		maxio = NFS_MAXBSIZE;
183191783Srmacklem	} else if (nmp->nm_flag & NFSMNT_NFSV3) {
184191783Srmacklem		if (nmp->nm_sotype == SOCK_DGRAM)
185191783Srmacklem			maxio = NFS_MAXDGRAMDATA;
186191783Srmacklem		else
187191783Srmacklem			maxio = NFS_MAXBSIZE;
188191783Srmacklem	} else {
189191783Srmacklem		maxio = NFS_V2MAXDATA;
190191783Srmacklem	}
191191783Srmacklem	if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0)
192191783Srmacklem		nmp->nm_rsize = maxio;
193191783Srmacklem	if (nmp->nm_rsize > MAXBSIZE)
194191783Srmacklem		nmp->nm_rsize = MAXBSIZE;
195191783Srmacklem	if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0)
196191783Srmacklem		nmp->nm_readdirsize = maxio;
197191783Srmacklem	if (nmp->nm_readdirsize > nmp->nm_rsize)
198191783Srmacklem		nmp->nm_readdirsize = nmp->nm_rsize;
199191783Srmacklem	if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0)
200191783Srmacklem		nmp->nm_wsize = maxio;
201191783Srmacklem	if (nmp->nm_wsize > MAXBSIZE)
202191783Srmacklem		nmp->nm_wsize = MAXBSIZE;
203191783Srmacklem
204191783Srmacklem	/*
205191783Srmacklem	 * Calculate the size used for io buffers.  Use the larger
206191783Srmacklem	 * of the two sizes to minimise nfs requests but make sure
207191783Srmacklem	 * that it is at least one VM page to avoid wasting buffer
208191783Srmacklem	 * space.
209191783Srmacklem	 */
210191783Srmacklem	iosize = imax(nmp->nm_rsize, nmp->nm_wsize);
211191783Srmacklem	iosize = imax(iosize, PAGE_SIZE);
212191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = iosize;
213191783Srmacklem	return (iosize);
214191783Srmacklem}
215191783Srmacklem
216191783Srmacklemstatic void
217191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs)
218191783Srmacklem{
219191783Srmacklem
220191783Srmacklem	args->version = NFS_ARGSVERSION;
221191783Srmacklem	args->addr = oargs->addr;
222191783Srmacklem	args->addrlen = oargs->addrlen;
223191783Srmacklem	args->sotype = oargs->sotype;
224191783Srmacklem	args->proto = oargs->proto;
225191783Srmacklem	args->fh = oargs->fh;
226191783Srmacklem	args->fhsize = oargs->fhsize;
227191783Srmacklem	args->flags = oargs->flags;
228191783Srmacklem	args->wsize = oargs->wsize;
229191783Srmacklem	args->rsize = oargs->rsize;
230191783Srmacklem	args->readdirsize = oargs->readdirsize;
231191783Srmacklem	args->timeo = oargs->timeo;
232191783Srmacklem	args->retrans = oargs->retrans;
233191783Srmacklem	args->readahead = oargs->readahead;
234191783Srmacklem	args->hostname = oargs->hostname;
235191783Srmacklem}
236191783Srmacklem
237191783Srmacklemstatic void
238191783Srmacklemnfs_convert_diskless(void)
239191783Srmacklem{
240191783Srmacklem
241221040Srmacklem	bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif,
242221040Srmacklem		sizeof(struct ifaliasreq));
243221040Srmacklem	bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway,
244221040Srmacklem		sizeof(struct sockaddr_in));
245221040Srmacklem	nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args);
246221040Srmacklem	if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) {
247221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_MYFH;
248221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH);
249191783Srmacklem	} else {
250221040Srmacklem		nfsv3_diskless.root_fhsize = NFSX_V2FH;
251221040Srmacklem		bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH);
252191783Srmacklem	}
253221040Srmacklem	bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr,
254221040Srmacklem		sizeof(struct sockaddr_in));
255221040Srmacklem	bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN);
256221040Srmacklem	nfsv3_diskless.root_time = nfs_diskless.root_time;
257221040Srmacklem	bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam,
258221040Srmacklem		MAXHOSTNAMELEN);
259221040Srmacklem	nfs_diskless_valid = 3;
260191783Srmacklem}
261191783Srmacklem
262191783Srmacklem/*
263191783Srmacklem * nfs statfs call
264191783Srmacklem */
265191783Srmacklemstatic int
266191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp)
267191783Srmacklem{
268191783Srmacklem	struct vnode *vp;
269191990Sattilio	struct thread *td;
270191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
271191783Srmacklem	struct nfsvattr nfsva;
272191783Srmacklem	struct nfsfsinfo fs;
273191783Srmacklem	struct nfsstatfs sb;
274191783Srmacklem	int error = 0, attrflag, gotfsinfo = 0, ret;
275191783Srmacklem	struct nfsnode *np;
276191783Srmacklem
277191990Sattilio	td = curthread;
278191990Sattilio
279191783Srmacklem	error = vfs_busy(mp, MBF_NOWAIT);
280191783Srmacklem	if (error)
281191783Srmacklem		return (error);
282220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
283191783Srmacklem	if (error) {
284191783Srmacklem		vfs_unbusy(mp);
285191783Srmacklem		return (error);
286191783Srmacklem	}
287191783Srmacklem	vp = NFSTOV(np);
288191783Srmacklem	mtx_lock(&nmp->nm_mtx);
289191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
290191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
291191783Srmacklem		error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva,
292191783Srmacklem		    &attrflag, NULL);
293191783Srmacklem		if (!error)
294191783Srmacklem			gotfsinfo = 1;
295191783Srmacklem	} else
296191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
297191783Srmacklem	if (!error)
298191783Srmacklem		error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
299191783Srmacklem		    &attrflag, NULL);
300191783Srmacklem	if (attrflag == 0) {
301191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
302191783Srmacklem		    td->td_ucred, td, &nfsva, NULL);
303191783Srmacklem		if (ret) {
304191783Srmacklem			/*
305191783Srmacklem			 * Just set default values to get things going.
306191783Srmacklem			 */
307191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
308191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
309191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
310191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
311191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
312191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
313191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
314191783Srmacklem			nfsva.na_vattr.va_gen = 1;
315191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
316191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
317191783Srmacklem		}
318191783Srmacklem	}
319191783Srmacklem	(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1);
320191783Srmacklem	if (!error) {
321191783Srmacklem	    mtx_lock(&nmp->nm_mtx);
322191783Srmacklem	    if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
323191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
324191783Srmacklem	    nfscl_loadsbinfo(nmp, &sb, sbp);
325191783Srmacklem	    sbp->f_iosize = newnfs_iosize(nmp);
326191783Srmacklem	    mtx_unlock(&nmp->nm_mtx);
327191783Srmacklem	    if (sbp != &mp->mnt_stat) {
328191783Srmacklem		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
329191783Srmacklem		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
330191783Srmacklem	    }
331191783Srmacklem	    strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
332191783Srmacklem	} else if (NFS_ISV4(vp)) {
333191783Srmacklem		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
334191783Srmacklem	}
335191783Srmacklem	vput(vp);
336191783Srmacklem	vfs_unbusy(mp);
337191783Srmacklem	return (error);
338191783Srmacklem}
339191783Srmacklem
340191783Srmacklem/*
341191783Srmacklem * nfs version 3 fsinfo rpc call
342191783Srmacklem */
343191783Srmacklemint
344191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
345191783Srmacklem    struct thread *td)
346191783Srmacklem{
347191783Srmacklem	struct nfsfsinfo fs;
348191783Srmacklem	struct nfsvattr nfsva;
349191783Srmacklem	int error, attrflag;
350191783Srmacklem
351191783Srmacklem	error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL);
352191783Srmacklem	if (!error) {
353191783Srmacklem		if (attrflag)
354191783Srmacklem			(void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0,
355191783Srmacklem			    1);
356191783Srmacklem		mtx_lock(&nmp->nm_mtx);
357191783Srmacklem		nfscl_loadfsinfo(nmp, &fs);
358191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
359191783Srmacklem	}
360191783Srmacklem	return (error);
361191783Srmacklem}
362191783Srmacklem
363191783Srmacklem/*
364191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the
365221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary
366191783Srmacklem * bootstrap.
367191783Srmacklem * It goes something like this:
368191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system
369191783Srmacklem *   can talk to the server
370221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as
371191783Srmacklem *   a default gateway.
372191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest.
373191783Srmacklem *
374191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless
375191783Srmacklem * structure, as well as other global NFS client variables here, as
376192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS
377191783Srmacklem * client activity occurs.
378191783Srmacklem */
379221040Srmacklemstatic int
380221040Srmacklemnfs_mountroot(struct mount *mp)
381191783Srmacklem{
382192145Srmacklem	struct thread *td = curthread;
383221040Srmacklem	struct nfsv3_diskless *nd = &nfsv3_diskless;
384191783Srmacklem	struct socket *so;
385191783Srmacklem	struct vnode *vp;
386191783Srmacklem	struct ifreq ir;
387193066Sjamie	int error;
388191783Srmacklem	u_long l;
389191783Srmacklem	char buf[128];
390191783Srmacklem	char *cp;
391191783Srmacklem
392191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP)
393192145Srmacklem	bootpc_init();		/* use bootp to get nfs_diskless filled in */
394191783Srmacklem#elif defined(NFS_ROOT)
395191783Srmacklem	nfs_setup_diskless();
396191783Srmacklem#endif
397191783Srmacklem
398221040Srmacklem	if (nfs_diskless_valid == 0)
399191783Srmacklem		return (-1);
400221040Srmacklem	if (nfs_diskless_valid == 1)
401191783Srmacklem		nfs_convert_diskless();
402191783Srmacklem
403191783Srmacklem	/*
404191783Srmacklem	 * XXX splnet, so networks will receive...
405191783Srmacklem	 */
406191783Srmacklem	splnet();
407191783Srmacklem
408191783Srmacklem	/*
409191783Srmacklem	 * Do enough of ifconfig(8) so that the critical net interface can
410191783Srmacklem	 * talk to the server.
411191783Srmacklem	 */
412191783Srmacklem	error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0,
413191783Srmacklem	    td->td_ucred, td);
414191783Srmacklem	if (error)
415192145Srmacklem		panic("nfs_mountroot: socreate(%04x): %d",
416191783Srmacklem			nd->myif.ifra_addr.sa_family, error);
417191783Srmacklem
418191783Srmacklem#if 0 /* XXX Bad idea */
419191783Srmacklem	/*
420191783Srmacklem	 * We might not have been told the right interface, so we pass
421191783Srmacklem	 * over the first ten interfaces of the same kind, until we get
422191783Srmacklem	 * one of them configured.
423191783Srmacklem	 */
424191783Srmacklem
425191783Srmacklem	for (i = strlen(nd->myif.ifra_name) - 1;
426191783Srmacklem		nd->myif.ifra_name[i] >= '0' &&
427191783Srmacklem		nd->myif.ifra_name[i] <= '9';
428191783Srmacklem		nd->myif.ifra_name[i] ++) {
429191783Srmacklem		error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
430191783Srmacklem		if(!error)
431191783Srmacklem			break;
432191783Srmacklem	}
433191783Srmacklem#endif
434191783Srmacklem	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td);
435191783Srmacklem	if (error)
436192145Srmacklem		panic("nfs_mountroot: SIOCAIFADDR: %d", error);
437191783Srmacklem	if ((cp = getenv("boot.netif.mtu")) != NULL) {
438191783Srmacklem		ir.ifr_mtu = strtol(cp, NULL, 10);
439191783Srmacklem		bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ);
440191783Srmacklem		freeenv(cp);
441191783Srmacklem		error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td);
442191783Srmacklem		if (error)
443192145Srmacklem			printf("nfs_mountroot: SIOCSIFMTU: %d", error);
444191783Srmacklem	}
445191783Srmacklem	soclose(so);
446191783Srmacklem
447191783Srmacklem	/*
448191783Srmacklem	 * If the gateway field is filled in, set it as the default route.
449191783Srmacklem	 * Note that pxeboot will set a default route of 0 if the route
450191783Srmacklem	 * is not set by the DHCP server.  Check also for a value of 0
451191783Srmacklem	 * to avoid panicking inappropriately in that situation.
452191783Srmacklem	 */
453191783Srmacklem	if (nd->mygateway.sin_len != 0 &&
454191783Srmacklem	    nd->mygateway.sin_addr.s_addr != 0) {
455191783Srmacklem		struct sockaddr_in mask, sin;
456191783Srmacklem
457191783Srmacklem		bzero((caddr_t)&mask, sizeof(mask));
458191783Srmacklem		sin = mask;
459191783Srmacklem		sin.sin_family = AF_INET;
460191783Srmacklem		sin.sin_len = sizeof(sin);
461192145Srmacklem                /* XXX MRT use table 0 for this sort of thing */
462218757Sbz		CURVNET_SET(TD_TO_VNET(td));
463191783Srmacklem		error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
464191783Srmacklem		    (struct sockaddr *)&nd->mygateway,
465191783Srmacklem		    (struct sockaddr *)&mask,
466191783Srmacklem		    RTF_UP | RTF_GATEWAY, NULL);
467218757Sbz		CURVNET_RESTORE();
468191783Srmacklem		if (error)
469192145Srmacklem			panic("nfs_mountroot: RTM_ADD: %d", error);
470191783Srmacklem	}
471191783Srmacklem
472191783Srmacklem	/*
473191783Srmacklem	 * Create the rootfs mount point.
474191783Srmacklem	 */
475191783Srmacklem	nd->root_args.fh = nd->root_fh;
476191783Srmacklem	nd->root_args.fhsize = nd->root_fhsize;
477191783Srmacklem	l = ntohl(nd->root_saddr.sin_addr.s_addr);
478191783Srmacklem	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s",
479191783Srmacklem		(l >> 24) & 0xff, (l >> 16) & 0xff,
480191783Srmacklem		(l >>  8) & 0xff, (l >>  0) & 0xff, nd->root_hostnam);
481191783Srmacklem	printf("NFS ROOT: %s\n", buf);
482192145Srmacklem	nd->root_args.hostname = buf;
483191783Srmacklem	if ((error = nfs_mountdiskless(buf,
484191783Srmacklem	    &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) {
485191783Srmacklem		return (error);
486191783Srmacklem	}
487191783Srmacklem
488191783Srmacklem	/*
489191783Srmacklem	 * This is not really an nfs issue, but it is much easier to
490191783Srmacklem	 * set hostname here and then let the "/etc/rc.xxx" files
491191783Srmacklem	 * mount the right /var based upon its preset value.
492191783Srmacklem	 */
493193066Sjamie	mtx_lock(&prison0.pr_mtx);
494194118Sjamie	strlcpy(prison0.pr_hostname, nd->my_hostnam,
495194118Sjamie	    sizeof(prison0.pr_hostname));
496193066Sjamie	mtx_unlock(&prison0.pr_mtx);
497191783Srmacklem	inittodr(ntohl(nd->root_time));
498191783Srmacklem	return (0);
499191783Srmacklem}
500191783Srmacklem
501191783Srmacklem/*
502191783Srmacklem * Internal version of mount system call for diskless setup.
503191783Srmacklem */
504191783Srmacklemstatic int
505191783Srmacklemnfs_mountdiskless(char *path,
506191783Srmacklem    struct sockaddr_in *sin, struct nfs_args *args, struct thread *td,
507191783Srmacklem    struct vnode **vpp, struct mount *mp)
508191783Srmacklem{
509191783Srmacklem	struct sockaddr *nam;
510221014Srmacklem	int dirlen, error;
511221014Srmacklem	char *dirpath;
512191783Srmacklem
513221014Srmacklem	/*
514221014Srmacklem	 * Find the directory path in "path", which also has the server's
515221014Srmacklem	 * name/ip address in it.
516221014Srmacklem	 */
517221014Srmacklem	dirpath = strchr(path, ':');
518221014Srmacklem	if (dirpath != NULL)
519221014Srmacklem		dirlen = strlen(++dirpath);
520221014Srmacklem	else
521221014Srmacklem		dirlen = 0;
522191783Srmacklem	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
523221014Srmacklem	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
524221014Srmacklem	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
525192145Srmacklem		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
526191783Srmacklem		return (error);
527191783Srmacklem	}
528191783Srmacklem	return (0);
529191783Srmacklem}
530191783Srmacklem
531191783Srmacklemstatic void
532192585Srmacklemnfs_sec_name(char *sec, int *flagsp)
533192585Srmacklem{
534192585Srmacklem	if (!strcmp(sec, "krb5"))
535192585Srmacklem		*flagsp |= NFSMNT_KERB;
536192585Srmacklem	else if (!strcmp(sec, "krb5i"))
537192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY);
538192585Srmacklem	else if (!strcmp(sec, "krb5p"))
539192585Srmacklem		*flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY);
540192585Srmacklem}
541192585Srmacklem
542192585Srmacklemstatic void
543191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp,
544214048Srmacklem    const char *hostname, struct ucred *cred, struct thread *td)
545191783Srmacklem{
546191783Srmacklem	int s;
547191783Srmacklem	int adjsock;
548214048Srmacklem	char *p;
549191783Srmacklem
550191783Srmacklem	s = splnet();
551191783Srmacklem
552191783Srmacklem	/*
553191783Srmacklem	 * Set read-only flag if requested; otherwise, clear it if this is
554191783Srmacklem	 * an update.  If this is not an update, then either the read-only
555191783Srmacklem	 * flag is already clear, or this is a root mount and it was set
556191783Srmacklem	 * intentionally at some previous point.
557191783Srmacklem	 */
558191783Srmacklem	if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) {
559191783Srmacklem		MNT_ILOCK(mp);
560191783Srmacklem		mp->mnt_flag |= MNT_RDONLY;
561191783Srmacklem		MNT_IUNLOCK(mp);
562191783Srmacklem	} else if (mp->mnt_flag & MNT_UPDATE) {
563191783Srmacklem		MNT_ILOCK(mp);
564191783Srmacklem		mp->mnt_flag &= ~MNT_RDONLY;
565191783Srmacklem		MNT_IUNLOCK(mp);
566191783Srmacklem	}
567191783Srmacklem
568191783Srmacklem	/*
569191783Srmacklem	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
570191783Srmacklem	 * no sense in that context.  Also, set up appropriate retransmit
571191783Srmacklem	 * and soft timeout behavior.
572191783Srmacklem	 */
573191783Srmacklem	if (argp->sotype == SOCK_STREAM) {
574191783Srmacklem		nmp->nm_flag &= ~NFSMNT_NOCONN;
575191783Srmacklem		nmp->nm_timeo = NFS_MAXTIMEO;
576220739Srmacklem		if ((argp->flags & NFSMNT_NFSV4) != 0)
577220739Srmacklem			nmp->nm_retry = INT_MAX;
578220739Srmacklem		else
579220739Srmacklem			nmp->nm_retry = NFS_RETRANS_TCP;
580191783Srmacklem	}
581191783Srmacklem
582220739Srmacklem	/* Also clear RDIRPLUS if NFSv2, it crashes some servers */
583220739Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
584220739Srmacklem		argp->flags &= ~NFSMNT_RDIRPLUS;
585191783Srmacklem		nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
586220739Srmacklem	}
587191783Srmacklem
588220739Srmacklem	/* Clear NFSMNT_RESVPORT for NFSv4, since it is not required. */
589220739Srmacklem	if ((argp->flags & NFSMNT_NFSV4) != 0) {
590220739Srmacklem		argp->flags &= ~NFSMNT_RESVPORT;
591220739Srmacklem		nmp->nm_flag &= ~NFSMNT_RESVPORT;
592220739Srmacklem	}
593220739Srmacklem
594220739Srmacklem	/* Re-bind if rsrvd port requested and wasn't on one */
595220739Srmacklem	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
596220739Srmacklem		  && (argp->flags & NFSMNT_RESVPORT);
597191783Srmacklem	/* Also re-bind if we're switching to/from a connected UDP socket */
598220739Srmacklem	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
599191783Srmacklem		    (argp->flags & NFSMNT_NOCONN));
600191783Srmacklem
601191783Srmacklem	/* Update flags atomically.  Don't change the lock bits. */
602191783Srmacklem	nmp->nm_flag = argp->flags | nmp->nm_flag;
603191783Srmacklem	splx(s);
604191783Srmacklem
605191783Srmacklem	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
606191783Srmacklem		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
607191783Srmacklem		if (nmp->nm_timeo < NFS_MINTIMEO)
608191783Srmacklem			nmp->nm_timeo = NFS_MINTIMEO;
609191783Srmacklem		else if (nmp->nm_timeo > NFS_MAXTIMEO)
610191783Srmacklem			nmp->nm_timeo = NFS_MAXTIMEO;
611191783Srmacklem	}
612191783Srmacklem
613191783Srmacklem	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
614191783Srmacklem		nmp->nm_retry = argp->retrans;
615191783Srmacklem		if (nmp->nm_retry > NFS_MAXREXMIT)
616191783Srmacklem			nmp->nm_retry = NFS_MAXREXMIT;
617191783Srmacklem	}
618191783Srmacklem
619191783Srmacklem	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
620191783Srmacklem		nmp->nm_wsize = argp->wsize;
621191783Srmacklem		/* Round down to multiple of blocksize */
622191783Srmacklem		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
623191783Srmacklem		if (nmp->nm_wsize <= 0)
624191783Srmacklem			nmp->nm_wsize = NFS_FABLKSIZE;
625191783Srmacklem	}
626191783Srmacklem
627191783Srmacklem	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
628191783Srmacklem		nmp->nm_rsize = argp->rsize;
629191783Srmacklem		/* Round down to multiple of blocksize */
630191783Srmacklem		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
631191783Srmacklem		if (nmp->nm_rsize <= 0)
632191783Srmacklem			nmp->nm_rsize = NFS_FABLKSIZE;
633191783Srmacklem	}
634191783Srmacklem
635191783Srmacklem	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
636191783Srmacklem		nmp->nm_readdirsize = argp->readdirsize;
637191783Srmacklem	}
638191783Srmacklem
639191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
640191783Srmacklem		nmp->nm_acregmin = argp->acregmin;
641191783Srmacklem	else
642191783Srmacklem		nmp->nm_acregmin = NFS_MINATTRTIMO;
643191783Srmacklem	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
644191783Srmacklem		nmp->nm_acregmax = argp->acregmax;
645191783Srmacklem	else
646191783Srmacklem		nmp->nm_acregmax = NFS_MAXATTRTIMO;
647191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
648191783Srmacklem		nmp->nm_acdirmin = argp->acdirmin;
649191783Srmacklem	else
650191783Srmacklem		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
651191783Srmacklem	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
652191783Srmacklem		nmp->nm_acdirmax = argp->acdirmax;
653191783Srmacklem	else
654191783Srmacklem		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
655191783Srmacklem	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
656191783Srmacklem		nmp->nm_acdirmin = nmp->nm_acdirmax;
657191783Srmacklem	if (nmp->nm_acregmin > nmp->nm_acregmax)
658191783Srmacklem		nmp->nm_acregmin = nmp->nm_acregmax;
659191783Srmacklem
660191783Srmacklem	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
661191783Srmacklem		if (argp->readahead <= NFS_MAXRAHEAD)
662191783Srmacklem			nmp->nm_readahead = argp->readahead;
663191783Srmacklem		else
664191783Srmacklem			nmp->nm_readahead = NFS_MAXRAHEAD;
665191783Srmacklem	}
666191783Srmacklem	if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) {
667191783Srmacklem		if (argp->wcommitsize < nmp->nm_wsize)
668191783Srmacklem			nmp->nm_wcommitsize = nmp->nm_wsize;
669191783Srmacklem		else
670191783Srmacklem			nmp->nm_wcommitsize = argp->wcommitsize;
671191783Srmacklem	}
672191783Srmacklem
673191783Srmacklem	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
674191783Srmacklem		    (nmp->nm_soproto != argp->proto));
675191783Srmacklem
676191783Srmacklem	if (nmp->nm_client != NULL && adjsock) {
677191783Srmacklem		int haslock = 0, error = 0;
678191783Srmacklem
679191783Srmacklem		if (nmp->nm_sotype == SOCK_STREAM) {
680191783Srmacklem			error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock);
681191783Srmacklem			if (!error)
682191783Srmacklem				haslock = 1;
683191783Srmacklem		}
684191783Srmacklem		if (!error) {
685191783Srmacklem		    newnfs_disconnect(&nmp->nm_sockreq);
686191783Srmacklem		    if (haslock)
687191783Srmacklem			newnfs_sndunlock(&nmp->nm_sockreq.nr_lock);
688191783Srmacklem		    nmp->nm_sotype = argp->sotype;
689191783Srmacklem		    nmp->nm_soproto = argp->proto;
690191783Srmacklem		    if (nmp->nm_sotype == SOCK_DGRAM)
691191783Srmacklem			while (newnfs_connect(nmp, &nmp->nm_sockreq,
692191783Srmacklem			    cred, td, 0)) {
693191783Srmacklem				printf("newnfs_args: retrying connect\n");
694207170Srmacklem				(void) nfs_catnap(PSOCK, 0, "newnfscon");
695191783Srmacklem			}
696191783Srmacklem		}
697191783Srmacklem	} else {
698191783Srmacklem		nmp->nm_sotype = argp->sotype;
699191783Srmacklem		nmp->nm_soproto = argp->proto;
700191783Srmacklem	}
701214048Srmacklem
702214048Srmacklem	if (hostname != NULL) {
703214048Srmacklem		strlcpy(nmp->nm_hostname, hostname,
704214048Srmacklem		    sizeof(nmp->nm_hostname));
705214048Srmacklem		p = strchr(nmp->nm_hostname, ':');
706214048Srmacklem		if (p != NULL)
707214048Srmacklem			*p = '\0';
708214048Srmacklem	}
709191783Srmacklem}
710191783Srmacklem
711221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args",
712191783Srmacklem    "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union",
713191783Srmacklem    "noclusterr", "noclusterw", "multilabel", "acls", "force", "update",
714192585Srmacklem    "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus",
715192585Srmacklem    "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
716192585Srmacklem    "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
717192585Srmacklem    "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
718192585Srmacklem    "principal", "nfsv4", "gssname", "allgssname", "dirpath",
719221436Sru    "negnametimeo", "nocto",
720191783Srmacklem    NULL };
721191783Srmacklem
722191783Srmacklem/*
723191783Srmacklem * VFS Operations.
724191783Srmacklem *
725191783Srmacklem * mount system call
726191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
727191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
728191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
729191783Srmacklem * an error after that means that I have to release the mbuf.
730191783Srmacklem */
731191783Srmacklem/* ARGSUSED */
732191783Srmacklemstatic int
733191990Sattilionfs_mount(struct mount *mp)
734191783Srmacklem{
735191783Srmacklem	struct nfs_args args = {
736191783Srmacklem	    .version = NFS_ARGSVERSION,
737191783Srmacklem	    .addr = NULL,
738191783Srmacklem	    .addrlen = sizeof (struct sockaddr_in),
739191783Srmacklem	    .sotype = SOCK_STREAM,
740191783Srmacklem	    .proto = 0,
741191783Srmacklem	    .fh = NULL,
742191783Srmacklem	    .fhsize = 0,
743220739Srmacklem	    .flags = NFSMNT_RESVPORT,
744191783Srmacklem	    .wsize = NFS_WSIZE,
745191783Srmacklem	    .rsize = NFS_RSIZE,
746191783Srmacklem	    .readdirsize = NFS_READDIRSIZE,
747191783Srmacklem	    .timeo = 10,
748191783Srmacklem	    .retrans = NFS_RETRANS,
749191783Srmacklem	    .readahead = NFS_DEFRAHEAD,
750191783Srmacklem	    .wcommitsize = 0,			/* was: NQ_DEFLEASE */
751191783Srmacklem	    .hostname = NULL,
752191783Srmacklem	    .acregmin = NFS_MINATTRTIMO,
753191783Srmacklem	    .acregmax = NFS_MAXATTRTIMO,
754191783Srmacklem	    .acdirmin = NFS_MINDIRATTRTIMO,
755191783Srmacklem	    .acdirmax = NFS_MAXDIRATTRTIMO,
756191783Srmacklem	};
757192585Srmacklem	int error = 0, ret, len;
758192585Srmacklem	struct sockaddr *nam = NULL;
759191783Srmacklem	struct vnode *vp;
760191990Sattilio	struct thread *td;
761191783Srmacklem	char hst[MNAMELEN];
762191783Srmacklem	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
763192585Srmacklem	char *opt, *name, *secname;
764203303Srmacklem	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
765221190Srmacklem	int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
766221205Srmacklem	size_t hstlen;
767191783Srmacklem
768221190Srmacklem	has_nfs_args_opt = 0;
769191783Srmacklem	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
770191783Srmacklem		error = EINVAL;
771191783Srmacklem		goto out;
772191783Srmacklem	}
773191783Srmacklem
774191990Sattilio	td = curthread;
775191783Srmacklem	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
776221040Srmacklem		error = nfs_mountroot(mp);
777191783Srmacklem		goto out;
778191783Srmacklem	}
779191783Srmacklem
780192585Srmacklem	nfscl_init();
781191783Srmacklem
782221190Srmacklem	/*
783221190Srmacklem	 * The old mount_nfs program passed the struct nfs_args
784221190Srmacklem	 * from userspace to kernel.  The new mount_nfs program
785221190Srmacklem	 * passes string options via nmount() from userspace to kernel
786221190Srmacklem	 * and we populate the struct nfs_args in the kernel.
787221190Srmacklem	 */
788221190Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) {
789221190Srmacklem		error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args,
790221190Srmacklem		    sizeof(args));
791221190Srmacklem		if (error != 0)
792221190Srmacklem			goto out;
793221190Srmacklem
794221190Srmacklem		if (args.version != NFS_ARGSVERSION) {
795221190Srmacklem			error = EPROGMISMATCH;
796221190Srmacklem			goto out;
797221190Srmacklem		}
798221190Srmacklem		has_nfs_args_opt = 1;
799221190Srmacklem	}
800221190Srmacklem
801192585Srmacklem	/* Handle the new style options. */
802192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0)
803192585Srmacklem		args.flags |= NFSMNT_NOCONN;
804192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0)
805192585Srmacklem		args.flags |= NFSMNT_NOCONN;
806192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0)
807192585Srmacklem		args.flags |= NFSMNT_NOLOCKD;
808192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0)
809192585Srmacklem		args.flags &= ~NFSMNT_NOLOCKD;
810192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0)
811192585Srmacklem		args.flags |= NFSMNT_INT;
812192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0)
813192585Srmacklem		args.flags |= NFSMNT_RDIRPLUS;
814192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0)
815192585Srmacklem		args.flags |= NFSMNT_RESVPORT;
816192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0)
817192585Srmacklem		args.flags &= ~NFSMNT_RESVPORT;
818192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0)
819192585Srmacklem		args.flags |= NFSMNT_SOFT;
820192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0)
821192585Srmacklem		args.flags &= ~NFSMNT_SOFT;
822192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0)
823192585Srmacklem		args.sotype = SOCK_DGRAM;
824192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0)
825192585Srmacklem		args.sotype = SOCK_DGRAM;
826192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0)
827192585Srmacklem		args.sotype = SOCK_STREAM;
828192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0)
829192585Srmacklem		args.flags |= NFSMNT_NFSV3;
830192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) {
831192585Srmacklem		args.flags |= NFSMNT_NFSV4;
832192585Srmacklem		args.sotype = SOCK_STREAM;
833191783Srmacklem	}
834192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0)
835192585Srmacklem		args.flags |= NFSMNT_ALLGSSNAME;
836221436Sru	if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
837221436Sru		args.flags |= NFSMNT_NOCTO;
838192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
839192585Srmacklem		if (opt == NULL) {
840192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize");
841192585Srmacklem			error = EINVAL;
842192585Srmacklem			goto out;
843192585Srmacklem		}
844192585Srmacklem		ret = sscanf(opt, "%d", &args.readdirsize);
845192585Srmacklem		if (ret != 1 || args.readdirsize <= 0) {
846192585Srmacklem			vfs_mount_error(mp, "illegal readdirsize: %s",
847192585Srmacklem			    opt);
848192585Srmacklem			error = EINVAL;
849192585Srmacklem			goto out;
850192585Srmacklem		}
851192585Srmacklem		args.flags |= NFSMNT_READDIRSIZE;
852192585Srmacklem	}
853192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) {
854192585Srmacklem		if (opt == NULL) {
855192585Srmacklem			vfs_mount_error(mp, "illegal readahead");
856192585Srmacklem			error = EINVAL;
857192585Srmacklem			goto out;
858192585Srmacklem		}
859192585Srmacklem		ret = sscanf(opt, "%d", &args.readahead);
860192585Srmacklem		if (ret != 1 || args.readahead <= 0) {
861192585Srmacklem			vfs_mount_error(mp, "illegal readahead: %s",
862192585Srmacklem			    opt);
863192585Srmacklem			error = EINVAL;
864192585Srmacklem			goto out;
865192585Srmacklem		}
866192585Srmacklem		args.flags |= NFSMNT_READAHEAD;
867192585Srmacklem	}
868192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) {
869192585Srmacklem		if (opt == NULL) {
870192585Srmacklem			vfs_mount_error(mp, "illegal wsize");
871192585Srmacklem			error = EINVAL;
872192585Srmacklem			goto out;
873192585Srmacklem		}
874192585Srmacklem		ret = sscanf(opt, "%d", &args.wsize);
875192585Srmacklem		if (ret != 1 || args.wsize <= 0) {
876192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
877192585Srmacklem			    opt);
878192585Srmacklem			error = EINVAL;
879192585Srmacklem			goto out;
880192585Srmacklem		}
881192585Srmacklem		args.flags |= NFSMNT_WSIZE;
882192585Srmacklem	}
883192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) {
884192585Srmacklem		if (opt == NULL) {
885192585Srmacklem			vfs_mount_error(mp, "illegal rsize");
886192585Srmacklem			error = EINVAL;
887192585Srmacklem			goto out;
888192585Srmacklem		}
889192585Srmacklem		ret = sscanf(opt, "%d", &args.rsize);
890192585Srmacklem		if (ret != 1 || args.rsize <= 0) {
891192585Srmacklem			vfs_mount_error(mp, "illegal wsize: %s",
892192585Srmacklem			    opt);
893192585Srmacklem			error = EINVAL;
894192585Srmacklem			goto out;
895192585Srmacklem		}
896192585Srmacklem		args.flags |= NFSMNT_RSIZE;
897192585Srmacklem	}
898192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) {
899192585Srmacklem		if (opt == NULL) {
900192585Srmacklem			vfs_mount_error(mp, "illegal retrans");
901192585Srmacklem			error = EINVAL;
902192585Srmacklem			goto out;
903192585Srmacklem		}
904192585Srmacklem		ret = sscanf(opt, "%d", &args.retrans);
905192585Srmacklem		if (ret != 1 || args.retrans <= 0) {
906192585Srmacklem			vfs_mount_error(mp, "illegal retrans: %s",
907192585Srmacklem			    opt);
908192585Srmacklem			error = EINVAL;
909192585Srmacklem			goto out;
910192585Srmacklem		}
911192585Srmacklem		args.flags |= NFSMNT_RETRANS;
912192585Srmacklem	}
913192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) {
914192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmin);
915192585Srmacklem		if (ret != 1 || args.acregmin < 0) {
916192585Srmacklem			vfs_mount_error(mp, "illegal acregmin: %s",
917192585Srmacklem			    opt);
918192585Srmacklem			error = EINVAL;
919192585Srmacklem			goto out;
920192585Srmacklem		}
921192585Srmacklem		args.flags |= NFSMNT_ACREGMIN;
922192585Srmacklem	}
923192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) {
924192585Srmacklem		ret = sscanf(opt, "%d", &args.acregmax);
925192585Srmacklem		if (ret != 1 || args.acregmax < 0) {
926192585Srmacklem			vfs_mount_error(mp, "illegal acregmax: %s",
927192585Srmacklem			    opt);
928192585Srmacklem			error = EINVAL;
929192585Srmacklem			goto out;
930192585Srmacklem		}
931192585Srmacklem		args.flags |= NFSMNT_ACREGMAX;
932192585Srmacklem	}
933192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) {
934192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmin);
935192585Srmacklem		if (ret != 1 || args.acdirmin < 0) {
936192585Srmacklem			vfs_mount_error(mp, "illegal acdirmin: %s",
937192585Srmacklem			    opt);
938192585Srmacklem			error = EINVAL;
939192585Srmacklem			goto out;
940192585Srmacklem		}
941192585Srmacklem		args.flags |= NFSMNT_ACDIRMIN;
942192585Srmacklem	}
943192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) {
944192585Srmacklem		ret = sscanf(opt, "%d", &args.acdirmax);
945192585Srmacklem		if (ret != 1 || args.acdirmax < 0) {
946192585Srmacklem			vfs_mount_error(mp, "illegal acdirmax: %s",
947192585Srmacklem			    opt);
948192585Srmacklem			error = EINVAL;
949192585Srmacklem			goto out;
950192585Srmacklem		}
951192585Srmacklem		args.flags |= NFSMNT_ACDIRMAX;
952192585Srmacklem	}
953192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) {
954192585Srmacklem		ret = sscanf(opt, "%d", &args.timeo);
955192585Srmacklem		if (ret != 1 || args.timeo <= 0) {
956192585Srmacklem			vfs_mount_error(mp, "illegal timeout: %s",
957192585Srmacklem			    opt);
958192585Srmacklem			error = EINVAL;
959192585Srmacklem			goto out;
960192585Srmacklem		}
961192585Srmacklem		args.flags |= NFSMNT_TIMEO;
962192585Srmacklem	}
963203303Srmacklem	if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL)
964203303Srmacklem	    == 0) {
965203303Srmacklem		ret = sscanf(opt, "%d", &negnametimeo);
966203303Srmacklem		if (ret != 1 || negnametimeo < 0) {
967203303Srmacklem			vfs_mount_error(mp, "illegal negnametimeo: %s",
968203303Srmacklem			    opt);
969203303Srmacklem			error = EINVAL;
970203303Srmacklem			goto out;
971203303Srmacklem		}
972203303Srmacklem	}
973192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "sec",
974192585Srmacklem		(void **) &secname, NULL) == 0)
975192585Srmacklem		nfs_sec_name(secname, &args.flags);
976191783Srmacklem
977191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
978191783Srmacklem		struct nfsmount *nmp = VFSTONFS(mp);
979191783Srmacklem
980191783Srmacklem		if (nmp == NULL) {
981191783Srmacklem			error = EIO;
982191783Srmacklem			goto out;
983191783Srmacklem		}
984191783Srmacklem		/*
985191783Srmacklem		 * When doing an update, we can't change version,
986191783Srmacklem		 * security, switch lockd strategies or change cookie
987191783Srmacklem		 * translation
988191783Srmacklem		 */
989191783Srmacklem		args.flags = (args.flags &
990191783Srmacklem		    ~(NFSMNT_NFSV3 |
991191783Srmacklem		      NFSMNT_NFSV4 |
992191783Srmacklem		      NFSMNT_KERB |
993191783Srmacklem		      NFSMNT_INTEGRITY |
994191783Srmacklem		      NFSMNT_PRIVACY |
995191783Srmacklem		      NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) |
996191783Srmacklem		    (nmp->nm_flag &
997191783Srmacklem			(NFSMNT_NFSV3 |
998191783Srmacklem			 NFSMNT_NFSV4 |
999191783Srmacklem			 NFSMNT_KERB |
1000191783Srmacklem			 NFSMNT_INTEGRITY |
1001191783Srmacklem			 NFSMNT_PRIVACY |
1002191783Srmacklem			 NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/));
1003214048Srmacklem		nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td);
1004191783Srmacklem		goto out;
1005191783Srmacklem	}
1006191783Srmacklem
1007191783Srmacklem	/*
1008191783Srmacklem	 * Make the nfs_ip_paranoia sysctl serve as the default connection
1009191783Srmacklem	 * or no-connection mode for those protocols that support
1010191783Srmacklem	 * no-connection mode (the flag will be cleared later for protocols
1011191783Srmacklem	 * that do not support no-connection mode).  This will allow a client
1012191783Srmacklem	 * to receive replies from a different IP then the request was
1013191783Srmacklem	 * sent to.  Note: default value for nfs_ip_paranoia is 1 (paranoid),
1014191783Srmacklem	 * not 0.
1015191783Srmacklem	 */
1016191783Srmacklem	if (nfs_ip_paranoia == 0)
1017191783Srmacklem		args.flags |= NFSMNT_NOCONN;
1018192585Srmacklem
1019221190Srmacklem	if (has_nfs_args_opt != 0) {
1020221190Srmacklem		/*
1021221190Srmacklem		 * In the 'nfs_args' case, the pointers in the args
1022221190Srmacklem		 * structure are in userland - we copy them in here.
1023221190Srmacklem		 */
1024221190Srmacklem		if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) {
1025192585Srmacklem			vfs_mount_error(mp, "Bad file handle");
1026191783Srmacklem			error = EINVAL;
1027191783Srmacklem			goto out;
1028191783Srmacklem		}
1029221190Srmacklem		error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1030221190Srmacklem		    args.fhsize);
1031221190Srmacklem		if (error != 0)
1032221190Srmacklem			goto out;
1033221205Srmacklem		error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen);
1034221190Srmacklem		if (error != 0)
1035221190Srmacklem			goto out;
1036221205Srmacklem		bzero(&hst[hstlen], MNAMELEN - hstlen);
1037221190Srmacklem		args.hostname = hst;
1038221190Srmacklem		/* sockargs() call must be after above copyin() calls */
1039221190Srmacklem		error = getsockaddr(&nam, (caddr_t)args.addr,
1040221190Srmacklem		    args.addrlen);
1041221190Srmacklem		if (error != 0)
1042221190Srmacklem			goto out;
1043191783Srmacklem	} else {
1044221190Srmacklem		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
1045221190Srmacklem		    &args.fhsize) == 0) {
1046221190Srmacklem			if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) {
1047221190Srmacklem				vfs_mount_error(mp, "Bad file handle");
1048221190Srmacklem				error = EINVAL;
1049221190Srmacklem				goto out;
1050221190Srmacklem			}
1051221190Srmacklem			bcopy(args.fh, nfh, args.fhsize);
1052221190Srmacklem		} else {
1053221190Srmacklem			args.fhsize = 0;
1054221190Srmacklem		}
1055221190Srmacklem		(void) vfs_getopt(mp->mnt_optnew, "hostname",
1056221190Srmacklem		    (void **)&args.hostname, &len);
1057221190Srmacklem		if (args.hostname == NULL) {
1058221190Srmacklem			vfs_mount_error(mp, "Invalid hostname");
1059221190Srmacklem			error = EINVAL;
1060221190Srmacklem			goto out;
1061221190Srmacklem		}
1062221190Srmacklem		bcopy(args.hostname, hst, MNAMELEN);
1063221190Srmacklem		hst[MNAMELEN - 1] = '\0';
1064192585Srmacklem	}
1065192585Srmacklem
1066192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0)
1067192585Srmacklem		strlcpy(srvkrbname, name, sizeof (srvkrbname));
1068192585Srmacklem	else
1069192585Srmacklem		snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst);
1070221014Srmacklem	srvkrbnamelen = strlen(srvkrbname);
1071192585Srmacklem
1072192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0)
1073192585Srmacklem		strlcpy(krbname, name, sizeof (krbname));
1074192585Srmacklem	else
1075191783Srmacklem		krbname[0] = '\0';
1076221014Srmacklem	krbnamelen = strlen(krbname);
1077192585Srmacklem
1078192585Srmacklem	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
1079192585Srmacklem		strlcpy(dirpath, name, sizeof (dirpath));
1080192585Srmacklem	else
1081191783Srmacklem		dirpath[0] = '\0';
1082221014Srmacklem	dirlen = strlen(dirpath);
1083192585Srmacklem
1084221190Srmacklem	if (has_nfs_args_opt == 0 && vfs_getopt(mp->mnt_optnew, "addr",
1085221190Srmacklem	    (void **)&args.addr, &args.addrlen) == 0) {
1086192585Srmacklem		if (args.addrlen > SOCK_MAXADDRLEN) {
1087192585Srmacklem			error = ENAMETOOLONG;
1088191783Srmacklem			goto out;
1089191783Srmacklem		}
1090192585Srmacklem		nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
1091192585Srmacklem		bcopy(args.addr, nam, args.addrlen);
1092192585Srmacklem		nam->sa_len = args.addrlen;
1093191783Srmacklem	}
1094192585Srmacklem
1095191783Srmacklem	args.fh = nfh;
1096221014Srmacklem	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
1097221014Srmacklem	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
1098221014Srmacklem	    negnametimeo);
1099191783Srmacklemout:
1100191783Srmacklem	if (!error) {
1101191783Srmacklem		MNT_ILOCK(mp);
1102191783Srmacklem		mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED);
1103191783Srmacklem		MNT_IUNLOCK(mp);
1104191783Srmacklem	}
1105191783Srmacklem	return (error);
1106191783Srmacklem}
1107191783Srmacklem
1108191783Srmacklem
1109191783Srmacklem/*
1110191783Srmacklem * VFS Operations.
1111191783Srmacklem *
1112191783Srmacklem * mount system call
1113191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then
1114191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before
1115191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and
1116191783Srmacklem * an error after that means that I have to release the mbuf.
1117191783Srmacklem */
1118191783Srmacklem/* ARGSUSED */
1119191783Srmacklemstatic int
1120191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags)
1121191783Srmacklem{
1122191783Srmacklem	int error;
1123191783Srmacklem	struct nfs_args args;
1124191783Srmacklem
1125191783Srmacklem	error = copyin(data, &args, sizeof (struct nfs_args));
1126191783Srmacklem	if (error)
1127191783Srmacklem		return error;
1128191783Srmacklem
1129191783Srmacklem	ma = mount_arg(ma, "nfs_args", &args, sizeof args);
1130191783Srmacklem
1131191783Srmacklem	error = kernel_mount(ma, flags);
1132191783Srmacklem	return (error);
1133191783Srmacklem}
1134191783Srmacklem
1135191783Srmacklem/*
1136191783Srmacklem * Common code for mount and mountroot
1137191783Srmacklem */
1138191783Srmacklemstatic int
1139191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1140221014Srmacklem    char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
1141221014Srmacklem    u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
1142221014Srmacklem    struct ucred *cred, struct thread *td, int negnametimeo)
1143191783Srmacklem{
1144191783Srmacklem	struct nfsmount *nmp;
1145191783Srmacklem	struct nfsnode *np;
1146195762Srmacklem	int error, trycnt, ret;
1147191783Srmacklem	struct nfsvattr nfsva;
1148191783Srmacklem	static u_int64_t clval = 0;
1149191783Srmacklem
1150191783Srmacklem	if (mp->mnt_flag & MNT_UPDATE) {
1151191783Srmacklem		nmp = VFSTONFS(mp);
1152191783Srmacklem		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
1153191783Srmacklem		FREE(nam, M_SONAME);
1154191783Srmacklem		return (0);
1155191783Srmacklem	} else {
1156191783Srmacklem		MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) +
1157221014Srmacklem		    krbnamelen + dirlen + srvkrbnamelen + 2,
1158221014Srmacklem		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
1159191783Srmacklem		TAILQ_INIT(&nmp->nm_bufq);
1160191783Srmacklem		if (clval == 0)
1161191783Srmacklem			clval = (u_int64_t)nfsboottime.tv_sec;
1162191783Srmacklem		nmp->nm_clval = clval++;
1163221014Srmacklem		nmp->nm_krbnamelen = krbnamelen;
1164221014Srmacklem		nmp->nm_dirpathlen = dirlen;
1165221014Srmacklem		nmp->nm_srvkrbnamelen = srvkrbnamelen;
1166192675Srmacklem		if (td->td_ucred->cr_uid != (uid_t)0) {
1167191783Srmacklem			/*
1168192675Srmacklem			 * nm_uid is used to get KerberosV credentials for
1169192675Srmacklem			 * the nfsv4 state handling operations if there is
1170192675Srmacklem			 * no host based principal set. Use the uid of
1171192675Srmacklem			 * this user if not root, since they are doing the
1172192675Srmacklem			 * mount. I don't think setting this for root will
1173192675Srmacklem			 * work, since root normally does not have user
1174192675Srmacklem			 * credentials in a credentials cache.
1175191783Srmacklem			 */
1176192675Srmacklem			nmp->nm_uid = td->td_ucred->cr_uid;
1177191783Srmacklem		} else {
1178191783Srmacklem			/*
1179192675Srmacklem			 * Just set to -1, so it won't be used.
1180191783Srmacklem			 */
1181191783Srmacklem			nmp->nm_uid = (uid_t)-1;
1182191783Srmacklem		}
1183191783Srmacklem
1184191783Srmacklem		/* Copy and null terminate all the names */
1185191783Srmacklem		if (nmp->nm_krbnamelen > 0) {
1186191783Srmacklem			bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen);
1187191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen] = '\0';
1188191783Srmacklem		}
1189191783Srmacklem		if (nmp->nm_dirpathlen > 0) {
1190191783Srmacklem			bcopy(dirpath, NFSMNT_DIRPATH(nmp),
1191191783Srmacklem			    nmp->nm_dirpathlen);
1192191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1193191783Srmacklem			    + 1] = '\0';
1194191783Srmacklem		}
1195191783Srmacklem		if (nmp->nm_srvkrbnamelen > 0) {
1196191783Srmacklem			bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp),
1197191783Srmacklem			    nmp->nm_srvkrbnamelen);
1198191783Srmacklem			nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen
1199191783Srmacklem			    + nmp->nm_srvkrbnamelen + 2] = '\0';
1200191783Srmacklem		}
1201191783Srmacklem		nmp->nm_sockreq.nr_cred = crhold(cred);
1202191783Srmacklem		mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF);
1203191783Srmacklem		mp->mnt_data = nmp;
1204214048Srmacklem		nmp->nm_getinfo = nfs_getnlminfo;
1205216931Srmacklem		nmp->nm_vinvalbuf = ncl_vinvalbuf;
1206191783Srmacklem	}
1207191783Srmacklem	vfs_getnewfsid(mp);
1208191783Srmacklem	nmp->nm_mountp = mp;
1209191783Srmacklem	mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK);
1210203303Srmacklem	nmp->nm_negnametimeo = negnametimeo;
1211191783Srmacklem
1212214048Srmacklem	nfs_decode_args(mp, nmp, argp, hst, cred, td);
1213192585Srmacklem
1214191783Srmacklem	/*
1215191783Srmacklem	 * V2 can only handle 32 bit filesizes.  A 4GB-1 limit may be too
1216191783Srmacklem	 * high, depending on whether we end up with negative offsets in
1217191783Srmacklem	 * the client or server somewhere.  2GB-1 may be safer.
1218191783Srmacklem	 *
1219191783Srmacklem	 * For V3, ncl_fsinfo will adjust this as necessary.  Assume maximum
1220191783Srmacklem	 * that we can handle until we find out otherwise.
1221191783Srmacklem	 * XXX Our "safe" limit on the client is what we can store in our
1222191783Srmacklem	 * buffer cache using signed(!) block numbers.
1223191783Srmacklem	 */
1224191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0)
1225191783Srmacklem		nmp->nm_maxfilesize = 0xffffffffLL;
1226191783Srmacklem	else
1227221537Srmacklem		nmp->nm_maxfilesize = OFF_MAX;
1228191783Srmacklem
1229191783Srmacklem	nmp->nm_timeo = NFS_TIMEO;
1230191783Srmacklem	nmp->nm_retry = NFS_RETRANS;
1231191783Srmacklem	if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) {
1232191783Srmacklem		nmp->nm_wsize = NFS_WSIZE;
1233191783Srmacklem		nmp->nm_rsize = NFS_RSIZE;
1234191783Srmacklem		nmp->nm_readdirsize = NFS_READDIRSIZE;
1235191783Srmacklem	}
1236191783Srmacklem	nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
1237191783Srmacklem	nmp->nm_numgrps = NFS_MAXGRPS;
1238191783Srmacklem	nmp->nm_readahead = NFS_DEFRAHEAD;
1239191783Srmacklem	nmp->nm_tprintf_delay = nfs_tprintf_delay;
1240191783Srmacklem	if (nmp->nm_tprintf_delay < 0)
1241191783Srmacklem		nmp->nm_tprintf_delay = 0;
1242191783Srmacklem	nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
1243191783Srmacklem	if (nmp->nm_tprintf_initial_delay < 0)
1244191783Srmacklem		nmp->nm_tprintf_initial_delay = 0;
1245191783Srmacklem	nmp->nm_fhsize = argp->fhsize;
1246191783Srmacklem	if (nmp->nm_fhsize > 0)
1247191783Srmacklem		bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
1248191783Srmacklem	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
1249191783Srmacklem	nmp->nm_nam = nam;
1250191783Srmacklem	/* Set up the sockets and per-host congestion */
1251191783Srmacklem	nmp->nm_sotype = argp->sotype;
1252191783Srmacklem	nmp->nm_soproto = argp->proto;
1253191783Srmacklem	nmp->nm_sockreq.nr_prog = NFS_PROG;
1254191783Srmacklem	if ((argp->flags & NFSMNT_NFSV4))
1255191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER4;
1256191783Srmacklem	else if ((argp->flags & NFSMNT_NFSV3))
1257191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER3;
1258191783Srmacklem	else
1259191783Srmacklem		nmp->nm_sockreq.nr_vers = NFS_VER2;
1260191783Srmacklem
1261191783Srmacklem
1262191783Srmacklem	if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
1263191783Srmacklem		goto bad;
1264191783Srmacklem
1265191783Srmacklem	/*
1266191783Srmacklem	 * A reference count is needed on the nfsnode representing the
1267191783Srmacklem	 * remote root.  If this object is not persistent, then backward
1268191783Srmacklem	 * traversals of the mount point (i.e. "..") will not work if
1269191783Srmacklem	 * the nfsnode gets flushed out of the cache. Ufs does not have
1270191783Srmacklem	 * this problem, because one can identify root inodes by their
1271191783Srmacklem	 * number == ROOTINO (2).
1272191783Srmacklem	 */
1273191783Srmacklem	if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
1274191783Srmacklem	    nmp->nm_dirpathlen > 0) {
1275191783Srmacklem		/*
1276191783Srmacklem		 * If the fhsize on the mount point == 0 for V4, the mount
1277191783Srmacklem		 * path needs to be looked up.
1278191783Srmacklem		 */
1279191783Srmacklem		trycnt = 3;
1280191783Srmacklem		do {
1281191783Srmacklem			error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
1282191783Srmacklem			    cred, td);
1283191783Srmacklem			if (error)
1284207170Srmacklem				(void) nfs_catnap(PZERO, error, "nfsgetdirp");
1285191783Srmacklem		} while (error && --trycnt > 0);
1286191783Srmacklem		if (error) {
1287191783Srmacklem			error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
1288191783Srmacklem			goto bad;
1289191783Srmacklem		}
1290191783Srmacklem	}
1291191783Srmacklem	if (nmp->nm_fhsize > 0) {
1292195762Srmacklem		/*
1293195762Srmacklem		 * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
1294195762Srmacklem		 * non-zero for the root vnode. f_iosize will be set correctly
1295195762Srmacklem		 * by nfs_statfs() before any I/O occurs.
1296195762Srmacklem		 */
1297195762Srmacklem		mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ;
1298220732Srmacklem		error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np,
1299220732Srmacklem		    LK_EXCLUSIVE);
1300191783Srmacklem		if (error)
1301191783Srmacklem			goto bad;
1302191783Srmacklem		*vpp = NFSTOV(np);
1303191783Srmacklem
1304191783Srmacklem		/*
1305191783Srmacklem		 * Get file attributes and transfer parameters for the
1306191783Srmacklem		 * mountpoint.  This has the side effect of filling in
1307191783Srmacklem		 * (*vpp)->v_type with the correct value.
1308191783Srmacklem		 */
1309191783Srmacklem		ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
1310191783Srmacklem		    cred, td, &nfsva, NULL);
1311191783Srmacklem		if (ret) {
1312191783Srmacklem			/*
1313191783Srmacklem			 * Just set default values to get things going.
1314191783Srmacklem			 */
1315191783Srmacklem			NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr));
1316191783Srmacklem			nfsva.na_vattr.va_type = VDIR;
1317191783Srmacklem			nfsva.na_vattr.va_mode = 0777;
1318191783Srmacklem			nfsva.na_vattr.va_nlink = 100;
1319191783Srmacklem			nfsva.na_vattr.va_uid = (uid_t)0;
1320191783Srmacklem			nfsva.na_vattr.va_gid = (gid_t)0;
1321191783Srmacklem			nfsva.na_vattr.va_fileid = 2;
1322191783Srmacklem			nfsva.na_vattr.va_gen = 1;
1323191783Srmacklem			nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
1324191783Srmacklem			nfsva.na_vattr.va_size = 512 * 1024;
1325191783Srmacklem		}
1326191783Srmacklem		(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
1327191783Srmacklem		if (argp->flags & NFSMNT_NFSV3)
1328191783Srmacklem			ncl_fsinfo(nmp, *vpp, cred, td);
1329191783Srmacklem
1330191783Srmacklem		/*
1331191783Srmacklem		 * Lose the lock but keep the ref.
1332191783Srmacklem		 */
1333191783Srmacklem		VOP_UNLOCK(*vpp, 0);
1334191783Srmacklem		return (0);
1335191783Srmacklem	}
1336191783Srmacklem	error = EIO;
1337191783Srmacklem
1338191783Srmacklembad:
1339191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1340191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1341191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1342191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1343191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1344191783Srmacklem	FREE(nam, M_SONAME);
1345191783Srmacklem	return (error);
1346191783Srmacklem}
1347191783Srmacklem
1348191783Srmacklem/*
1349191783Srmacklem * unmount system call
1350191783Srmacklem */
1351191783Srmacklemstatic int
1352191990Sattilionfs_unmount(struct mount *mp, int mntflags)
1353191783Srmacklem{
1354191990Sattilio	struct thread *td;
1355191783Srmacklem	struct nfsmount *nmp;
1356191783Srmacklem	int error, flags = 0, trycnt = 0;
1357191783Srmacklem
1358191990Sattilio	td = curthread;
1359191990Sattilio
1360191783Srmacklem	if (mntflags & MNT_FORCE)
1361191783Srmacklem		flags |= FORCECLOSE;
1362191783Srmacklem	nmp = VFSTONFS(mp);
1363191783Srmacklem	/*
1364191783Srmacklem	 * Goes something like this..
1365191783Srmacklem	 * - Call vflush() to clear out vnodes for this filesystem
1366191783Srmacklem	 * - Close the socket
1367191783Srmacklem	 * - Free up the data structures
1368191783Srmacklem	 */
1369191783Srmacklem	/* In the forced case, cancel any outstanding requests. */
1370191783Srmacklem	if (mntflags & MNT_FORCE) {
1371191783Srmacklem		error = newnfs_nmcancelreqs(nmp);
1372191783Srmacklem		if (error)
1373191783Srmacklem			goto out;
1374191783Srmacklem		/* For a forced close, get rid of the renew thread now */
1375191783Srmacklem		nfscl_umount(nmp, td);
1376191783Srmacklem	}
1377191783Srmacklem	/* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */
1378191783Srmacklem	do {
1379191783Srmacklem		error = vflush(mp, 1, flags, td);
1380191783Srmacklem		if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30)
1381207170Srmacklem			(void) nfs_catnap(PSOCK, error, "newndm");
1382191783Srmacklem	} while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30);
1383191783Srmacklem	if (error)
1384191783Srmacklem		goto out;
1385191783Srmacklem
1386191783Srmacklem	/*
1387191783Srmacklem	 * We are now committed to the unmount.
1388191783Srmacklem	 */
1389191783Srmacklem	if ((mntflags & MNT_FORCE) == 0)
1390191783Srmacklem		nfscl_umount(nmp, td);
1391191783Srmacklem	newnfs_disconnect(&nmp->nm_sockreq);
1392191783Srmacklem	crfree(nmp->nm_sockreq.nr_cred);
1393191783Srmacklem	FREE(nmp->nm_nam, M_SONAME);
1394191783Srmacklem
1395191783Srmacklem	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
1396191783Srmacklem	mtx_destroy(&nmp->nm_mtx);
1397191783Srmacklem	FREE(nmp, M_NEWNFSMNT);
1398191783Srmacklemout:
1399191783Srmacklem	return (error);
1400191783Srmacklem}
1401191783Srmacklem
1402191783Srmacklem/*
1403191783Srmacklem * Return root of a filesystem
1404191783Srmacklem */
1405191783Srmacklemstatic int
1406191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp)
1407191783Srmacklem{
1408191783Srmacklem	struct vnode *vp;
1409191783Srmacklem	struct nfsmount *nmp;
1410191783Srmacklem	struct nfsnode *np;
1411191783Srmacklem	int error;
1412191783Srmacklem
1413191783Srmacklem	nmp = VFSTONFS(mp);
1414220732Srmacklem	error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags);
1415191783Srmacklem	if (error)
1416191783Srmacklem		return error;
1417191783Srmacklem	vp = NFSTOV(np);
1418191783Srmacklem	/*
1419191783Srmacklem	 * Get transfer parameters and attributes for root vnode once.
1420191783Srmacklem	 */
1421191783Srmacklem	mtx_lock(&nmp->nm_mtx);
1422191783Srmacklem	if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) {
1423191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1424191783Srmacklem		ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread);
1425191783Srmacklem	} else
1426191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1427191783Srmacklem	if (vp->v_type == VNON)
1428191783Srmacklem	    vp->v_type = VDIR;
1429191783Srmacklem	vp->v_vflag |= VV_ROOT;
1430191783Srmacklem	*vpp = vp;
1431191783Srmacklem	return (0);
1432191783Srmacklem}
1433191783Srmacklem
1434191783Srmacklem/*
1435191783Srmacklem * Flush out the buffer cache
1436191783Srmacklem */
1437191783Srmacklem/* ARGSUSED */
1438191783Srmacklemstatic int
1439191990Sattilionfs_sync(struct mount *mp, int waitfor)
1440191783Srmacklem{
1441191783Srmacklem	struct vnode *vp, *mvp;
1442191990Sattilio	struct thread *td;
1443191783Srmacklem	int error, allerror = 0;
1444191783Srmacklem
1445191990Sattilio	td = curthread;
1446191990Sattilio
1447191783Srmacklem	/*
1448191783Srmacklem	 * Force stale buffer cache information to be flushed.
1449191783Srmacklem	 */
1450191783Srmacklem	MNT_ILOCK(mp);
1451191783Srmacklemloop:
1452191783Srmacklem	MNT_VNODE_FOREACH(vp, mp, mvp) {
1453191783Srmacklem		VI_LOCK(vp);
1454191783Srmacklem		MNT_IUNLOCK(mp);
1455191783Srmacklem		/* XXX Racy bv_cnt check. */
1456191783Srmacklem		if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1457191783Srmacklem		    waitfor == MNT_LAZY) {
1458191783Srmacklem			VI_UNLOCK(vp);
1459191783Srmacklem			MNT_ILOCK(mp);
1460191783Srmacklem			continue;
1461191783Srmacklem		}
1462191783Srmacklem		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
1463191783Srmacklem			MNT_ILOCK(mp);
1464191783Srmacklem			MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
1465191783Srmacklem			goto loop;
1466191783Srmacklem		}
1467191783Srmacklem		error = VOP_FSYNC(vp, waitfor, td);
1468191783Srmacklem		if (error)
1469191783Srmacklem			allerror = error;
1470191783Srmacklem		VOP_UNLOCK(vp, 0);
1471191783Srmacklem		vrele(vp);
1472191783Srmacklem
1473191783Srmacklem		MNT_ILOCK(mp);
1474191783Srmacklem	}
1475191783Srmacklem	MNT_IUNLOCK(mp);
1476191783Srmacklem	return (allerror);
1477191783Srmacklem}
1478191783Srmacklem
1479191783Srmacklemstatic int
1480191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
1481191783Srmacklem{
1482191783Srmacklem	struct nfsmount *nmp = VFSTONFS(mp);
1483191783Srmacklem	struct vfsquery vq;
1484191783Srmacklem	int error;
1485191783Srmacklem
1486191783Srmacklem	bzero(&vq, sizeof(vq));
1487191783Srmacklem	switch (op) {
1488191783Srmacklem#if 0
1489191783Srmacklem	case VFS_CTL_NOLOCKS:
1490191783Srmacklem		val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
1491191783Srmacklem 		if (req->oldptr != NULL) {
1492191783Srmacklem 			error = SYSCTL_OUT(req, &val, sizeof(val));
1493191783Srmacklem 			if (error)
1494191783Srmacklem 				return (error);
1495191783Srmacklem 		}
1496191783Srmacklem 		if (req->newptr != NULL) {
1497191783Srmacklem 			error = SYSCTL_IN(req, &val, sizeof(val));
1498191783Srmacklem 			if (error)
1499191783Srmacklem 				return (error);
1500191783Srmacklem			if (val)
1501191783Srmacklem				nmp->nm_flag |= NFSMNT_NOLOCKS;
1502191783Srmacklem			else
1503191783Srmacklem				nmp->nm_flag &= ~NFSMNT_NOLOCKS;
1504191783Srmacklem 		}
1505191783Srmacklem		break;
1506191783Srmacklem#endif
1507191783Srmacklem	case VFS_CTL_QUERY:
1508191783Srmacklem		mtx_lock(&nmp->nm_mtx);
1509191783Srmacklem		if (nmp->nm_state & NFSSTA_TIMEO)
1510191783Srmacklem			vq.vq_flags |= VQ_NOTRESP;
1511191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
1512191783Srmacklem#if 0
1513191783Srmacklem		if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
1514191783Srmacklem		    (nmp->nm_state & NFSSTA_LOCKTIMEO))
1515191783Srmacklem			vq.vq_flags |= VQ_NOTRESPLOCK;
1516191783Srmacklem#endif
1517191783Srmacklem		error = SYSCTL_OUT(req, &vq, sizeof(vq));
1518191783Srmacklem		break;
1519191783Srmacklem 	case VFS_CTL_TIMEO:
1520191783Srmacklem 		if (req->oldptr != NULL) {
1521191783Srmacklem 			error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
1522191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1523191783Srmacklem 			if (error)
1524191783Srmacklem 				return (error);
1525191783Srmacklem 		}
1526191783Srmacklem 		if (req->newptr != NULL) {
1527191783Srmacklem			error = vfs_suser(mp, req->td);
1528191783Srmacklem			if (error)
1529191783Srmacklem				return (error);
1530191783Srmacklem 			error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
1531191783Srmacklem 			    sizeof(nmp->nm_tprintf_initial_delay));
1532191783Srmacklem 			if (error)
1533191783Srmacklem 				return (error);
1534191783Srmacklem 			if (nmp->nm_tprintf_initial_delay < 0)
1535191783Srmacklem 				nmp->nm_tprintf_initial_delay = 0;
1536191783Srmacklem 		}
1537191783Srmacklem		break;
1538191783Srmacklem	default:
1539191783Srmacklem		return (ENOTSUP);
1540191783Srmacklem	}
1541191783Srmacklem	return (0);
1542191783Srmacklem}
1543191783Srmacklem
1544214048Srmacklem/*
1545214048Srmacklem * Extract the information needed by the nlm from the nfs vnode.
1546214048Srmacklem */
1547214048Srmacklemstatic void
1548214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp,
1549216931Srmacklem    struct sockaddr_storage *sp, int *is_v3p, off_t *sizep,
1550216931Srmacklem    struct timeval *timeop)
1551214048Srmacklem{
1552214048Srmacklem	struct nfsmount *nmp;
1553214048Srmacklem	struct nfsnode *np = VTONFS(vp);
1554214048Srmacklem
1555214048Srmacklem	nmp = VFSTONFS(vp->v_mount);
1556214048Srmacklem	if (fhlenp != NULL)
1557214053Srmacklem		*fhlenp = (size_t)np->n_fhp->nfh_len;
1558214048Srmacklem	if (fhp != NULL)
1559214048Srmacklem		bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len);
1560214048Srmacklem	if (sp != NULL)
1561214048Srmacklem		bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp)));
1562214048Srmacklem	if (is_v3p != NULL)
1563214048Srmacklem		*is_v3p = NFS_ISV3(vp);
1564214048Srmacklem	if (sizep != NULL)
1565214048Srmacklem		*sizep = np->n_size;
1566216931Srmacklem	if (timeop != NULL) {
1567216931Srmacklem		timeop->tv_sec = nmp->nm_timeo / NFS_HZ;
1568216931Srmacklem		timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ);
1569216931Srmacklem	}
1570214048Srmacklem}
1571214048Srmacklem
1572