1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993
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 */
33191783Srmacklem
34191783Srmacklem#include <sys/cdefs.h>
35191783Srmacklem__FBSDID("$FreeBSD: stable/11/sys/fs/nfsclient/nfs_clrpcops.c 361236 2020-05-19 01:43:00Z freqlabs $");
36191783Srmacklem
37191783Srmacklem/*
38191783Srmacklem * Rpc op calls, generally called from the vnode op calls or through the
39191783Srmacklem * buffer cache, for NFS v2, 3 and 4.
40191783Srmacklem * These do not normally make any changes to vnode arguments or use
41191783Srmacklem * structures that might change between the VFS variants. The returned
42191783Srmacklem * arguments are all at the end, after the NFSPROC_T *p one.
43191783Srmacklem */
44191783Srmacklem
45229802Srmacklem#include "opt_inet6.h"
46229802Srmacklem
47191783Srmacklem#include <fs/nfs/nfsport.h>
48285066Srmacklem#include <sys/sysctl.h>
49191783Srmacklem
50285066SrmacklemSYSCTL_DECL(_vfs_nfs);
51285066Srmacklem
52285066Srmacklemstatic int	nfsignore_eexist = 0;
53285066SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, ignore_eexist, CTLFLAG_RW,
54285066Srmacklem    &nfsignore_eexist, 0, "NFS ignore EEXIST replies for mkdir/symlink");
55285066Srmacklem
56191783Srmacklem/*
57191783Srmacklem * Global variables
58191783Srmacklem */
59191783Srmacklemextern int nfs_numnfscbd;
60191783Srmacklemextern struct timeval nfsboottime;
61191783Srmacklemextern u_int32_t newnfs_false, newnfs_true;
62191783Srmacklemextern nfstype nfsv34_type[9];
63191783Srmacklemextern int nfsrv_useacl;
64191783Srmacklemextern char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
65240289Srmacklemextern int nfscl_debuglevel;
66191783SrmacklemNFSCLSTATEMUTEX;
67191783Srmacklemint nfstest_outofseq = 0;
68191783Srmacklemint nfscl_assumeposixlocks = 1;
69191783Srmacklemint nfscl_enablecallb = 0;
70191783Srmacklemshort nfsv4_cbport = NFSV4_CBPORT;
71191783Srmacklemint nfstest_openallsetattr = 0;
72191783Srmacklem
73191783Srmacklem#define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
74191783Srmacklem
75244042Srmacklem/*
76244042Srmacklem * nfscl_getsameserver() can return one of three values:
77244042Srmacklem * NFSDSP_USETHISSESSION - Use this session for the DS.
78244042Srmacklem * NFSDSP_SEQTHISSESSION - Use the nfsclds_sequence field of this dsp for new
79244042Srmacklem *     session.
80244042Srmacklem * NFSDSP_NOTFOUND - No matching server was found.
81244042Srmacklem */
82244042Srmacklemenum nfsclds_state {
83244042Srmacklem	NFSDSP_USETHISSESSION = 0,
84244042Srmacklem	NFSDSP_SEQTHISSESSION = 1,
85244042Srmacklem	NFSDSP_NOTFOUND = 2,
86244042Srmacklem};
87244042Srmacklem
88191783Srmacklemstatic int nfsrpc_setattrrpc(vnode_t , struct vattr *, nfsv4stateid_t *,
89191783Srmacklem    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
90191783Srmacklemstatic int nfsrpc_readrpc(vnode_t , struct uio *, struct ucred *,
91191783Srmacklem    nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *, void *);
92222289Srmacklemstatic int nfsrpc_writerpc(vnode_t , struct uio *, int *, int *,
93191783Srmacklem    struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *,
94191783Srmacklem    void *);
95191783Srmacklemstatic int nfsrpc_createv23(vnode_t , char *, int, struct vattr *,
96191783Srmacklem    nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *,
97191783Srmacklem    struct nfsvattr *, struct nfsfh **, int *, int *, void *);
98191783Srmacklemstatic int nfsrpc_createv4(vnode_t , char *, int, struct vattr *,
99191783Srmacklem    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **, struct ucred *,
100191783Srmacklem    NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *,
101191783Srmacklem    int *, void *, int *);
102191783Srmacklemstatic int nfsrpc_locku(struct nfsrv_descript *, struct nfsmount *,
103191783Srmacklem    struct nfscllockowner *, u_int64_t, u_int64_t,
104191783Srmacklem    u_int32_t, struct ucred *, NFSPROC_T *, int);
105191783Srmacklemstatic int nfsrpc_setaclrpc(vnode_t, struct ucred *, NFSPROC_T *,
106191783Srmacklem    struct acl *, nfsv4stateid_t *, void *);
107244042Srmacklemstatic int nfsrpc_getlayout(struct nfsmount *, vnode_t, struct nfsfh *, int,
108244042Srmacklem    uint32_t *, nfsv4stateid_t *, uint64_t, struct nfscllayout **,
109244042Srmacklem    struct ucred *, NFSPROC_T *);
110244042Srmacklemstatic int nfsrpc_fillsa(struct nfsmount *, struct sockaddr_storage *,
111244042Srmacklem    struct nfsclds **, NFSPROC_T *);
112244042Srmacklemstatic void nfscl_initsessionslots(struct nfsclsession *);
113244042Srmacklemstatic int nfscl_doflayoutio(vnode_t, struct uio *, int *, int *, int *,
114244042Srmacklem    nfsv4stateid_t *, int, struct nfscldevinfo *, struct nfscllayout *,
115321029Srmacklem    struct nfsclflayout *, uint64_t, uint64_t, int, struct ucred *,
116321029Srmacklem    NFSPROC_T *);
117244042Srmacklemstatic int nfsrpc_readds(vnode_t, struct uio *, nfsv4stateid_t *, int *,
118244042Srmacklem    struct nfsclds *, uint64_t, int, struct nfsfh *, struct ucred *,
119244042Srmacklem    NFSPROC_T *);
120244042Srmacklemstatic int nfsrpc_writeds(vnode_t, struct uio *, int *, int *,
121244042Srmacklem    nfsv4stateid_t *, struct nfsclds *, uint64_t, int,
122244042Srmacklem    struct nfsfh *, int, struct ucred *, NFSPROC_T *);
123244042Srmacklemstatic enum nfsclds_state nfscl_getsameserver(struct nfsmount *,
124244042Srmacklem    struct nfsclds *, struct nfsclds **);
125244042Srmacklemstatic int nfsrpc_commitds(vnode_t, uint64_t, int, struct nfsclds *,
126321029Srmacklem    struct nfsfh *, struct ucred *, NFSPROC_T *);
127320998Srmacklemstatic void nfsrv_setuplayoutget(struct nfsrv_descript *, int, uint64_t,
128320998Srmacklem    uint64_t, uint64_t, nfsv4stateid_t *, int, int);
129320998Srmacklemstatic int nfsrv_parselayoutget(struct nfsrv_descript *, nfsv4stateid_t *,
130320998Srmacklem    int *, struct nfsclflayouthead *);
131320998Srmacklemstatic int nfsrpc_getopenlayout(struct nfsmount *, vnode_t, u_int8_t *,
132320998Srmacklem    int, uint8_t *, int, uint32_t, struct nfsclopen *, uint8_t *, int,
133320998Srmacklem    struct nfscldeleg **, struct ucred *, NFSPROC_T *);
134320998Srmacklemstatic int nfsrpc_getcreatelayout(vnode_t, char *, int, struct vattr *,
135320998Srmacklem    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **,
136320998Srmacklem    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
137320998Srmacklem    struct nfsfh **, int *, int *, void *, int *);
138320998Srmacklemstatic int nfsrpc_openlayoutrpc(struct nfsmount *, vnode_t, u_int8_t *,
139320998Srmacklem    int, uint8_t *, int, uint32_t, struct nfsclopen *, uint8_t *, int,
140320998Srmacklem    struct nfscldeleg **, nfsv4stateid_t *, int, int, int *,
141320998Srmacklem    struct nfsclflayouthead *, int *, struct ucred *, NFSPROC_T *);
142320998Srmacklemstatic int nfsrpc_createlayout(vnode_t, char *, int, struct vattr *,
143320998Srmacklem    nfsquad_t, int, struct nfsclowner *, struct nfscldeleg **,
144320998Srmacklem    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
145320998Srmacklem    struct nfsfh **, int *, int *, void *, int *, nfsv4stateid_t *,
146320998Srmacklem    int, int, int *, struct nfsclflayouthead *, int *);
147320998Srmacklemstatic int nfsrpc_layoutgetres(struct nfsmount *, vnode_t, uint8_t *,
148320998Srmacklem    int, nfsv4stateid_t *, int, uint32_t *, struct nfscllayout **,
149320998Srmacklem    struct nfsclflayouthead *, int, int *, struct ucred *, NFSPROC_T *);
150191783Srmacklem
151191783Srmacklem/*
152191783Srmacklem * nfs null call from vfs.
153191783Srmacklem */
154361236Sfreqlabsint
155191783Srmacklemnfsrpc_null(vnode_t vp, struct ucred *cred, NFSPROC_T *p)
156191783Srmacklem{
157191783Srmacklem	int error;
158191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
159191783Srmacklem
160191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_NULL, vp);
161191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
162191783Srmacklem	if (nd->nd_repstat && !error)
163191783Srmacklem		error = nd->nd_repstat;
164191783Srmacklem	mbuf_freem(nd->nd_mrep);
165191783Srmacklem	return (error);
166191783Srmacklem}
167191783Srmacklem
168191783Srmacklem/*
169191783Srmacklem * nfs access rpc op.
170191783Srmacklem * For nfs version 3 and 4, use the access rpc to check accessibility. If file
171191783Srmacklem * modes are changed on the server, accesses might still fail later.
172191783Srmacklem */
173361236Sfreqlabsint
174191783Srmacklemnfsrpc_access(vnode_t vp, int acmode, struct ucred *cred,
175191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
176191783Srmacklem{
177191783Srmacklem	int error;
178191783Srmacklem	u_int32_t mode, rmode;
179191783Srmacklem
180191783Srmacklem	if (acmode & VREAD)
181191783Srmacklem		mode = NFSACCESS_READ;
182191783Srmacklem	else
183191783Srmacklem		mode = 0;
184191783Srmacklem	if (vnode_vtype(vp) == VDIR) {
185191783Srmacklem		if (acmode & VWRITE)
186191783Srmacklem			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND |
187191783Srmacklem				 NFSACCESS_DELETE);
188191783Srmacklem		if (acmode & VEXEC)
189191783Srmacklem			mode |= NFSACCESS_LOOKUP;
190191783Srmacklem	} else {
191191783Srmacklem		if (acmode & VWRITE)
192191783Srmacklem			mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
193191783Srmacklem		if (acmode & VEXEC)
194191783Srmacklem			mode |= NFSACCESS_EXECUTE;
195191783Srmacklem	}
196191783Srmacklem
197191783Srmacklem	/*
198191783Srmacklem	 * Now, just call nfsrpc_accessrpc() to do the actual RPC.
199191783Srmacklem	 */
200191783Srmacklem	error = nfsrpc_accessrpc(vp, mode, cred, p, nap, attrflagp, &rmode,
201191783Srmacklem	    NULL);
202191783Srmacklem
203191783Srmacklem	/*
204191783Srmacklem	 * The NFS V3 spec does not clarify whether or not
205191783Srmacklem	 * the returned access bits can be a superset of
206191783Srmacklem	 * the ones requested, so...
207191783Srmacklem	 */
208191783Srmacklem	if (!error && (rmode & mode) != mode)
209191783Srmacklem		error = EACCES;
210191783Srmacklem	return (error);
211191783Srmacklem}
212191783Srmacklem
213191783Srmacklem/*
214191783Srmacklem * The actual rpc, separated out for Darwin.
215191783Srmacklem */
216361236Sfreqlabsint
217191783Srmacklemnfsrpc_accessrpc(vnode_t vp, u_int32_t mode, struct ucred *cred,
218191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, u_int32_t *rmodep,
219191783Srmacklem    void *stuff)
220191783Srmacklem{
221191783Srmacklem	u_int32_t *tl;
222191783Srmacklem	u_int32_t supported, rmode;
223191783Srmacklem	int error;
224191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
225191783Srmacklem	nfsattrbit_t attrbits;
226191783Srmacklem
227191783Srmacklem	*attrflagp = 0;
228191783Srmacklem	supported = mode;
229191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_ACCESS, vp);
230191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
231191783Srmacklem	*tl = txdr_unsigned(mode);
232191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
233191783Srmacklem		/*
234191783Srmacklem		 * And do a Getattr op.
235191783Srmacklem		 */
236191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
237191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
238191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
239191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
240191783Srmacklem	}
241191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
242191783Srmacklem	if (error)
243191783Srmacklem		return (error);
244191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
245191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
246191783Srmacklem		if (error)
247191783Srmacklem			goto nfsmout;
248191783Srmacklem	}
249191783Srmacklem	if (!nd->nd_repstat) {
250191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
251191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
252191783Srmacklem			supported = fxdr_unsigned(u_int32_t, *tl++);
253191783Srmacklem		} else {
254191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
255191783Srmacklem		}
256191783Srmacklem		rmode = fxdr_unsigned(u_int32_t, *tl);
257191783Srmacklem		if (nd->nd_flag & ND_NFSV4)
258191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
259191783Srmacklem
260191783Srmacklem		/*
261191783Srmacklem		 * It's not obvious what should be done about
262191783Srmacklem		 * unsupported access modes. For now, be paranoid
263191783Srmacklem		 * and clear the unsupported ones.
264191783Srmacklem		 */
265191783Srmacklem		rmode &= supported;
266191783Srmacklem		*rmodep = rmode;
267191783Srmacklem	} else
268191783Srmacklem		error = nd->nd_repstat;
269191783Srmacklemnfsmout:
270191783Srmacklem	mbuf_freem(nd->nd_mrep);
271191783Srmacklem	return (error);
272191783Srmacklem}
273191783Srmacklem
274191783Srmacklem/*
275191783Srmacklem * nfs open rpc
276191783Srmacklem */
277361236Sfreqlabsint
278191783Srmacklemnfsrpc_open(vnode_t vp, int amode, struct ucred *cred, NFSPROC_T *p)
279191783Srmacklem{
280191783Srmacklem	struct nfsclopen *op;
281191783Srmacklem	struct nfscldeleg *dp;
282191783Srmacklem	struct nfsfh *nfhp;
283191783Srmacklem	struct nfsnode *np = VTONFS(vp);
284191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
285191783Srmacklem	u_int32_t mode, clidrev;
286191783Srmacklem	int ret, newone, error, expireret = 0, retrycnt;
287191783Srmacklem
288191783Srmacklem	/*
289191783Srmacklem	 * For NFSv4, Open Ops are only done on Regular Files.
290191783Srmacklem	 */
291191783Srmacklem	if (vnode_vtype(vp) != VREG)
292191783Srmacklem		return (0);
293191783Srmacklem	mode = 0;
294191783Srmacklem	if (amode & FREAD)
295191783Srmacklem		mode |= NFSV4OPEN_ACCESSREAD;
296191783Srmacklem	if (amode & FWRITE)
297191783Srmacklem		mode |= NFSV4OPEN_ACCESSWRITE;
298191783Srmacklem	nfhp = np->n_fhp;
299191783Srmacklem
300191783Srmacklem	retrycnt = 0;
301191783Srmacklem#ifdef notdef
302191783Srmacklem{ char name[100]; int namel;
303191783Srmacklemnamel = (np->n_v4->n4_namelen < 100) ? np->n_v4->n4_namelen : 99;
304191783Srmacklembcopy(NFS4NODENAME(np->n_v4), name, namel);
305191783Srmacklemname[namel] = '\0';
306191783Srmacklemprintf("rpcopen p=0x%x name=%s",p->p_pid,name);
307191783Srmacklemif (nfhp->nfh_len > 0) printf(" fh=0x%x\n",nfhp->nfh_fh[12]);
308191783Srmacklemelse printf(" fhl=0\n");
309191783Srmacklem}
310191783Srmacklem#endif
311191783Srmacklem	do {
312191783Srmacklem	    dp = NULL;
313191783Srmacklem	    error = nfscl_open(vp, nfhp->nfh_fh, nfhp->nfh_len, mode, 1,
314191783Srmacklem		cred, p, NULL, &op, &newone, &ret, 1);
315191783Srmacklem	    if (error) {
316191783Srmacklem		return (error);
317191783Srmacklem	    }
318191783Srmacklem	    if (nmp->nm_clp != NULL)
319191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
320191783Srmacklem	    else
321191783Srmacklem		clidrev = 0;
322191783Srmacklem	    if (ret == NFSCLOPEN_DOOPEN) {
323191783Srmacklem		if (np->n_v4 != NULL) {
324320998Srmacklem			/*
325320998Srmacklem			 * For the first attempt, try and get a layout, if
326320998Srmacklem			 * pNFS is enabled for the mount.
327320998Srmacklem			 */
328320998Srmacklem			if (!NFSHASPNFS(nmp) || nfscl_enablecallb == 0 ||
329320998Srmacklem			    nfs_numnfscbd == 0 ||
330320998Srmacklem			    (np->n_flag & NNOLAYOUT) != 0 || retrycnt > 0)
331320998Srmacklem				error = nfsrpc_openrpc(nmp, vp,
332320998Srmacklem				    np->n_v4->n4_data,
333320998Srmacklem				    np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
334320998Srmacklem				    np->n_fhp->nfh_len, mode, op,
335320998Srmacklem				    NFS4NODENAME(np->n_v4),
336320998Srmacklem				    np->n_v4->n4_namelen,
337320998Srmacklem				    &dp, 0, 0x0, cred, p, 0, 0);
338320998Srmacklem			else
339320998Srmacklem				error = nfsrpc_getopenlayout(nmp, vp,
340320998Srmacklem				    np->n_v4->n4_data,
341320998Srmacklem				    np->n_v4->n4_fhlen, np->n_fhp->nfh_fh,
342320998Srmacklem				    np->n_fhp->nfh_len, mode, op,
343320998Srmacklem				    NFS4NODENAME(np->n_v4),
344320998Srmacklem				    np->n_v4->n4_namelen, &dp, cred, p);
345191783Srmacklem			if (dp != NULL) {
346191783Srmacklem#ifdef APPLE
347191783Srmacklem				OSBitAndAtomic((int32_t)~NDELEGMOD, (UInt32 *)&np->n_flag);
348191783Srmacklem#else
349191783Srmacklem				NFSLOCKNODE(np);
350191783Srmacklem				np->n_flag &= ~NDELEGMOD;
351210034Srmacklem				/*
352210034Srmacklem				 * Invalidate the attribute cache, so that
353210034Srmacklem				 * attributes that pre-date the issue of a
354210034Srmacklem				 * delegation are not cached, since the
355210034Srmacklem				 * cached attributes will remain valid while
356210034Srmacklem				 * the delegation is held.
357210034Srmacklem				 */
358210034Srmacklem				NFSINVALATTRCACHE(np);
359191783Srmacklem				NFSUNLOCKNODE(np);
360191783Srmacklem#endif
361191783Srmacklem				(void) nfscl_deleg(nmp->nm_mountp,
362191783Srmacklem				    op->nfso_own->nfsow_clp,
363191783Srmacklem				    nfhp->nfh_fh, nfhp->nfh_len, cred, p, &dp);
364191783Srmacklem			}
365191783Srmacklem		} else {
366191783Srmacklem			error = EIO;
367191783Srmacklem		}
368191783Srmacklem		newnfs_copyincred(cred, &op->nfso_cred);
369206688Srmacklem	    } else if (ret == NFSCLOPEN_SETCRED)
370206688Srmacklem		/*
371206688Srmacklem		 * This is a new local open on a delegation. It needs
372206688Srmacklem		 * to have credentials so that an open can be done
373206688Srmacklem		 * against the server during recovery.
374206688Srmacklem		 */
375206688Srmacklem		newnfs_copyincred(cred, &op->nfso_cred);
376191783Srmacklem
377191783Srmacklem	    /*
378191783Srmacklem	     * nfso_opencnt is the count of how many VOP_OPEN()s have
379191783Srmacklem	     * been done on this Open successfully and a VOP_CLOSE()
380191783Srmacklem	     * is expected for each of these.
381191783Srmacklem	     * If error is non-zero, don't increment it, since the Open
382191783Srmacklem	     * hasn't succeeded yet.
383191783Srmacklem	     */
384191783Srmacklem	    if (!error)
385191783Srmacklem		op->nfso_opencnt++;
386317520Srmacklem	    nfscl_openrelease(nmp, op, error, newone);
387191783Srmacklem	    if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
388244042Srmacklem		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
389244042Srmacklem		error == NFSERR_BADSESSION) {
390207170Srmacklem		(void) nfs_catnap(PZERO, error, "nfs_open");
391191783Srmacklem	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
392191783Srmacklem		&& clidrev != 0) {
393191783Srmacklem		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
394191783Srmacklem		retrycnt++;
395191783Srmacklem	    }
396191783Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
397191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
398244042Srmacklem	    error == NFSERR_BADSESSION ||
399191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
400191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
401191783Srmacklem	if (error && retrycnt >= 4)
402191783Srmacklem		error = EIO;
403191783Srmacklem	return (error);
404191783Srmacklem}
405191783Srmacklem
406191783Srmacklem/*
407191783Srmacklem * the actual open rpc
408191783Srmacklem */
409361236Sfreqlabsint
410191783Srmacklemnfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
411191783Srmacklem    u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
412191783Srmacklem    u_int8_t *name, int namelen, struct nfscldeleg **dpp,
413191783Srmacklem    int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p,
414191783Srmacklem    int syscred, int recursed)
415191783Srmacklem{
416191783Srmacklem	u_int32_t *tl;
417191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
418191783Srmacklem	struct nfscldeleg *dp, *ndp = NULL;
419191783Srmacklem	struct nfsvattr nfsva;
420191783Srmacklem	u_int32_t rflags, deleg;
421191783Srmacklem	nfsattrbit_t attrbits;
422191783Srmacklem	int error, ret, acesize, limitby;
423317393Srmacklem	struct nfsclsession *tsep;
424191783Srmacklem
425191783Srmacklem	dp = *dpp;
426191783Srmacklem	*dpp = NULL;
427244042Srmacklem	nfscl_reqstart(nd, NFSPROC_OPEN, nmp, nfhp, fhlen, NULL, NULL);
428191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
429191783Srmacklem	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
430191783Srmacklem	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
431191783Srmacklem	*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
432317393Srmacklem	tsep = nfsmnt_mdssession(nmp);
433317393Srmacklem	*tl++ = tsep->nfsess_clientid.lval[0];
434317393Srmacklem	*tl = tsep->nfsess_clientid.lval[1];
435191783Srmacklem	(void) nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
436191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
437191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
438191783Srmacklem	if (reclaim) {
439191783Srmacklem		*tl = txdr_unsigned(NFSV4OPEN_CLAIMPREVIOUS);
440191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
441191783Srmacklem		*tl = txdr_unsigned(delegtype);
442191783Srmacklem	} else {
443191783Srmacklem		if (dp != NULL) {
444191783Srmacklem			*tl = txdr_unsigned(NFSV4OPEN_CLAIMDELEGATECUR);
445191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
446244042Srmacklem			if (NFSHASNFSV4N(nmp))
447244042Srmacklem				*tl++ = 0;
448244042Srmacklem			else
449244042Srmacklem				*tl++ = dp->nfsdl_stateid.seqid;
450191783Srmacklem			*tl++ = dp->nfsdl_stateid.other[0];
451191783Srmacklem			*tl++ = dp->nfsdl_stateid.other[1];
452191783Srmacklem			*tl = dp->nfsdl_stateid.other[2];
453191783Srmacklem		} else {
454191783Srmacklem			*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
455191783Srmacklem		}
456191783Srmacklem		(void) nfsm_strtom(nd, name, namelen);
457191783Srmacklem	}
458191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
459191783Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
460191783Srmacklem	NFSZERO_ATTRBIT(&attrbits);
461191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
462191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY);
463191783Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
464191783Srmacklem	if (syscred)
465191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
466191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
467244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
468191783Srmacklem	if (error)
469191783Srmacklem		return (error);
470191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
471191783Srmacklem	if (!nd->nd_repstat) {
472191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
473191783Srmacklem		    6 * NFSX_UNSIGNED);
474191783Srmacklem		op->nfso_stateid.seqid = *tl++;
475191783Srmacklem		op->nfso_stateid.other[0] = *tl++;
476191783Srmacklem		op->nfso_stateid.other[1] = *tl++;
477191783Srmacklem		op->nfso_stateid.other[2] = *tl;
478191783Srmacklem		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
479191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
480191783Srmacklem		if (error)
481191783Srmacklem			goto nfsmout;
482191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
483191783Srmacklem		deleg = fxdr_unsigned(u_int32_t, *tl);
484191783Srmacklem		if (deleg == NFSV4OPEN_DELEGATEREAD ||
485191783Srmacklem		    deleg == NFSV4OPEN_DELEGATEWRITE) {
486191783Srmacklem			if (!(op->nfso_own->nfsow_clp->nfsc_flags &
487191783Srmacklem			      NFSCLFLAGS_FIRSTDELEG))
488191783Srmacklem				op->nfso_own->nfsow_clp->nfsc_flags |=
489191783Srmacklem				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
490191783Srmacklem			MALLOC(ndp, struct nfscldeleg *,
491191783Srmacklem			    sizeof (struct nfscldeleg) + newfhlen,
492191783Srmacklem			    M_NFSCLDELEG, M_WAITOK);
493191783Srmacklem			LIST_INIT(&ndp->nfsdl_owner);
494191783Srmacklem			LIST_INIT(&ndp->nfsdl_lock);
495191783Srmacklem			ndp->nfsdl_clp = op->nfso_own->nfsow_clp;
496191783Srmacklem			ndp->nfsdl_fhlen = newfhlen;
497191783Srmacklem			NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen);
498191783Srmacklem			newnfs_copyincred(cred, &ndp->nfsdl_cred);
499191783Srmacklem			nfscl_lockinit(&ndp->nfsdl_rwlock);
500191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
501191783Srmacklem			    NFSX_UNSIGNED);
502191783Srmacklem			ndp->nfsdl_stateid.seqid = *tl++;
503191783Srmacklem			ndp->nfsdl_stateid.other[0] = *tl++;
504191783Srmacklem			ndp->nfsdl_stateid.other[1] = *tl++;
505191783Srmacklem			ndp->nfsdl_stateid.other[2] = *tl++;
506191783Srmacklem			ret = fxdr_unsigned(int, *tl);
507191783Srmacklem			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
508191783Srmacklem				ndp->nfsdl_flags = NFSCLDL_WRITE;
509191783Srmacklem				/*
510191783Srmacklem				 * Indicates how much the file can grow.
511191783Srmacklem				 */
512191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *,
513191783Srmacklem				    3 * NFSX_UNSIGNED);
514191783Srmacklem				limitby = fxdr_unsigned(int, *tl++);
515191783Srmacklem				switch (limitby) {
516191783Srmacklem				case NFSV4OPEN_LIMITSIZE:
517191783Srmacklem					ndp->nfsdl_sizelimit = fxdr_hyper(tl);
518191783Srmacklem					break;
519191783Srmacklem				case NFSV4OPEN_LIMITBLOCKS:
520191783Srmacklem					ndp->nfsdl_sizelimit =
521191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl++);
522191783Srmacklem					ndp->nfsdl_sizelimit *=
523191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl);
524191783Srmacklem					break;
525191783Srmacklem				default:
526191783Srmacklem					error = NFSERR_BADXDR;
527191783Srmacklem					goto nfsmout;
528297793Spfg				}
529191783Srmacklem			} else {
530191783Srmacklem				ndp->nfsdl_flags = NFSCLDL_READ;
531191783Srmacklem			}
532191783Srmacklem			if (ret)
533191783Srmacklem				ndp->nfsdl_flags |= NFSCLDL_RECALL;
534191783Srmacklem			error = nfsrv_dissectace(nd, &ndp->nfsdl_ace, &ret,
535191783Srmacklem			    &acesize, p);
536191783Srmacklem			if (error)
537191783Srmacklem				goto nfsmout;
538191783Srmacklem		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
539191783Srmacklem			error = NFSERR_BADXDR;
540191783Srmacklem			goto nfsmout;
541191783Srmacklem		}
542191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
543191783Srmacklem		error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
544191783Srmacklem		    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
545191783Srmacklem		    NULL, NULL, NULL, p, cred);
546191783Srmacklem		if (error)
547191783Srmacklem			goto nfsmout;
548191783Srmacklem		if (ndp != NULL) {
549191783Srmacklem			ndp->nfsdl_change = nfsva.na_filerev;
550191783Srmacklem			ndp->nfsdl_modtime = nfsva.na_mtime;
551191783Srmacklem			ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
552191783Srmacklem		}
553191783Srmacklem		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM)) {
554191783Srmacklem		    do {
555191783Srmacklem			ret = nfsrpc_openconfirm(vp, newfhp, newfhlen, op,
556191783Srmacklem			    cred, p);
557191783Srmacklem			if (ret == NFSERR_DELAY)
558207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_open");
559191783Srmacklem		    } while (ret == NFSERR_DELAY);
560191783Srmacklem		    error = ret;
561191783Srmacklem		}
562191783Srmacklem		if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) ||
563191783Srmacklem		    nfscl_assumeposixlocks)
564191783Srmacklem		    op->nfso_posixlock = 1;
565191783Srmacklem		else
566191783Srmacklem		    op->nfso_posixlock = 0;
567191783Srmacklem
568191783Srmacklem		/*
569191783Srmacklem		 * If the server is handing out delegations, but we didn't
570191783Srmacklem		 * get one because an OpenConfirm was required, try the
571191783Srmacklem		 * Open again, to get a delegation. This is a harmless no-op,
572191783Srmacklem		 * from a server's point of view.
573191783Srmacklem		 */
574191783Srmacklem		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM) &&
575191783Srmacklem		    (op->nfso_own->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG)
576191783Srmacklem		    && !error && dp == NULL && ndp == NULL && !recursed) {
577191783Srmacklem		    do {
578191783Srmacklem			ret = nfsrpc_openrpc(nmp, vp, nfhp, fhlen, newfhp,
579191783Srmacklem			    newfhlen, mode, op, name, namelen, &ndp, 0, 0x0,
580191783Srmacklem			    cred, p, syscred, 1);
581191783Srmacklem			if (ret == NFSERR_DELAY)
582207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_open2");
583191783Srmacklem		    } while (ret == NFSERR_DELAY);
584191783Srmacklem		    if (ret) {
585281770Spfg			if (ndp != NULL) {
586191783Srmacklem				FREE((caddr_t)ndp, M_NFSCLDELEG);
587281770Spfg				ndp = NULL;
588281770Spfg			}
589191783Srmacklem			if (ret == NFSERR_STALECLIENTID ||
590244042Srmacklem			    ret == NFSERR_STALEDONTRECOVER ||
591244042Srmacklem			    ret == NFSERR_BADSESSION)
592191783Srmacklem				error = ret;
593191783Srmacklem		    }
594191783Srmacklem		}
595191783Srmacklem	}
596191783Srmacklem	if (nd->nd_repstat != 0 && error == 0)
597191783Srmacklem		error = nd->nd_repstat;
598317393Srmacklem	if (error == NFSERR_STALECLIENTID)
599191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
600191783Srmacklemnfsmout:
601191783Srmacklem	if (!error)
602191783Srmacklem		*dpp = ndp;
603191783Srmacklem	else if (ndp != NULL)
604191783Srmacklem		FREE((caddr_t)ndp, M_NFSCLDELEG);
605191783Srmacklem	mbuf_freem(nd->nd_mrep);
606191783Srmacklem	return (error);
607191783Srmacklem}
608191783Srmacklem
609191783Srmacklem/*
610191783Srmacklem * open downgrade rpc
611191783Srmacklem */
612361236Sfreqlabsint
613191783Srmacklemnfsrpc_opendowngrade(vnode_t vp, u_int32_t mode, struct nfsclopen *op,
614191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
615191783Srmacklem{
616191783Srmacklem	u_int32_t *tl;
617191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
618191783Srmacklem	int error;
619191783Srmacklem
620191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_OPENDOWNGRADE, vp);
621191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
622244042Srmacklem	if (NFSHASNFSV4N(VFSTONFS(vnode_mount(vp))))
623244042Srmacklem		*tl++ = 0;
624244042Srmacklem	else
625244042Srmacklem		*tl++ = op->nfso_stateid.seqid;
626191783Srmacklem	*tl++ = op->nfso_stateid.other[0];
627191783Srmacklem	*tl++ = op->nfso_stateid.other[1];
628191783Srmacklem	*tl++ = op->nfso_stateid.other[2];
629191783Srmacklem	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
630191783Srmacklem	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
631191783Srmacklem	*tl = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
632191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
633191783Srmacklem	if (error)
634191783Srmacklem		return (error);
635191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
636191783Srmacklem	if (!nd->nd_repstat) {
637191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
638191783Srmacklem		op->nfso_stateid.seqid = *tl++;
639191783Srmacklem		op->nfso_stateid.other[0] = *tl++;
640191783Srmacklem		op->nfso_stateid.other[1] = *tl++;
641191783Srmacklem		op->nfso_stateid.other[2] = *tl;
642191783Srmacklem	}
643191783Srmacklem	if (nd->nd_repstat && error == 0)
644191783Srmacklem		error = nd->nd_repstat;
645317393Srmacklem	if (error == NFSERR_STALESTATEID)
646191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
647191783Srmacklemnfsmout:
648191783Srmacklem	mbuf_freem(nd->nd_mrep);
649191783Srmacklem	return (error);
650191783Srmacklem}
651191783Srmacklem
652191783Srmacklem/*
653191783Srmacklem * V4 Close operation.
654191783Srmacklem */
655361236Sfreqlabsint
656192337Srmacklemnfsrpc_close(vnode_t vp, int doclose, NFSPROC_T *p)
657191783Srmacklem{
658191783Srmacklem	struct nfsclclient *clp;
659191783Srmacklem	int error;
660191783Srmacklem
661191783Srmacklem	if (vnode_vtype(vp) != VREG)
662191783Srmacklem		return (0);
663192337Srmacklem	if (doclose)
664195510Srmacklem		error = nfscl_doclose(vp, &clp, p);
665192337Srmacklem	else
666195510Srmacklem		error = nfscl_getclose(vp, &clp);
667191783Srmacklem	if (error)
668191783Srmacklem		return (error);
669191783Srmacklem
670191783Srmacklem	nfscl_clientrelease(clp);
671191783Srmacklem	return (0);
672191783Srmacklem}
673191783Srmacklem
674191783Srmacklem/*
675195510Srmacklem * Close the open.
676191783Srmacklem */
677361236Sfreqlabsvoid
678195510Srmacklemnfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p)
679191783Srmacklem{
680191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
681223747Srmacklem	struct nfscllockowner *lp, *nlp;
682191783Srmacklem	struct nfscllock *lop, *nlop;
683191783Srmacklem	struct ucred *tcred;
684191783Srmacklem	u_int64_t off = 0, len = 0;
685191783Srmacklem	u_int32_t type = NFSV4LOCKT_READ;
686195510Srmacklem	int error, do_unlock, trycnt;
687191783Srmacklem
688191783Srmacklem	tcred = newnfs_getcred();
689195510Srmacklem	newnfs_copycred(&op->nfso_cred, tcred);
690195510Srmacklem	/*
691195510Srmacklem	 * (Theoretically this could be done in the same
692195510Srmacklem	 *  compound as the close, but having multiple
693195510Srmacklem	 *  sequenced Ops in the same compound might be
694195510Srmacklem	 *  too scary for some servers.)
695195510Srmacklem	 */
696195510Srmacklem	if (op->nfso_posixlock) {
697195510Srmacklem		off = 0;
698195510Srmacklem		len = NFS64BITSSET;
699195510Srmacklem		type = NFSV4LOCKT_READ;
700195510Srmacklem	}
701195510Srmacklem
702195510Srmacklem	/*
703195510Srmacklem	 * Since this function is only called from VOP_INACTIVE(), no
704195510Srmacklem	 * other thread will be manipulating this Open. As such, the
705195510Srmacklem	 * lock lists are not being changed by other threads, so it should
706195510Srmacklem	 * be safe to do this without locking.
707195510Srmacklem	 */
708195510Srmacklem	LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
709195510Srmacklem		do_unlock = 1;
710195510Srmacklem		LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
711191783Srmacklem			if (op->nfso_posixlock == 0) {
712195510Srmacklem				off = lop->nfslo_first;
713195510Srmacklem				len = lop->nfslo_end - lop->nfslo_first;
714195510Srmacklem				if (lop->nfslo_type == F_WRLCK)
715195510Srmacklem					type = NFSV4LOCKT_WRITE;
716195510Srmacklem				else
717195510Srmacklem					type = NFSV4LOCKT_READ;
718191783Srmacklem			}
719195510Srmacklem			if (do_unlock) {
720195510Srmacklem				trycnt = 0;
721195510Srmacklem				do {
722195510Srmacklem					error = nfsrpc_locku(nd, nmp, lp, off,
723195510Srmacklem					    len, type, tcred, p, 0);
724195510Srmacklem					if ((nd->nd_repstat == NFSERR_GRACE ||
725195510Srmacklem					    nd->nd_repstat == NFSERR_DELAY) &&
726195510Srmacklem					    error == 0)
727195510Srmacklem						(void) nfs_catnap(PZERO,
728207170Srmacklem						    (int)nd->nd_repstat,
729195510Srmacklem						    "nfs_close");
730195510Srmacklem				} while ((nd->nd_repstat == NFSERR_GRACE ||
731195510Srmacklem				    nd->nd_repstat == NFSERR_DELAY) &&
732195510Srmacklem				    error == 0 && trycnt++ < 5);
733195510Srmacklem				if (op->nfso_posixlock)
734195510Srmacklem					do_unlock = 0;
735191783Srmacklem			}
736191783Srmacklem			nfscl_freelock(lop, 0);
737191783Srmacklem		}
738223747Srmacklem		/*
739223747Srmacklem		 * Do a ReleaseLockOwner.
740223747Srmacklem		 * The lock owner name nfsl_owner may be used by other opens for
741223747Srmacklem		 * other files but the lock_owner4 name that nfsrpc_rellockown()
742223747Srmacklem		 * puts on the wire has the file handle for this file appended
743223747Srmacklem		 * to it, so it can be done now.
744223747Srmacklem		 */
745227760Srmacklem		(void)nfsrpc_rellockown(nmp, lp, lp->nfsl_open->nfso_fh,
746227760Srmacklem		    lp->nfsl_open->nfso_fhlen, tcred, p);
747195510Srmacklem	}
748191783Srmacklem
749195510Srmacklem	/*
750195510Srmacklem	 * There could be other Opens for different files on the same
751195510Srmacklem	 * OpenOwner, so locking is required.
752195510Srmacklem	 */
753195510Srmacklem	NFSLOCKCLSTATE();
754195510Srmacklem	nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
755195510Srmacklem	NFSUNLOCKCLSTATE();
756195510Srmacklem	do {
757195510Srmacklem		error = nfscl_tryclose(op, tcred, nmp, p);
758195510Srmacklem		if (error == NFSERR_GRACE)
759207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_close");
760195510Srmacklem	} while (error == NFSERR_GRACE);
761195510Srmacklem	NFSLOCKCLSTATE();
762195510Srmacklem	nfscl_lockunlock(&op->nfso_own->nfsow_rwlock);
763195510Srmacklem
764223747Srmacklem	LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp)
765223747Srmacklem		nfscl_freelockowner(lp, 0);
766195510Srmacklem	nfscl_freeopen(op, 0);
767195510Srmacklem	NFSUNLOCKCLSTATE();
768191783Srmacklem	NFSFREECRED(tcred);
769191783Srmacklem}
770191783Srmacklem
771191783Srmacklem/*
772191783Srmacklem * The actual Close RPC.
773191783Srmacklem */
774361236Sfreqlabsint
775191783Srmacklemnfsrpc_closerpc(struct nfsrv_descript *nd, struct nfsmount *nmp,
776191783Srmacklem    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p,
777191783Srmacklem    int syscred)
778191783Srmacklem{
779191783Srmacklem	u_int32_t *tl;
780191783Srmacklem	int error;
781191783Srmacklem
782191783Srmacklem	nfscl_reqstart(nd, NFSPROC_CLOSE, nmp, op->nfso_fh,
783244042Srmacklem	    op->nfso_fhlen, NULL, NULL);
784191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
785191783Srmacklem	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
786244042Srmacklem	if (NFSHASNFSV4N(nmp))
787244042Srmacklem		*tl++ = 0;
788244042Srmacklem	else
789244042Srmacklem		*tl++ = op->nfso_stateid.seqid;
790191783Srmacklem	*tl++ = op->nfso_stateid.other[0];
791191783Srmacklem	*tl++ = op->nfso_stateid.other[1];
792191783Srmacklem	*tl = op->nfso_stateid.other[2];
793191783Srmacklem	if (syscred)
794191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
795191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
796244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
797191783Srmacklem	if (error)
798191783Srmacklem		return (error);
799191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
800191783Srmacklem	if (nd->nd_repstat == 0)
801191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
802191783Srmacklem	error = nd->nd_repstat;
803317393Srmacklem	if (error == NFSERR_STALESTATEID)
804191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
805191783Srmacklemnfsmout:
806191783Srmacklem	mbuf_freem(nd->nd_mrep);
807191783Srmacklem	return (error);
808191783Srmacklem}
809191783Srmacklem
810191783Srmacklem/*
811191783Srmacklem * V4 Open Confirm RPC.
812191783Srmacklem */
813361236Sfreqlabsint
814191783Srmacklemnfsrpc_openconfirm(vnode_t vp, u_int8_t *nfhp, int fhlen,
815191783Srmacklem    struct nfsclopen *op, struct ucred *cred, NFSPROC_T *p)
816191783Srmacklem{
817191783Srmacklem	u_int32_t *tl;
818191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
819244042Srmacklem	struct nfsmount *nmp;
820191783Srmacklem	int error;
821191783Srmacklem
822244042Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
823244042Srmacklem	if (NFSHASNFSV4N(nmp))
824244042Srmacklem		return (0);		/* No confirmation for NFSv4.1. */
825244042Srmacklem	nfscl_reqstart(nd, NFSPROC_OPENCONFIRM, nmp, nfhp, fhlen, NULL, NULL);
826191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
827191783Srmacklem	*tl++ = op->nfso_stateid.seqid;
828191783Srmacklem	*tl++ = op->nfso_stateid.other[0];
829191783Srmacklem	*tl++ = op->nfso_stateid.other[1];
830191783Srmacklem	*tl++ = op->nfso_stateid.other[2];
831191783Srmacklem	*tl = txdr_unsigned(op->nfso_own->nfsow_seqid);
832191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
833191783Srmacklem	if (error)
834191783Srmacklem		return (error);
835191783Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
836191783Srmacklem	if (!nd->nd_repstat) {
837191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
838191783Srmacklem		op->nfso_stateid.seqid = *tl++;
839191783Srmacklem		op->nfso_stateid.other[0] = *tl++;
840191783Srmacklem		op->nfso_stateid.other[1] = *tl++;
841191783Srmacklem		op->nfso_stateid.other[2] = *tl;
842191783Srmacklem	}
843191783Srmacklem	error = nd->nd_repstat;
844317393Srmacklem	if (error == NFSERR_STALESTATEID)
845191783Srmacklem		nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
846191783Srmacklemnfsmout:
847191783Srmacklem	mbuf_freem(nd->nd_mrep);
848191783Srmacklem	return (error);
849191783Srmacklem}
850191783Srmacklem
851191783Srmacklem/*
852191783Srmacklem * Do the setclientid and setclientid confirm RPCs. Called from nfs_statfs()
853191783Srmacklem * when a mount has just occurred and when the server replies NFSERR_EXPIRED.
854191783Srmacklem */
855361236Sfreqlabsint
856244042Srmacklemnfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
857191783Srmacklem    struct ucred *cred, NFSPROC_T *p)
858191783Srmacklem{
859191783Srmacklem	u_int32_t *tl;
860191783Srmacklem	struct nfsrv_descript nfsd;
861191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
862191783Srmacklem	nfsattrbit_t attrbits;
863191783Srmacklem	u_int8_t *cp = NULL, *cp2, addr[INET6_ADDRSTRLEN + 9];
864191783Srmacklem	u_short port;
865195825Srmacklem	int error, isinet6 = 0, callblen;
866191783Srmacklem	nfsquad_t confirm;
867191783Srmacklem	u_int32_t lease;
868191783Srmacklem	static u_int32_t rev = 0;
869317393Srmacklem	struct nfsclds *dsp;
870294084Smelifaro	struct in6_addr a6;
871317393Srmacklem	struct nfsclsession *tsep;
872191783Srmacklem
873191783Srmacklem	if (nfsboottime.tv_sec == 0)
874191783Srmacklem		NFSSETBOOTTIME(nfsboottime);
875244042Srmacklem	clp->nfsc_rev = rev++;
876244042Srmacklem	if (NFSHASNFSV4N(nmp)) {
877317393Srmacklem		/*
878317393Srmacklem		 * Either there was no previous session or the
879317393Srmacklem		 * previous session has failed, so...
880317393Srmacklem		 * do an ExchangeID followed by the CreateSession.
881317393Srmacklem		 */
882244042Srmacklem		error = nfsrpc_exchangeid(nmp, clp, &nmp->nm_sockreq,
883244042Srmacklem		    NFSV4EXCH_USEPNFSMDS | NFSV4EXCH_USENONPNFS, &dsp, cred, p);
884244042Srmacklem		NFSCL_DEBUG(1, "aft exch=%d\n", error);
885317393Srmacklem		if (error == 0)
886244042Srmacklem			error = nfsrpc_createsession(nmp, &dsp->nfsclds_sess,
887244042Srmacklem			    &nmp->nm_sockreq,
888244042Srmacklem			    dsp->nfsclds_sess.nfsess_sequenceid, 1, cred, p);
889317393Srmacklem		if (error == 0) {
890317393Srmacklem			NFSLOCKMNT(nmp);
891317393Srmacklem			/*
892317393Srmacklem			 * The old sessions cannot be safely free'd
893317393Srmacklem			 * here, since they may still be used by
894317393Srmacklem			 * in-progress RPCs.
895317393Srmacklem			 */
896317393Srmacklem			tsep = NULL;
897317393Srmacklem			if (TAILQ_FIRST(&nmp->nm_sess) != NULL)
898317393Srmacklem				tsep = NFSMNT_MDSSESSION(nmp);
899317393Srmacklem			TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp,
900317393Srmacklem			    nfsclds_list);
901317393Srmacklem			/*
902317393Srmacklem			 * Wake up RPCs waiting for a slot on the
903317393Srmacklem			 * old session. These will then fail with
904317393Srmacklem			 * NFSERR_BADSESSION and be retried with the
905317393Srmacklem			 * new session by nfsv4_setsequence().
906317393Srmacklem			 * Also wakeup() processes waiting for the
907317393Srmacklem			 * new session.
908317393Srmacklem			 */
909317393Srmacklem			if (tsep != NULL)
910317393Srmacklem				wakeup(&tsep->nfsess_slots);
911317393Srmacklem			wakeup(&nmp->nm_sess);
912317393Srmacklem			NFSUNLOCKMNT(nmp);
913317393Srmacklem		} else
914317393Srmacklem			nfscl_freenfsclds(dsp);
915317393Srmacklem		NFSCL_DEBUG(1, "aft createsess=%d\n", error);
916244042Srmacklem		if (error == 0 && reclaim == 0) {
917244042Srmacklem			error = nfsrpc_reclaimcomplete(nmp, cred, p);
918244042Srmacklem			NFSCL_DEBUG(1, "aft reclaimcomp=%d\n", error);
919244042Srmacklem			if (error == NFSERR_COMPLETEALREADY ||
920244042Srmacklem			    error == NFSERR_NOTSUPP)
921244042Srmacklem				/* Ignore this error. */
922244042Srmacklem				error = 0;
923244042Srmacklem		}
924244042Srmacklem		return (error);
925244042Srmacklem	}
926244042Srmacklem
927244042Srmacklem	/*
928244042Srmacklem	 * Allocate a single session structure for NFSv4.0, because some of
929244042Srmacklem	 * the fields are used by NFSv4.0 although it doesn't do a session.
930244042Srmacklem	 */
931244042Srmacklem	dsp = malloc(sizeof(struct nfsclds), M_NFSCLDS, M_WAITOK | M_ZERO);
932244042Srmacklem	mtx_init(&dsp->nfsclds_mtx, "nfsds", NULL, MTX_DEF);
933244042Srmacklem	mtx_init(&dsp->nfsclds_sess.nfsess_mtx, "nfssession", NULL, MTX_DEF);
934244042Srmacklem	NFSLOCKMNT(nmp);
935244042Srmacklem	TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp, nfsclds_list);
936317393Srmacklem	tsep = NFSMNT_MDSSESSION(nmp);
937244042Srmacklem	NFSUNLOCKMNT(nmp);
938244042Srmacklem
939244042Srmacklem	nfscl_reqstart(nd, NFSPROC_SETCLIENTID, nmp, NULL, 0, NULL, NULL);
940191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
941191783Srmacklem	*tl++ = txdr_unsigned(nfsboottime.tv_sec);
942244042Srmacklem	*tl = txdr_unsigned(clp->nfsc_rev);
943191783Srmacklem	(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
944191783Srmacklem
945191783Srmacklem	/*
946191783Srmacklem	 * set up the callback address
947191783Srmacklem	 */
948191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
949191783Srmacklem	*tl = txdr_unsigned(NFS_CALLBCKPROG);
950191783Srmacklem	callblen = strlen(nfsv4_callbackaddr);
951191783Srmacklem	if (callblen == 0)
952294084Smelifaro		cp = nfscl_getmyip(nmp, &a6, &isinet6);
953191783Srmacklem	if (nfscl_enablecallb && nfs_numnfscbd > 0 &&
954191783Srmacklem	    (callblen > 0 || cp != NULL)) {
955191783Srmacklem		port = htons(nfsv4_cbport);
956191783Srmacklem		cp2 = (u_int8_t *)&port;
957191783Srmacklem#ifdef INET6
958191783Srmacklem		if ((callblen > 0 &&
959191783Srmacklem		     strchr(nfsv4_callbackaddr, ':')) || isinet6) {
960191783Srmacklem			char ip6buf[INET6_ADDRSTRLEN], *ip6add;
961191783Srmacklem
962191783Srmacklem			(void) nfsm_strtom(nd, "tcp6", 4);
963191783Srmacklem			if (callblen == 0) {
964191783Srmacklem				ip6_sprintf(ip6buf, (struct in6_addr *)cp);
965191783Srmacklem				ip6add = ip6buf;
966191783Srmacklem			} else {
967191783Srmacklem				ip6add = nfsv4_callbackaddr;
968191783Srmacklem			}
969191783Srmacklem			snprintf(addr, INET6_ADDRSTRLEN + 9, "%s.%d.%d",
970191783Srmacklem			    ip6add, cp2[0], cp2[1]);
971191783Srmacklem		} else
972191783Srmacklem#endif
973191783Srmacklem		{
974191783Srmacklem			(void) nfsm_strtom(nd, "tcp", 3);
975191783Srmacklem			if (callblen == 0)
976191783Srmacklem				snprintf(addr, INET6_ADDRSTRLEN + 9,
977191783Srmacklem				    "%d.%d.%d.%d.%d.%d", cp[0], cp[1],
978191783Srmacklem				    cp[2], cp[3], cp2[0], cp2[1]);
979191783Srmacklem			else
980191783Srmacklem				snprintf(addr, INET6_ADDRSTRLEN + 9,
981191783Srmacklem				    "%s.%d.%d", nfsv4_callbackaddr,
982191783Srmacklem				    cp2[0], cp2[1]);
983191783Srmacklem		}
984191783Srmacklem		(void) nfsm_strtom(nd, addr, strlen(addr));
985191783Srmacklem	} else {
986191783Srmacklem		(void) nfsm_strtom(nd, "tcp", 3);
987191783Srmacklem		(void) nfsm_strtom(nd, "0.0.0.0.0.0", 11);
988191783Srmacklem	}
989191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
990191783Srmacklem	*tl = txdr_unsigned(clp->nfsc_cbident);
991191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
992191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
993244042Srmacklem		NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
994191783Srmacklem	if (error)
995191783Srmacklem		return (error);
996191783Srmacklem	if (nd->nd_repstat == 0) {
997191783Srmacklem	    NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
998317393Srmacklem	    tsep->nfsess_clientid.lval[0] = *tl++;
999317393Srmacklem	    tsep->nfsess_clientid.lval[1] = *tl++;
1000191783Srmacklem	    confirm.lval[0] = *tl++;
1001191783Srmacklem	    confirm.lval[1] = *tl;
1002191783Srmacklem	    mbuf_freem(nd->nd_mrep);
1003191783Srmacklem	    nd->nd_mrep = NULL;
1004191783Srmacklem
1005191783Srmacklem	    /*
1006191783Srmacklem	     * and confirm it.
1007191783Srmacklem	     */
1008244042Srmacklem	    nfscl_reqstart(nd, NFSPROC_SETCLIENTIDCFRM, nmp, NULL, 0, NULL,
1009244042Srmacklem		NULL);
1010191783Srmacklem	    NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1011317393Srmacklem	    *tl++ = tsep->nfsess_clientid.lval[0];
1012317393Srmacklem	    *tl++ = tsep->nfsess_clientid.lval[1];
1013191783Srmacklem	    *tl++ = confirm.lval[0];
1014191783Srmacklem	    *tl = confirm.lval[1];
1015191783Srmacklem	    nd->nd_flag |= ND_USEGSSNAME;
1016191783Srmacklem	    error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
1017244042Srmacklem		cred, NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
1018191783Srmacklem	    if (error)
1019191783Srmacklem		return (error);
1020191783Srmacklem	    mbuf_freem(nd->nd_mrep);
1021191783Srmacklem	    nd->nd_mrep = NULL;
1022191783Srmacklem	    if (nd->nd_repstat == 0) {
1023191783Srmacklem		nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, nmp->nm_fh,
1024244042Srmacklem		    nmp->nm_fhsize, NULL, NULL);
1025191783Srmacklem		NFSZERO_ATTRBIT(&attrbits);
1026191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_LEASETIME);
1027191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1028191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
1029191783Srmacklem		error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p,
1030244042Srmacklem		    cred, NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
1031191783Srmacklem		if (error)
1032191783Srmacklem		    return (error);
1033191783Srmacklem		if (nd->nd_repstat == 0) {
1034191783Srmacklem		    error = nfsv4_loadattr(nd, NULL, NULL, NULL, NULL, 0, NULL,
1035191783Srmacklem			NULL, NULL, NULL, NULL, 0, NULL, &lease, NULL, p, cred);
1036191783Srmacklem		    if (error)
1037191783Srmacklem			goto nfsmout;
1038191783Srmacklem		    clp->nfsc_renew = NFSCL_RENEW(lease);
1039191783Srmacklem		    clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
1040191783Srmacklem		    clp->nfsc_clientidrev++;
1041191783Srmacklem		    if (clp->nfsc_clientidrev == 0)
1042191783Srmacklem			clp->nfsc_clientidrev++;
1043191783Srmacklem		}
1044191783Srmacklem	    }
1045191783Srmacklem	}
1046191783Srmacklem	error = nd->nd_repstat;
1047191783Srmacklemnfsmout:
1048191783Srmacklem	mbuf_freem(nd->nd_mrep);
1049191783Srmacklem	return (error);
1050191783Srmacklem}
1051191783Srmacklem
1052191783Srmacklem/*
1053191783Srmacklem * nfs getattr call.
1054191783Srmacklem */
1055361236Sfreqlabsint
1056191783Srmacklemnfsrpc_getattr(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
1057191783Srmacklem    struct nfsvattr *nap, void *stuff)
1058191783Srmacklem{
1059191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1060191783Srmacklem	int error;
1061191783Srmacklem	nfsattrbit_t attrbits;
1062191783Srmacklem
1063191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
1064191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1065191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1066191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1067191783Srmacklem	}
1068191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
1069191783Srmacklem	if (error)
1070191783Srmacklem		return (error);
1071191783Srmacklem	if (!nd->nd_repstat)
1072191783Srmacklem		error = nfsm_loadattr(nd, nap);
1073191783Srmacklem	else
1074191783Srmacklem		error = nd->nd_repstat;
1075191783Srmacklem	mbuf_freem(nd->nd_mrep);
1076191783Srmacklem	return (error);
1077191783Srmacklem}
1078191783Srmacklem
1079191783Srmacklem/*
1080191783Srmacklem * nfs getattr call with non-vnode arguemnts.
1081191783Srmacklem */
1082361236Sfreqlabsint
1083191783Srmacklemnfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred,
1084244042Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, u_int64_t *xidp,
1085244042Srmacklem    uint32_t *leasep)
1086191783Srmacklem{
1087191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1088191783Srmacklem	int error, vers = NFS_VER2;
1089191783Srmacklem	nfsattrbit_t attrbits;
1090191783Srmacklem
1091244042Srmacklem	nfscl_reqstart(nd, NFSPROC_GETATTR, nmp, fhp, fhlen, NULL, NULL);
1092191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1093191783Srmacklem		vers = NFS_VER4;
1094191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1095244042Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_LEASETIME);
1096191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1097191783Srmacklem	} else if (nd->nd_flag & ND_NFSV3) {
1098191783Srmacklem		vers = NFS_VER3;
1099191783Srmacklem	}
1100191783Srmacklem	if (syscred)
1101191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
1102191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
1103244042Srmacklem	    NFS_PROG, vers, NULL, 1, xidp, NULL);
1104191783Srmacklem	if (error)
1105191783Srmacklem		return (error);
1106244042Srmacklem	if (nd->nd_repstat == 0) {
1107244042Srmacklem		if ((nd->nd_flag & ND_NFSV4) != 0)
1108244042Srmacklem			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
1109244042Srmacklem			    NULL, NULL, NULL, NULL, NULL, 0, NULL, leasep, NULL,
1110244042Srmacklem			    NULL, NULL);
1111244042Srmacklem		else
1112244042Srmacklem			error = nfsm_loadattr(nd, nap);
1113244042Srmacklem	} else
1114191783Srmacklem		error = nd->nd_repstat;
1115191783Srmacklem	mbuf_freem(nd->nd_mrep);
1116191783Srmacklem	return (error);
1117191783Srmacklem}
1118191783Srmacklem
1119191783Srmacklem/*
1120191783Srmacklem * Do an nfs setattr operation.
1121191783Srmacklem */
1122361236Sfreqlabsint
1123191783Srmacklemnfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
1124191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *rnap, int *attrflagp,
1125191783Srmacklem    void *stuff)
1126191783Srmacklem{
1127191783Srmacklem	int error, expireret = 0, openerr, retrycnt;
1128191783Srmacklem	u_int32_t clidrev = 0, mode;
1129191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1130191783Srmacklem	struct nfsfh *nfhp;
1131191783Srmacklem	nfsv4stateid_t stateid;
1132191783Srmacklem	void *lckp;
1133191783Srmacklem
1134191783Srmacklem	if (nmp->nm_clp != NULL)
1135191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
1136191783Srmacklem	if (vap != NULL && NFSATTRISSET(u_quad_t, vap, va_size))
1137191783Srmacklem		mode = NFSV4OPEN_ACCESSWRITE;
1138191783Srmacklem	else
1139191783Srmacklem		mode = NFSV4OPEN_ACCESSREAD;
1140191783Srmacklem	retrycnt = 0;
1141191783Srmacklem	do {
1142191783Srmacklem		lckp = NULL;
1143191783Srmacklem		openerr = 1;
1144191783Srmacklem		if (NFSHASNFSV4(nmp)) {
1145191783Srmacklem			nfhp = VTONFS(vp)->n_fhp;
1146191783Srmacklem			error = nfscl_getstateid(vp, nfhp->nfh_fh,
1147244042Srmacklem			    nfhp->nfh_len, mode, 0, cred, p, &stateid, &lckp);
1148191783Srmacklem			if (error && vnode_vtype(vp) == VREG &&
1149191783Srmacklem			    (mode == NFSV4OPEN_ACCESSWRITE ||
1150191783Srmacklem			     nfstest_openallsetattr)) {
1151191783Srmacklem				/*
1152191783Srmacklem				 * No Open stateid, so try and open the file
1153191783Srmacklem				 * now.
1154191783Srmacklem				 */
1155191783Srmacklem				if (mode == NFSV4OPEN_ACCESSWRITE)
1156191783Srmacklem					openerr = nfsrpc_open(vp, FWRITE, cred,
1157191783Srmacklem					    p);
1158191783Srmacklem				else
1159191783Srmacklem					openerr = nfsrpc_open(vp, FREAD, cred,
1160191783Srmacklem					    p);
1161191783Srmacklem				if (!openerr)
1162191783Srmacklem					(void) nfscl_getstateid(vp,
1163191783Srmacklem					    nfhp->nfh_fh, nfhp->nfh_len,
1164244042Srmacklem					    mode, 0, cred, p, &stateid, &lckp);
1165191783Srmacklem			}
1166191783Srmacklem		}
1167191783Srmacklem		if (vap != NULL)
1168191783Srmacklem			error = nfsrpc_setattrrpc(vp, vap, &stateid, cred, p,
1169191783Srmacklem			    rnap, attrflagp, stuff);
1170191783Srmacklem		else
1171191783Srmacklem			error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
1172191783Srmacklem			    stuff);
1173317983Srmacklem		if (error == NFSERR_OPENMODE && mode == NFSV4OPEN_ACCESSREAD) {
1174317983Srmacklem			NFSLOCKMNT(nmp);
1175317983Srmacklem			nmp->nm_state |= NFSSTA_OPENMODE;
1176317983Srmacklem			NFSUNLOCKMNT(nmp);
1177317983Srmacklem		}
1178317393Srmacklem		if (error == NFSERR_STALESTATEID)
1179191783Srmacklem			nfscl_initiate_recovery(nmp->nm_clp);
1180191783Srmacklem		if (lckp != NULL)
1181191783Srmacklem			nfscl_lockderef(lckp);
1182191783Srmacklem		if (!openerr)
1183192337Srmacklem			(void) nfsrpc_close(vp, 0, p);
1184191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1185191783Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1186244042Srmacklem		    error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
1187207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_setattr");
1188191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1189191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1190191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1191191783Srmacklem		}
1192191783Srmacklem		retrycnt++;
1193191783Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1194191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1195244042Srmacklem	    error == NFSERR_BADSESSION ||
1196191783Srmacklem	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1197191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1198317983Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4) ||
1199317983Srmacklem	    (error == NFSERR_OPENMODE && mode == NFSV4OPEN_ACCESSREAD &&
1200317983Srmacklem	     retrycnt < 4));
1201191783Srmacklem	if (error && retrycnt >= 4)
1202191783Srmacklem		error = EIO;
1203191783Srmacklem	return (error);
1204191783Srmacklem}
1205191783Srmacklem
1206191783Srmacklemstatic int
1207191783Srmacklemnfsrpc_setattrrpc(vnode_t vp, struct vattr *vap,
1208191783Srmacklem    nfsv4stateid_t *stateidp, struct ucred *cred, NFSPROC_T *p,
1209191783Srmacklem    struct nfsvattr *rnap, int *attrflagp, void *stuff)
1210191783Srmacklem{
1211191783Srmacklem	u_int32_t *tl;
1212191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1213191783Srmacklem	int error;
1214191783Srmacklem	nfsattrbit_t attrbits;
1215191783Srmacklem
1216191783Srmacklem	*attrflagp = 0;
1217191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_SETATTR, vp);
1218191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
1219191783Srmacklem		nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1220191783Srmacklem	vap->va_type = vnode_vtype(vp);
1221191783Srmacklem	nfscl_fillsattr(nd, vap, vp, NFSSATTR_FULL, 0);
1222191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
1223191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1224191783Srmacklem		*tl = newnfs_false;
1225191783Srmacklem	} else if (nd->nd_flag & ND_NFSV4) {
1226191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1227191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1228191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1229191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1230191783Srmacklem	}
1231191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
1232191783Srmacklem	if (error)
1233191783Srmacklem		return (error);
1234191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1235191783Srmacklem		error = nfscl_wcc_data(nd, vp, rnap, attrflagp, NULL, stuff);
1236317405Srmacklem	if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 && !error)
1237191783Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1238191783Srmacklem	if (!(nd->nd_flag & ND_NFSV3) && !nd->nd_repstat && !error)
1239191783Srmacklem		error = nfscl_postop_attr(nd, rnap, attrflagp, stuff);
1240191783Srmacklem	mbuf_freem(nd->nd_mrep);
1241191783Srmacklem	if (nd->nd_repstat && !error)
1242191783Srmacklem		error = nd->nd_repstat;
1243191783Srmacklem	return (error);
1244191783Srmacklem}
1245191783Srmacklem
1246191783Srmacklem/*
1247191783Srmacklem * nfs lookup rpc
1248191783Srmacklem */
1249361236Sfreqlabsint
1250191783Srmacklemnfsrpc_lookup(vnode_t dvp, char *name, int len, struct ucred *cred,
1251191783Srmacklem    NFSPROC_T *p, struct nfsvattr *dnap, struct nfsvattr *nap,
1252191783Srmacklem    struct nfsfh **nfhpp, int *attrflagp, int *dattrflagp, void *stuff)
1253191783Srmacklem{
1254191783Srmacklem	u_int32_t *tl;
1255191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1256191783Srmacklem	struct nfsmount *nmp;
1257191783Srmacklem	struct nfsnode *np;
1258191783Srmacklem	struct nfsfh *nfhp;
1259191783Srmacklem	nfsattrbit_t attrbits;
1260191783Srmacklem	int error = 0, lookupp = 0;
1261191783Srmacklem
1262191783Srmacklem	*attrflagp = 0;
1263191783Srmacklem	*dattrflagp = 0;
1264191783Srmacklem	if (vnode_vtype(dvp) != VDIR)
1265191783Srmacklem		return (ENOTDIR);
1266191783Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
1267191783Srmacklem	if (len > NFS_MAXNAMLEN)
1268191783Srmacklem		return (ENAMETOOLONG);
1269191783Srmacklem	if (NFSHASNFSV4(nmp) && len == 1 &&
1270191783Srmacklem		name[0] == '.') {
1271191783Srmacklem		/*
1272191783Srmacklem		 * Just return the current dir's fh.
1273191783Srmacklem		 */
1274191783Srmacklem		np = VTONFS(dvp);
1275191783Srmacklem		MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1276191783Srmacklem			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1277191783Srmacklem		nfhp->nfh_len = np->n_fhp->nfh_len;
1278191783Srmacklem		NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1279191783Srmacklem		*nfhpp = nfhp;
1280191783Srmacklem		return (0);
1281191783Srmacklem	}
1282191783Srmacklem	if (NFSHASNFSV4(nmp) && len == 2 &&
1283191783Srmacklem		name[0] == '.' && name[1] == '.') {
1284191783Srmacklem		lookupp = 1;
1285191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, dvp);
1286191783Srmacklem	} else {
1287191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_LOOKUP, dvp);
1288191783Srmacklem		(void) nfsm_strtom(nd, name, len);
1289191783Srmacklem	}
1290191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1291191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1292191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1293191783Srmacklem		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1294191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1295191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1296191783Srmacklem	}
1297191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, stuff);
1298191783Srmacklem	if (error)
1299191783Srmacklem		return (error);
1300191783Srmacklem	if (nd->nd_repstat) {
1301191783Srmacklem		/*
1302191783Srmacklem		 * When an NFSv4 Lookupp returns ENOENT, it means that
1303191783Srmacklem		 * the lookup is at the root of an fs, so return this dir.
1304191783Srmacklem		 */
1305191783Srmacklem		if (nd->nd_repstat == NFSERR_NOENT && lookupp) {
1306191783Srmacklem		    np = VTONFS(dvp);
1307191783Srmacklem		    MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) +
1308191783Srmacklem			np->n_fhp->nfh_len, M_NFSFH, M_WAITOK);
1309191783Srmacklem		    nfhp->nfh_len = np->n_fhp->nfh_len;
1310191783Srmacklem		    NFSBCOPY(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len);
1311191783Srmacklem		    *nfhpp = nfhp;
1312191783Srmacklem		    mbuf_freem(nd->nd_mrep);
1313191783Srmacklem		    return (0);
1314191783Srmacklem		}
1315191783Srmacklem		if (nd->nd_flag & ND_NFSV3)
1316191783Srmacklem		    error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1317264672Srmacklem		else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
1318264672Srmacklem		    ND_NFSV4) {
1319264672Srmacklem			/* Load the directory attributes. */
1320264672Srmacklem			error = nfsm_loadattr(nd, dnap);
1321264672Srmacklem			if (error == 0)
1322264672Srmacklem				*dattrflagp = 1;
1323264672Srmacklem		}
1324191783Srmacklem		goto nfsmout;
1325191783Srmacklem	}
1326191783Srmacklem	if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
1327264672Srmacklem		/* Load the directory attributes. */
1328264672Srmacklem		error = nfsm_loadattr(nd, dnap);
1329264672Srmacklem		if (error != 0)
1330191783Srmacklem			goto nfsmout;
1331264672Srmacklem		*dattrflagp = 1;
1332264672Srmacklem		/* Skip over the Lookup and GetFH operation status values. */
1333264672Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1334191783Srmacklem	}
1335191783Srmacklem	error = nfsm_getfh(nd, nfhpp);
1336191783Srmacklem	if (error)
1337191783Srmacklem		goto nfsmout;
1338191783Srmacklem
1339191783Srmacklem	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1340191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) && !error)
1341191783Srmacklem		error = nfscl_postop_attr(nd, dnap, dattrflagp, stuff);
1342191783Srmacklemnfsmout:
1343191783Srmacklem	mbuf_freem(nd->nd_mrep);
1344191783Srmacklem	if (!error && nd->nd_repstat)
1345191783Srmacklem		error = nd->nd_repstat;
1346191783Srmacklem	return (error);
1347191783Srmacklem}
1348191783Srmacklem
1349191783Srmacklem/*
1350191783Srmacklem * Do a readlink rpc.
1351191783Srmacklem */
1352361236Sfreqlabsint
1353191783Srmacklemnfsrpc_readlink(vnode_t vp, struct uio *uiop, struct ucred *cred,
1354191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1355191783Srmacklem{
1356191783Srmacklem	u_int32_t *tl;
1357191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1358191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1359191783Srmacklem	nfsattrbit_t attrbits;
1360191783Srmacklem	int error, len, cangetattr = 1;
1361191783Srmacklem
1362191783Srmacklem	*attrflagp = 0;
1363191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_READLINK, vp);
1364191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1365191783Srmacklem		/*
1366191783Srmacklem		 * And do a Getattr op.
1367191783Srmacklem		 */
1368191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1369191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1370191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1371191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1372191783Srmacklem	}
1373191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
1374191783Srmacklem	if (error)
1375191783Srmacklem		return (error);
1376191783Srmacklem	if (nd->nd_flag & ND_NFSV3)
1377191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1378191783Srmacklem	if (!nd->nd_repstat && !error) {
1379191783Srmacklem		NFSM_STRSIZ(len, NFS_MAXPATHLEN);
1380191783Srmacklem		/*
1381191783Srmacklem		 * This seems weird to me, but must have been added to
1382191783Srmacklem		 * FreeBSD for some reason. The only thing I can think of
1383191783Srmacklem		 * is that there was/is some server that replies with
1384191783Srmacklem		 * more link data than it should?
1385191783Srmacklem		 */
1386191783Srmacklem		if (len == NFS_MAXPATHLEN) {
1387191783Srmacklem			NFSLOCKNODE(np);
1388191783Srmacklem			if (np->n_size > 0 && np->n_size < NFS_MAXPATHLEN) {
1389191783Srmacklem				len = np->n_size;
1390191783Srmacklem				cangetattr = 0;
1391191783Srmacklem			}
1392191783Srmacklem			NFSUNLOCKNODE(np);
1393191783Srmacklem		}
1394191783Srmacklem		error = nfsm_mbufuio(nd, uiop, len);
1395191783Srmacklem		if ((nd->nd_flag & ND_NFSV4) && !error && cangetattr)
1396191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1397191783Srmacklem	}
1398191783Srmacklem	if (nd->nd_repstat && !error)
1399191783Srmacklem		error = nd->nd_repstat;
1400191783Srmacklemnfsmout:
1401191783Srmacklem	mbuf_freem(nd->nd_mrep);
1402191783Srmacklem	return (error);
1403191783Srmacklem}
1404191783Srmacklem
1405191783Srmacklem/*
1406191783Srmacklem * Read operation.
1407191783Srmacklem */
1408361236Sfreqlabsint
1409191783Srmacklemnfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
1410191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1411191783Srmacklem{
1412191783Srmacklem	int error, expireret = 0, retrycnt;
1413191783Srmacklem	u_int32_t clidrev = 0;
1414191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1415191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1416191783Srmacklem	struct ucred *newcred;
1417191783Srmacklem	struct nfsfh *nfhp = NULL;
1418191783Srmacklem	nfsv4stateid_t stateid;
1419191783Srmacklem	void *lckp;
1420191783Srmacklem
1421191783Srmacklem	if (nmp->nm_clp != NULL)
1422191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
1423191783Srmacklem	newcred = cred;
1424191783Srmacklem	if (NFSHASNFSV4(nmp)) {
1425191783Srmacklem		nfhp = np->n_fhp;
1426228827Srmacklem		newcred = NFSNEWCRED(cred);
1427191783Srmacklem	}
1428191783Srmacklem	retrycnt = 0;
1429191783Srmacklem	do {
1430191783Srmacklem		lckp = NULL;
1431191783Srmacklem		if (NFSHASNFSV4(nmp))
1432191783Srmacklem			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1433244042Srmacklem			    NFSV4OPEN_ACCESSREAD, 0, newcred, p, &stateid,
1434244042Srmacklem			    &lckp);
1435191783Srmacklem		error = nfsrpc_readrpc(vp, uiop, newcred, &stateid, p, nap,
1436191783Srmacklem		    attrflagp, stuff);
1437317983Srmacklem		if (error == NFSERR_OPENMODE) {
1438317983Srmacklem			NFSLOCKMNT(nmp);
1439317983Srmacklem			nmp->nm_state |= NFSSTA_OPENMODE;
1440317983Srmacklem			NFSUNLOCKMNT(nmp);
1441317983Srmacklem		}
1442317393Srmacklem		if (error == NFSERR_STALESTATEID)
1443191783Srmacklem			nfscl_initiate_recovery(nmp->nm_clp);
1444191783Srmacklem		if (lckp != NULL)
1445191783Srmacklem			nfscl_lockderef(lckp);
1446191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1447191783Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1448244042Srmacklem		    error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
1449207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_read");
1450191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1451191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1452191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1453191783Srmacklem		}
1454191783Srmacklem		retrycnt++;
1455191783Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1456191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1457244042Srmacklem	    error == NFSERR_BADSESSION ||
1458191783Srmacklem	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1459191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1460317983Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4) ||
1461317983Srmacklem	    (error == NFSERR_OPENMODE && retrycnt < 4));
1462191783Srmacklem	if (error && retrycnt >= 4)
1463191783Srmacklem		error = EIO;
1464228827Srmacklem	if (NFSHASNFSV4(nmp))
1465191783Srmacklem		NFSFREECRED(newcred);
1466191783Srmacklem	return (error);
1467191783Srmacklem}
1468191783Srmacklem
1469191783Srmacklem/*
1470191783Srmacklem * The actual read RPC.
1471191783Srmacklem */
1472191783Srmacklemstatic int
1473191783Srmacklemnfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
1474191783Srmacklem    nfsv4stateid_t *stateidp, NFSPROC_T *p, struct nfsvattr *nap,
1475191783Srmacklem    int *attrflagp, void *stuff)
1476191783Srmacklem{
1477191783Srmacklem	u_int32_t *tl;
1478191783Srmacklem	int error = 0, len, retlen, tsiz, eof = 0;
1479191783Srmacklem	struct nfsrv_descript nfsd;
1480191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1481191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
1482220810Srmacklem	int rsize;
1483220876Srmacklem	off_t tmp_off;
1484191783Srmacklem
1485191783Srmacklem	*attrflagp = 0;
1486191783Srmacklem	tsiz = uio_uio_resid(uiop);
1487220876Srmacklem	tmp_off = uiop->uio_offset + tsiz;
1488220810Srmacklem	NFSLOCKMNT(nmp);
1489220876Srmacklem	if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset) {
1490220810Srmacklem		NFSUNLOCKMNT(nmp);
1491191783Srmacklem		return (EFBIG);
1492220810Srmacklem	}
1493220810Srmacklem	rsize = nmp->nm_rsize;
1494220810Srmacklem	NFSUNLOCKMNT(nmp);
1495191783Srmacklem	nd->nd_mrep = NULL;
1496191783Srmacklem	while (tsiz > 0) {
1497191783Srmacklem		*attrflagp = 0;
1498220810Srmacklem		len = (tsiz > rsize) ? rsize : tsiz;
1499191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_READ, vp);
1500191783Srmacklem		if (nd->nd_flag & ND_NFSV4)
1501191783Srmacklem			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1502191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
1503191783Srmacklem		if (nd->nd_flag & ND_NFSV2) {
1504191783Srmacklem			*tl++ = txdr_unsigned(uiop->uio_offset);
1505191783Srmacklem			*tl++ = txdr_unsigned(len);
1506191783Srmacklem			*tl = 0;
1507191783Srmacklem		} else {
1508191783Srmacklem			txdr_hyper(uiop->uio_offset, tl);
1509191783Srmacklem			*(tl + 2) = txdr_unsigned(len);
1510191783Srmacklem		}
1511191783Srmacklem		/*
1512191783Srmacklem		 * Since I can't do a Getattr for NFSv4 for Write, there
1513191783Srmacklem		 * doesn't seem any point in doing one here, either.
1514191783Srmacklem		 * (See the comment in nfsrpc_writerpc() for more info.)
1515191783Srmacklem		 */
1516191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
1517191783Srmacklem		if (error)
1518191783Srmacklem			return (error);
1519191783Srmacklem		if (nd->nd_flag & ND_NFSV3) {
1520191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
1521191783Srmacklem		} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
1522191783Srmacklem			error = nfsm_loadattr(nd, nap);
1523191783Srmacklem			if (!error)
1524191783Srmacklem				*attrflagp = 1;
1525191783Srmacklem		}
1526191783Srmacklem		if (nd->nd_repstat || error) {
1527191783Srmacklem			if (!error)
1528191783Srmacklem				error = nd->nd_repstat;
1529191783Srmacklem			goto nfsmout;
1530191783Srmacklem		}
1531191783Srmacklem		if (nd->nd_flag & ND_NFSV3) {
1532191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1533191783Srmacklem			eof = fxdr_unsigned(int, *(tl + 1));
1534191783Srmacklem		} else if (nd->nd_flag & ND_NFSV4) {
1535191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1536191783Srmacklem			eof = fxdr_unsigned(int, *tl);
1537191783Srmacklem		}
1538245977Skib		NFSM_STRSIZ(retlen, len);
1539191783Srmacklem		error = nfsm_mbufuio(nd, uiop, retlen);
1540191783Srmacklem		if (error)
1541191783Srmacklem			goto nfsmout;
1542191783Srmacklem		mbuf_freem(nd->nd_mrep);
1543191783Srmacklem		nd->nd_mrep = NULL;
1544191783Srmacklem		tsiz -= retlen;
1545191783Srmacklem		if (!(nd->nd_flag & ND_NFSV2)) {
1546191783Srmacklem			if (eof || retlen == 0)
1547191783Srmacklem				tsiz = 0;
1548191783Srmacklem		} else if (retlen < len)
1549191783Srmacklem			tsiz = 0;
1550191783Srmacklem	}
1551191783Srmacklem	return (0);
1552191783Srmacklemnfsmout:
1553191783Srmacklem	if (nd->nd_mrep != NULL)
1554191783Srmacklem		mbuf_freem(nd->nd_mrep);
1555191783Srmacklem	return (error);
1556191783Srmacklem}
1557191783Srmacklem
1558191783Srmacklem/*
1559191783Srmacklem * nfs write operation
1560207082Srmacklem * When called_from_strategy != 0, it should return EIO for an error that
1561207082Srmacklem * indicates recovery is in progress, so that the buffer will be left
1562207082Srmacklem * dirty and be written back to the server later. If it loops around,
1563207082Srmacklem * the recovery thread could get stuck waiting for the buffer and recovery
1564207082Srmacklem * will then deadlock.
1565191783Srmacklem */
1566361236Sfreqlabsint
1567222289Srmacklemnfsrpc_write(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
1568191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
1569207082Srmacklem    void *stuff, int called_from_strategy)
1570191783Srmacklem{
1571191783Srmacklem	int error, expireret = 0, retrycnt, nostateid;
1572191783Srmacklem	u_int32_t clidrev = 0;
1573191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1574191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1575191783Srmacklem	struct ucred *newcred;
1576191783Srmacklem	struct nfsfh *nfhp = NULL;
1577191783Srmacklem	nfsv4stateid_t stateid;
1578191783Srmacklem	void *lckp;
1579191783Srmacklem
1580222289Srmacklem	*must_commit = 0;
1581191783Srmacklem	if (nmp->nm_clp != NULL)
1582191783Srmacklem		clidrev = nmp->nm_clp->nfsc_clientidrev;
1583191783Srmacklem	newcred = cred;
1584191783Srmacklem	if (NFSHASNFSV4(nmp)) {
1585228827Srmacklem		newcred = NFSNEWCRED(cred);
1586191783Srmacklem		nfhp = np->n_fhp;
1587191783Srmacklem	}
1588191783Srmacklem	retrycnt = 0;
1589191783Srmacklem	do {
1590191783Srmacklem		lckp = NULL;
1591191783Srmacklem		nostateid = 0;
1592191783Srmacklem		if (NFSHASNFSV4(nmp)) {
1593191783Srmacklem			(void)nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len,
1594244042Srmacklem			    NFSV4OPEN_ACCESSWRITE, 0, newcred, p, &stateid,
1595244042Srmacklem			    &lckp);
1596191783Srmacklem			if (stateid.other[0] == 0 && stateid.other[1] == 0 &&
1597191783Srmacklem			    stateid.other[2] == 0) {
1598191783Srmacklem				nostateid = 1;
1599240289Srmacklem				NFSCL_DEBUG(1, "stateid0 in write\n");
1600191783Srmacklem			}
1601191783Srmacklem		}
1602191783Srmacklem
1603191783Srmacklem		/*
1604191783Srmacklem		 * If there is no stateid for NFSv4, it means this is an
1605191783Srmacklem		 * extraneous write after close. Basically a poorly
1606191783Srmacklem		 * implemented buffer cache. Just don't do the write.
1607191783Srmacklem		 */
1608191783Srmacklem		if (nostateid)
1609191783Srmacklem			error = 0;
1610191783Srmacklem		else
1611222289Srmacklem			error = nfsrpc_writerpc(vp, uiop, iomode, must_commit,
1612191783Srmacklem			    newcred, &stateid, p, nap, attrflagp, stuff);
1613317393Srmacklem		if (error == NFSERR_STALESTATEID)
1614191783Srmacklem			nfscl_initiate_recovery(nmp->nm_clp);
1615191783Srmacklem		if (lckp != NULL)
1616191783Srmacklem			nfscl_lockderef(lckp);
1617191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
1618191783Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1619244042Srmacklem		    error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
1620207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_write");
1621191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1622191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1623191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1624191783Srmacklem		}
1625191783Srmacklem		retrycnt++;
1626207082Srmacklem	} while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
1627244042Srmacklem	    ((error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
1628207082Srmacklem	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy == 0) ||
1629191783Srmacklem	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
1630191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1631191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
1632207082Srmacklem	if (error != 0 && (retrycnt >= 4 ||
1633244042Srmacklem	    ((error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
1634207082Srmacklem	      error == NFSERR_STALEDONTRECOVER) && called_from_strategy != 0)))
1635191783Srmacklem		error = EIO;
1636228827Srmacklem	if (NFSHASNFSV4(nmp))
1637191783Srmacklem		NFSFREECRED(newcred);
1638191783Srmacklem	return (error);
1639191783Srmacklem}
1640191783Srmacklem
1641191783Srmacklem/*
1642191783Srmacklem * The actual write RPC.
1643191783Srmacklem */
1644191783Srmacklemstatic int
1645191783Srmacklemnfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
1646222289Srmacklem    int *must_commit, struct ucred *cred, nfsv4stateid_t *stateidp,
1647191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
1648191783Srmacklem{
1649191783Srmacklem	u_int32_t *tl;
1650191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
1651191783Srmacklem	struct nfsnode *np = VTONFS(vp);
1652191783Srmacklem	int error = 0, len, tsiz, rlen, commit, committed = NFSWRITE_FILESYNC;
1653191783Srmacklem	int wccflag = 0, wsize;
1654191783Srmacklem	int32_t backup;
1655191783Srmacklem	struct nfsrv_descript nfsd;
1656191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
1657191783Srmacklem	nfsattrbit_t attrbits;
1658220876Srmacklem	off_t tmp_off;
1659191783Srmacklem
1660209120Skib	KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
1661191783Srmacklem	*attrflagp = 0;
1662191783Srmacklem	tsiz = uio_uio_resid(uiop);
1663220876Srmacklem	tmp_off = uiop->uio_offset + tsiz;
1664191783Srmacklem	NFSLOCKMNT(nmp);
1665220876Srmacklem	if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset) {
1666191783Srmacklem		NFSUNLOCKMNT(nmp);
1667191783Srmacklem		return (EFBIG);
1668191783Srmacklem	}
1669191783Srmacklem	wsize = nmp->nm_wsize;
1670191783Srmacklem	NFSUNLOCKMNT(nmp);
1671191783Srmacklem	nd->nd_mrep = NULL;	/* NFSv2 sometimes does a write with */
1672191783Srmacklem	nd->nd_repstat = 0;	/* uio_resid == 0, so the while is not done */
1673191783Srmacklem	while (tsiz > 0) {
1674191783Srmacklem		*attrflagp = 0;
1675191783Srmacklem		len = (tsiz > wsize) ? wsize : tsiz;
1676191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_WRITE, vp);
1677191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
1678191783Srmacklem			nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
1679191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+2*NFSX_UNSIGNED);
1680191783Srmacklem			txdr_hyper(uiop->uio_offset, tl);
1681191783Srmacklem			tl += 2;
1682191783Srmacklem			*tl++ = txdr_unsigned(*iomode);
1683191783Srmacklem			*tl = txdr_unsigned(len);
1684191783Srmacklem		} else if (nd->nd_flag & ND_NFSV3) {
1685191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER+3*NFSX_UNSIGNED);
1686191783Srmacklem			txdr_hyper(uiop->uio_offset, tl);
1687191783Srmacklem			tl += 2;
1688191783Srmacklem			*tl++ = txdr_unsigned(len);
1689191783Srmacklem			*tl++ = txdr_unsigned(*iomode);
1690191783Srmacklem			*tl = txdr_unsigned(len);
1691191783Srmacklem		} else {
1692191783Srmacklem			u_int32_t x;
1693191783Srmacklem
1694191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1695191783Srmacklem			/*
1696191783Srmacklem			 * Not sure why someone changed this, since the
1697191783Srmacklem			 * RFC clearly states that "beginoffset" and
1698191783Srmacklem			 * "totalcount" are ignored, but it wouldn't
1699191783Srmacklem			 * surprise me if there's a busted server out there.
1700191783Srmacklem			 */
1701191783Srmacklem			/* Set both "begin" and "current" to non-garbage. */
1702191783Srmacklem			x = txdr_unsigned((u_int32_t)uiop->uio_offset);
1703191783Srmacklem			*tl++ = x;      /* "begin offset" */
1704191783Srmacklem			*tl++ = x;      /* "current offset" */
1705191783Srmacklem			x = txdr_unsigned(len);
1706191783Srmacklem			*tl++ = x;      /* total to this offset */
1707191783Srmacklem			*tl = x;        /* size of this write */
1708191783Srmacklem
1709191783Srmacklem		}
1710191783Srmacklem		nfsm_uiombuf(nd, uiop, len);
1711191783Srmacklem		/*
1712191783Srmacklem		 * Although it is tempting to do a normal Getattr Op in the
1713191783Srmacklem		 * NFSv4 compound, the result can be a nearly hung client
1714191783Srmacklem		 * system if the Getattr asks for Owner and/or OwnerGroup.
1715191783Srmacklem		 * It occurs when the client can't map either the Owner or
1716191783Srmacklem		 * Owner_group name in the Getattr reply to a uid/gid. When
1717191783Srmacklem		 * there is a cache miss, the kernel does an upcall to the
1718191783Srmacklem		 * nfsuserd. Then, it can try and read the local /etc/passwd
1719191783Srmacklem		 * or /etc/group file. It can then block in getnewbuf(),
1720191783Srmacklem		 * waiting for dirty writes to be pushed to the NFS server.
1721191783Srmacklem		 * The only reason this doesn't result in a complete
1722191783Srmacklem		 * deadlock, is that the upcall times out and allows
1723191783Srmacklem		 * the write to complete. However, progress is so slow
1724191783Srmacklem		 * that it might just as well be deadlocked.
1725223657Srmacklem		 * As such, we get the rest of the attributes, but not
1726223657Srmacklem		 * Owner or Owner_group.
1727191783Srmacklem		 * nb: nfscl_loadattrcache() needs to be told that these
1728191783Srmacklem		 *     partial attributes from a write rpc are being
1729191783Srmacklem		 *     passed in, via a argument flag.
1730191783Srmacklem		 */
1731191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
1732191783Srmacklem			NFSWRITEGETATTR_ATTRBIT(&attrbits);
1733191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1734191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
1735191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
1736191783Srmacklem		}
1737191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
1738191783Srmacklem		if (error)
1739191783Srmacklem			return (error);
1740191783Srmacklem		if (nd->nd_repstat) {
1741191783Srmacklem			/*
1742191783Srmacklem			 * In case the rpc gets retried, roll
1743191783Srmacklem			 * the uio fileds changed by nfsm_uiombuf()
1744191783Srmacklem			 * back.
1745191783Srmacklem			 */
1746191783Srmacklem			uiop->uio_offset -= len;
1747191783Srmacklem			uio_uio_resid_add(uiop, len);
1748191783Srmacklem			uio_iov_base_add(uiop, -len);
1749191783Srmacklem			uio_iov_len_add(uiop, len);
1750191783Srmacklem		}
1751191783Srmacklem		if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1752191783Srmacklem			error = nfscl_wcc_data(nd, vp, nap, attrflagp,
1753191783Srmacklem			    &wccflag, stuff);
1754191783Srmacklem			if (error)
1755191783Srmacklem				goto nfsmout;
1756191783Srmacklem		}
1757191783Srmacklem		if (!nd->nd_repstat) {
1758191783Srmacklem			if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
1759191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED
1760191783Srmacklem					+ NFSX_VERF);
1761191783Srmacklem				rlen = fxdr_unsigned(int, *tl++);
1762191783Srmacklem				if (rlen == 0) {
1763191783Srmacklem					error = NFSERR_IO;
1764191783Srmacklem					goto nfsmout;
1765191783Srmacklem				} else if (rlen < len) {
1766191783Srmacklem					backup = len - rlen;
1767191783Srmacklem					uio_iov_base_add(uiop, -(backup));
1768191783Srmacklem					uio_iov_len_add(uiop, backup);
1769191783Srmacklem					uiop->uio_offset -= backup;
1770191783Srmacklem					uio_uio_resid_add(uiop, backup);
1771191783Srmacklem					len = rlen;
1772191783Srmacklem				}
1773191783Srmacklem				commit = fxdr_unsigned(int, *tl++);
1774191783Srmacklem
1775191783Srmacklem				/*
1776298788Spfg				 * Return the lowest commitment level
1777191783Srmacklem				 * obtained by any of the RPCs.
1778191783Srmacklem				 */
1779191783Srmacklem				if (committed == NFSWRITE_FILESYNC)
1780191783Srmacklem					committed = commit;
1781191783Srmacklem				else if (committed == NFSWRITE_DATASYNC &&
1782191783Srmacklem					commit == NFSWRITE_UNSTABLE)
1783191783Srmacklem					committed = commit;
1784191783Srmacklem				NFSLOCKMNT(nmp);
1785191783Srmacklem				if (!NFSHASWRITEVERF(nmp)) {
1786191783Srmacklem					NFSBCOPY((caddr_t)tl,
1787191783Srmacklem					    (caddr_t)&nmp->nm_verf[0],
1788191783Srmacklem					    NFSX_VERF);
1789191783Srmacklem					NFSSETWRITEVERF(nmp);
1790222289Srmacklem	    			} else if (NFSBCMP(tl, nmp->nm_verf,
1791222289Srmacklem				    NFSX_VERF)) {
1792222289Srmacklem					*must_commit = 1;
1793222289Srmacklem					NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
1794191783Srmacklem				}
1795191783Srmacklem				NFSUNLOCKMNT(nmp);
1796191783Srmacklem			}
1797191783Srmacklem			if (nd->nd_flag & ND_NFSV4)
1798191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1799191783Srmacklem			if (nd->nd_flag & (ND_NFSV2 | ND_NFSV4)) {
1800191783Srmacklem				error = nfsm_loadattr(nd, nap);
1801191783Srmacklem				if (!error)
1802191783Srmacklem					*attrflagp = NFS_LATTR_NOSHRINK;
1803191783Srmacklem			}
1804191783Srmacklem		} else {
1805191783Srmacklem			error = nd->nd_repstat;
1806191783Srmacklem		}
1807191783Srmacklem		if (error)
1808191783Srmacklem			goto nfsmout;
1809297837Srmacklem		NFSWRITERPC_SETTIME(wccflag, np, nap, (nd->nd_flag & ND_NFSV4));
1810191783Srmacklem		mbuf_freem(nd->nd_mrep);
1811191783Srmacklem		nd->nd_mrep = NULL;
1812191783Srmacklem		tsiz -= len;
1813191783Srmacklem	}
1814191783Srmacklemnfsmout:
1815191783Srmacklem	if (nd->nd_mrep != NULL)
1816191783Srmacklem		mbuf_freem(nd->nd_mrep);
1817191783Srmacklem	*iomode = committed;
1818191783Srmacklem	if (nd->nd_repstat && !error)
1819191783Srmacklem		error = nd->nd_repstat;
1820191783Srmacklem	return (error);
1821191783Srmacklem}
1822191783Srmacklem
1823191783Srmacklem/*
1824191783Srmacklem * nfs mknod rpc
1825191783Srmacklem * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
1826191783Srmacklem * mode set to specify the file type and the size field for rdev.
1827191783Srmacklem */
1828361236Sfreqlabsint
1829191783Srmacklemnfsrpc_mknod(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1830191783Srmacklem    u_int32_t rdev, enum vtype vtyp, struct ucred *cred, NFSPROC_T *p,
1831191783Srmacklem    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1832191783Srmacklem    int *attrflagp, int *dattrflagp, void *dstuff)
1833191783Srmacklem{
1834191783Srmacklem	u_int32_t *tl;
1835191783Srmacklem	int error = 0;
1836191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1837191783Srmacklem	nfsattrbit_t attrbits;
1838191783Srmacklem
1839191783Srmacklem	*nfhpp = NULL;
1840191783Srmacklem	*attrflagp = 0;
1841191783Srmacklem	*dattrflagp = 0;
1842191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
1843191783Srmacklem		return (ENAMETOOLONG);
1844191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_MKNOD, dvp);
1845191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1846201345Srmacklem		if (vtyp == VBLK || vtyp == VCHR) {
1847201345Srmacklem			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1848201345Srmacklem			*tl++ = vtonfsv34_type(vtyp);
1849201345Srmacklem			*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1850201345Srmacklem			*tl = txdr_unsigned(NFSMINOR(rdev));
1851201345Srmacklem		} else {
1852201345Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1853201345Srmacklem			*tl = vtonfsv34_type(vtyp);
1854201345Srmacklem		}
1855191783Srmacklem	}
1856191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
1857191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
1858191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1859191783Srmacklem		*tl = vtonfsv34_type(vtyp);
1860191783Srmacklem	}
1861191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
1862191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, 0, 0);
1863191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) &&
1864191783Srmacklem	    (vtyp == VCHR || vtyp == VBLK)) {
1865191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1866191783Srmacklem		*tl++ = txdr_unsigned(NFSMAJOR(rdev));
1867191783Srmacklem		*tl = txdr_unsigned(NFSMINOR(rdev));
1868191783Srmacklem	}
1869191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
1870191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
1871191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
1872191783Srmacklem		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
1873191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
1874191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
1875191783Srmacklem	}
1876191783Srmacklem	if (nd->nd_flag & ND_NFSV2)
1877191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZERDEV, rdev);
1878191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
1879191783Srmacklem	if (error)
1880191783Srmacklem		return (error);
1881191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
1882191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1883191783Srmacklem	if (!nd->nd_repstat) {
1884191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
1885191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
1886191783Srmacklem			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
1887191783Srmacklem			if (error)
1888191783Srmacklem				goto nfsmout;
1889191783Srmacklem		}
1890191783Srmacklem		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
1891191783Srmacklem		if (error)
1892191783Srmacklem			goto nfsmout;
1893191783Srmacklem	}
1894191783Srmacklem	if (nd->nd_flag & ND_NFSV3)
1895191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
1896191783Srmacklem	if (!error && nd->nd_repstat)
1897191783Srmacklem		error = nd->nd_repstat;
1898191783Srmacklemnfsmout:
1899191783Srmacklem	mbuf_freem(nd->nd_mrep);
1900191783Srmacklem	return (error);
1901191783Srmacklem}
1902191783Srmacklem
1903191783Srmacklem/*
1904191783Srmacklem * nfs file create call
1905191783Srmacklem * Mostly just call the approriate routine. (I separated out v4, so that
1906191783Srmacklem * error recovery wouldn't be as difficult.)
1907191783Srmacklem */
1908361236Sfreqlabsint
1909191783Srmacklemnfsrpc_create(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1910191783Srmacklem    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1911191783Srmacklem    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1912191783Srmacklem    int *attrflagp, int *dattrflagp, void *dstuff)
1913191783Srmacklem{
1914191783Srmacklem	int error = 0, newone, expireret = 0, retrycnt, unlocked;
1915191783Srmacklem	struct nfsclowner *owp;
1916191783Srmacklem	struct nfscldeleg *dp;
1917191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(dvp));
1918191783Srmacklem	u_int32_t clidrev;
1919191783Srmacklem
1920191783Srmacklem	if (NFSHASNFSV4(nmp)) {
1921191783Srmacklem	    retrycnt = 0;
1922191783Srmacklem	    do {
1923191783Srmacklem		dp = NULL;
1924191783Srmacklem		error = nfscl_open(dvp, NULL, 0, (NFSV4OPEN_ACCESSWRITE |
1925191783Srmacklem		    NFSV4OPEN_ACCESSREAD), 0, cred, p, &owp, NULL, &newone,
1926191783Srmacklem		    NULL, 1);
1927191783Srmacklem		if (error)
1928191783Srmacklem			return (error);
1929191783Srmacklem		if (nmp->nm_clp != NULL)
1930191783Srmacklem			clidrev = nmp->nm_clp->nfsc_clientidrev;
1931191783Srmacklem		else
1932191783Srmacklem			clidrev = 0;
1933320998Srmacklem		if (!NFSHASPNFS(nmp) || nfscl_enablecallb == 0 ||
1934320998Srmacklem		    nfs_numnfscbd == 0 || retrycnt > 0)
1935320998Srmacklem			error = nfsrpc_createv4(dvp, name, namelen, vap, cverf,
1936320998Srmacklem			  fmode, owp, &dp, cred, p, dnap, nnap, nfhpp,
1937320998Srmacklem			  attrflagp, dattrflagp, dstuff, &unlocked);
1938320998Srmacklem		else
1939320998Srmacklem			error = nfsrpc_getcreatelayout(dvp, name, namelen, vap,
1940320998Srmacklem			  cverf, fmode, owp, &dp, cred, p, dnap, nnap, nfhpp,
1941320998Srmacklem			  attrflagp, dattrflagp, dstuff, &unlocked);
1942210034Srmacklem		/*
1943210034Srmacklem		 * There is no need to invalidate cached attributes here,
1944210034Srmacklem		 * since new post-delegation issue attributes are always
1945210034Srmacklem		 * returned by nfsrpc_createv4() and these will update the
1946210034Srmacklem		 * attribute cache.
1947210034Srmacklem		 */
1948191783Srmacklem		if (dp != NULL)
1949191783Srmacklem			(void) nfscl_deleg(nmp->nm_mountp, owp->nfsow_clp,
1950191783Srmacklem			    (*nfhpp)->nfh_fh, (*nfhpp)->nfh_len, cred, p, &dp);
1951317520Srmacklem		nfscl_ownerrelease(nmp, owp, error, newone, unlocked);
1952191783Srmacklem		if (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1953244042Srmacklem		    error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1954244042Srmacklem		    error == NFSERR_BADSESSION) {
1955207170Srmacklem			(void) nfs_catnap(PZERO, error, "nfs_open");
1956191783Srmacklem		} else if ((error == NFSERR_EXPIRED ||
1957191783Srmacklem		    error == NFSERR_BADSTATEID) && clidrev != 0) {
1958191783Srmacklem			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
1959191783Srmacklem			retrycnt++;
1960191783Srmacklem		}
1961191783Srmacklem	    } while (error == NFSERR_GRACE || error == NFSERR_STALECLIENTID ||
1962191783Srmacklem		error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
1963244042Srmacklem		error == NFSERR_BADSESSION ||
1964191783Srmacklem		((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
1965191783Srmacklem		 expireret == 0 && clidrev != 0 && retrycnt < 4));
1966191783Srmacklem	    if (error && retrycnt >= 4)
1967191783Srmacklem		    error = EIO;
1968191783Srmacklem	} else {
1969191783Srmacklem		error = nfsrpc_createv23(dvp, name, namelen, vap, cverf,
1970191783Srmacklem		    fmode, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
1971191783Srmacklem		    dstuff);
1972191783Srmacklem	}
1973191783Srmacklem	return (error);
1974191783Srmacklem}
1975191783Srmacklem
1976191783Srmacklem/*
1977191783Srmacklem * The create rpc for v2 and 3.
1978191783Srmacklem */
1979191783Srmacklemstatic int
1980191783Srmacklemnfsrpc_createv23(vnode_t dvp, char *name, int namelen, struct vattr *vap,
1981191783Srmacklem    nfsquad_t cverf, int fmode, struct ucred *cred, NFSPROC_T *p,
1982191783Srmacklem    struct nfsvattr *dnap, struct nfsvattr *nnap, struct nfsfh **nfhpp,
1983191783Srmacklem    int *attrflagp, int *dattrflagp, void *dstuff)
1984191783Srmacklem{
1985191783Srmacklem	u_int32_t *tl;
1986191783Srmacklem	int error = 0;
1987191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
1988191783Srmacklem
1989191783Srmacklem	*nfhpp = NULL;
1990191783Srmacklem	*attrflagp = 0;
1991191783Srmacklem	*dattrflagp = 0;
1992191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
1993191783Srmacklem		return (ENAMETOOLONG);
1994191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
1995191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
1996191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
1997191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
1998191783Srmacklem		if (fmode & O_EXCL) {
1999191783Srmacklem			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
2000191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
2001191783Srmacklem			*tl++ = cverf.lval[0];
2002191783Srmacklem			*tl = cverf.lval[1];
2003191783Srmacklem		} else {
2004191783Srmacklem			*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
2005191783Srmacklem			nfscl_fillsattr(nd, vap, dvp, 0, 0);
2006191783Srmacklem		}
2007191783Srmacklem	} else {
2008191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZE0, 0);
2009191783Srmacklem	}
2010191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2011191783Srmacklem	if (error)
2012191783Srmacklem		return (error);
2013191783Srmacklem	if (nd->nd_repstat == 0) {
2014191783Srmacklem		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2015191783Srmacklem		if (error)
2016191783Srmacklem			goto nfsmout;
2017191783Srmacklem	}
2018191783Srmacklem	if (nd->nd_flag & ND_NFSV3)
2019191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2020191783Srmacklem	if (nd->nd_repstat != 0 && error == 0)
2021191783Srmacklem		error = nd->nd_repstat;
2022191783Srmacklemnfsmout:
2023191783Srmacklem	mbuf_freem(nd->nd_mrep);
2024191783Srmacklem	return (error);
2025191783Srmacklem}
2026191783Srmacklem
2027191783Srmacklemstatic int
2028191783Srmacklemnfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
2029191783Srmacklem    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
2030191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2031191783Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2032191783Srmacklem    int *dattrflagp, void *dstuff, int *unlockedp)
2033191783Srmacklem{
2034191783Srmacklem	u_int32_t *tl;
2035191783Srmacklem	int error = 0, deleg, newone, ret, acesize, limitby;
2036191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2037191783Srmacklem	struct nfsclopen *op;
2038191783Srmacklem	struct nfscldeleg *dp = NULL;
2039191783Srmacklem	struct nfsnode *np;
2040191783Srmacklem	struct nfsfh *nfhp;
2041191783Srmacklem	nfsattrbit_t attrbits;
2042191783Srmacklem	nfsv4stateid_t stateid;
2043191783Srmacklem	u_int32_t rflags;
2044244042Srmacklem	struct nfsmount *nmp;
2045317393Srmacklem	struct nfsclsession *tsep;
2046191783Srmacklem
2047244042Srmacklem	nmp = VFSTONFS(dvp->v_mount);
2048264681Srmacklem	np = VTONFS(dvp);
2049191783Srmacklem	*unlockedp = 0;
2050191783Srmacklem	*nfhpp = NULL;
2051191783Srmacklem	*dpp = NULL;
2052191783Srmacklem	*attrflagp = 0;
2053191783Srmacklem	*dattrflagp = 0;
2054191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2055191783Srmacklem		return (ENAMETOOLONG);
2056191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_CREATE, dvp);
2057191783Srmacklem	/*
2058191783Srmacklem	 * For V4, this is actually an Open op.
2059191783Srmacklem	 */
2060191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2061191783Srmacklem	*tl++ = txdr_unsigned(owp->nfsow_seqid);
2062191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
2063191783Srmacklem	    NFSV4OPEN_ACCESSREAD);
2064191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
2065317393Srmacklem	tsep = nfsmnt_mdssession(nmp);
2066317393Srmacklem	*tl++ = tsep->nfsess_clientid.lval[0];
2067317393Srmacklem	*tl = tsep->nfsess_clientid.lval[1];
2068191783Srmacklem	(void) nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
2069191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2070191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
2071191783Srmacklem	if (fmode & O_EXCL) {
2072244042Srmacklem		if (NFSHASNFSV4N(nmp)) {
2073244042Srmacklem			if (NFSHASSESSPERSIST(nmp)) {
2074244042Srmacklem				/* Use GUARDED for persistent sessions. */
2075244042Srmacklem				*tl = txdr_unsigned(NFSCREATE_GUARDED);
2076244042Srmacklem				nfscl_fillsattr(nd, vap, dvp, 0, 0);
2077244042Srmacklem			} else {
2078244042Srmacklem				/* Otherwise, use EXCLUSIVE4_1. */
2079244042Srmacklem				*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE41);
2080244042Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
2081244042Srmacklem				*tl++ = cverf.lval[0];
2082244042Srmacklem				*tl = cverf.lval[1];
2083244042Srmacklem				nfscl_fillsattr(nd, vap, dvp, 0, 0);
2084244042Srmacklem			}
2085244042Srmacklem		} else {
2086244042Srmacklem			/* NFSv4.0 */
2087244042Srmacklem			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE);
2088244042Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
2089244042Srmacklem			*tl++ = cverf.lval[0];
2090244042Srmacklem			*tl = cverf.lval[1];
2091244042Srmacklem		}
2092191783Srmacklem	} else {
2093191783Srmacklem		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
2094191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, 0, 0);
2095191783Srmacklem	}
2096191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2097191783Srmacklem	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
2098191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2099264681Srmacklem	/* Get the new file's handle and attributes. */
2100191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2101191783Srmacklem	*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2102191783Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
2103191783Srmacklem	NFSGETATTR_ATTRBIT(&attrbits);
2104191783Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
2105264681Srmacklem	/* Get the directory's post-op attributes. */
2106264681Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2107264681Srmacklem	*tl = txdr_unsigned(NFSV4OP_PUTFH);
2108264681Srmacklem	(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 0);
2109264681Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2110264681Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
2111264681Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
2112191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2113191783Srmacklem	if (error)
2114191783Srmacklem		return (error);
2115191783Srmacklem	NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
2116191783Srmacklem	if (nd->nd_repstat == 0) {
2117191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
2118191783Srmacklem		    6 * NFSX_UNSIGNED);
2119191783Srmacklem		stateid.seqid = *tl++;
2120191783Srmacklem		stateid.other[0] = *tl++;
2121191783Srmacklem		stateid.other[1] = *tl++;
2122191783Srmacklem		stateid.other[2] = *tl;
2123191783Srmacklem		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
2124191783Srmacklem		(void) nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2125191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
2126191783Srmacklem		deleg = fxdr_unsigned(int, *tl);
2127191783Srmacklem		if (deleg == NFSV4OPEN_DELEGATEREAD ||
2128191783Srmacklem		    deleg == NFSV4OPEN_DELEGATEWRITE) {
2129191783Srmacklem			if (!(owp->nfsow_clp->nfsc_flags &
2130191783Srmacklem			      NFSCLFLAGS_FIRSTDELEG))
2131191783Srmacklem				owp->nfsow_clp->nfsc_flags |=
2132191783Srmacklem				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
2133191783Srmacklem			MALLOC(dp, struct nfscldeleg *,
2134191783Srmacklem			    sizeof (struct nfscldeleg) + NFSX_V4FHMAX,
2135191783Srmacklem			    M_NFSCLDELEG, M_WAITOK);
2136191783Srmacklem			LIST_INIT(&dp->nfsdl_owner);
2137191783Srmacklem			LIST_INIT(&dp->nfsdl_lock);
2138191783Srmacklem			dp->nfsdl_clp = owp->nfsow_clp;
2139191783Srmacklem			newnfs_copyincred(cred, &dp->nfsdl_cred);
2140191783Srmacklem			nfscl_lockinit(&dp->nfsdl_rwlock);
2141191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
2142191783Srmacklem			    NFSX_UNSIGNED);
2143191783Srmacklem			dp->nfsdl_stateid.seqid = *tl++;
2144191783Srmacklem			dp->nfsdl_stateid.other[0] = *tl++;
2145191783Srmacklem			dp->nfsdl_stateid.other[1] = *tl++;
2146191783Srmacklem			dp->nfsdl_stateid.other[2] = *tl++;
2147191783Srmacklem			ret = fxdr_unsigned(int, *tl);
2148191783Srmacklem			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
2149191783Srmacklem				dp->nfsdl_flags = NFSCLDL_WRITE;
2150191783Srmacklem				/*
2151191783Srmacklem				 * Indicates how much the file can grow.
2152191783Srmacklem				 */
2153191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *,
2154191783Srmacklem				    3 * NFSX_UNSIGNED);
2155191783Srmacklem				limitby = fxdr_unsigned(int, *tl++);
2156191783Srmacklem				switch (limitby) {
2157191783Srmacklem				case NFSV4OPEN_LIMITSIZE:
2158191783Srmacklem					dp->nfsdl_sizelimit = fxdr_hyper(tl);
2159191783Srmacklem					break;
2160191783Srmacklem				case NFSV4OPEN_LIMITBLOCKS:
2161191783Srmacklem					dp->nfsdl_sizelimit =
2162191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl++);
2163191783Srmacklem					dp->nfsdl_sizelimit *=
2164191783Srmacklem					    fxdr_unsigned(u_int64_t, *tl);
2165191783Srmacklem					break;
2166191783Srmacklem				default:
2167191783Srmacklem					error = NFSERR_BADXDR;
2168191783Srmacklem					goto nfsmout;
2169297793Spfg				}
2170191783Srmacklem			} else {
2171191783Srmacklem				dp->nfsdl_flags = NFSCLDL_READ;
2172191783Srmacklem			}
2173191783Srmacklem			if (ret)
2174191783Srmacklem				dp->nfsdl_flags |= NFSCLDL_RECALL;
2175191783Srmacklem			error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
2176191783Srmacklem			    &acesize, p);
2177191783Srmacklem			if (error)
2178191783Srmacklem				goto nfsmout;
2179191783Srmacklem		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
2180191783Srmacklem			error = NFSERR_BADXDR;
2181191783Srmacklem			goto nfsmout;
2182191783Srmacklem		}
2183191783Srmacklem		error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2184191783Srmacklem		if (error)
2185191783Srmacklem			goto nfsmout;
2186264681Srmacklem		/* Get rid of the PutFH and Getattr status values. */
2187264681Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
2188264681Srmacklem		/* Load the directory attributes. */
2189264681Srmacklem		error = nfsm_loadattr(nd, dnap);
2190264681Srmacklem		if (error)
2191264681Srmacklem			goto nfsmout;
2192264681Srmacklem		*dattrflagp = 1;
2193191783Srmacklem		if (dp != NULL && *attrflagp) {
2194191783Srmacklem			dp->nfsdl_change = nnap->na_filerev;
2195191783Srmacklem			dp->nfsdl_modtime = nnap->na_mtime;
2196191783Srmacklem			dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
2197191783Srmacklem		}
2198191783Srmacklem		/*
2199191783Srmacklem		 * We can now complete the Open state.
2200191783Srmacklem		 */
2201191783Srmacklem		nfhp = *nfhpp;
2202191783Srmacklem		if (dp != NULL) {
2203191783Srmacklem			dp->nfsdl_fhlen = nfhp->nfh_len;
2204191783Srmacklem			NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh, nfhp->nfh_len);
2205191783Srmacklem		}
2206191783Srmacklem		/*
2207191783Srmacklem		 * Get an Open structure that will be
2208191783Srmacklem		 * attached to the OpenOwner, acquired already.
2209191783Srmacklem		 */
2210191783Srmacklem		error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len,
2211191783Srmacklem		    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
2212191783Srmacklem		    cred, p, NULL, &op, &newone, NULL, 0);
2213191783Srmacklem		if (error)
2214191783Srmacklem			goto nfsmout;
2215191783Srmacklem		op->nfso_stateid = stateid;
2216191783Srmacklem		newnfs_copyincred(cred, &op->nfso_cred);
2217191783Srmacklem		if ((rflags & NFSV4OPEN_RESULTCONFIRM)) {
2218191783Srmacklem		    do {
2219191783Srmacklem			ret = nfsrpc_openconfirm(dvp, nfhp->nfh_fh,
2220191783Srmacklem			    nfhp->nfh_len, op, cred, p);
2221191783Srmacklem			if (ret == NFSERR_DELAY)
2222207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_create");
2223191783Srmacklem		    } while (ret == NFSERR_DELAY);
2224191783Srmacklem		    error = ret;
2225191783Srmacklem		}
2226191783Srmacklem
2227191783Srmacklem		/*
2228191783Srmacklem		 * If the server is handing out delegations, but we didn't
2229191783Srmacklem		 * get one because an OpenConfirm was required, try the
2230191783Srmacklem		 * Open again, to get a delegation. This is a harmless no-op,
2231191783Srmacklem		 * from a server's point of view.
2232191783Srmacklem		 */
2233191783Srmacklem		if ((rflags & NFSV4OPEN_RESULTCONFIRM) &&
2234191783Srmacklem		    (owp->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG) &&
2235191783Srmacklem		    !error && dp == NULL) {
2236191783Srmacklem		    do {
2237191783Srmacklem			ret = nfsrpc_openrpc(VFSTONFS(vnode_mount(dvp)), dvp,
2238191783Srmacklem			    np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
2239191783Srmacklem			    nfhp->nfh_fh, nfhp->nfh_len,
2240191783Srmacklem			    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), op,
2241191783Srmacklem			    name, namelen, &dp, 0, 0x0, cred, p, 0, 1);
2242191783Srmacklem			if (ret == NFSERR_DELAY)
2243207170Srmacklem			    (void) nfs_catnap(PZERO, ret, "nfs_crt2");
2244191783Srmacklem		    } while (ret == NFSERR_DELAY);
2245191783Srmacklem		    if (ret) {
2246281756Spfg			if (dp != NULL) {
2247191783Srmacklem				FREE((caddr_t)dp, M_NFSCLDELEG);
2248281756Spfg				dp = NULL;
2249281756Spfg			}
2250191783Srmacklem			if (ret == NFSERR_STALECLIENTID ||
2251244042Srmacklem			    ret == NFSERR_STALEDONTRECOVER ||
2252244042Srmacklem			    ret == NFSERR_BADSESSION)
2253191783Srmacklem				error = ret;
2254191783Srmacklem		    }
2255191783Srmacklem		}
2256317520Srmacklem		nfscl_openrelease(nmp, op, error, newone);
2257191783Srmacklem		*unlockedp = 1;
2258191783Srmacklem	}
2259191783Srmacklem	if (nd->nd_repstat != 0 && error == 0)
2260191783Srmacklem		error = nd->nd_repstat;
2261317393Srmacklem	if (error == NFSERR_STALECLIENTID)
2262191783Srmacklem		nfscl_initiate_recovery(owp->nfsow_clp);
2263191783Srmacklemnfsmout:
2264191783Srmacklem	if (!error)
2265191783Srmacklem		*dpp = dp;
2266191783Srmacklem	else if (dp != NULL)
2267191783Srmacklem		FREE((caddr_t)dp, M_NFSCLDELEG);
2268191783Srmacklem	mbuf_freem(nd->nd_mrep);
2269191783Srmacklem	return (error);
2270191783Srmacklem}
2271191783Srmacklem
2272191783Srmacklem/*
2273191783Srmacklem * Nfs remove rpc
2274191783Srmacklem */
2275361236Sfreqlabsint
2276191783Srmacklemnfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp,
2277191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp,
2278191783Srmacklem    void *dstuff)
2279191783Srmacklem{
2280191783Srmacklem	u_int32_t *tl;
2281191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2282191783Srmacklem	struct nfsnode *np;
2283191783Srmacklem	struct nfsmount *nmp;
2284191783Srmacklem	nfsv4stateid_t dstateid;
2285191783Srmacklem	int error, ret = 0, i;
2286191783Srmacklem
2287191783Srmacklem	*dattrflagp = 0;
2288191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2289191783Srmacklem		return (ENAMETOOLONG);
2290191783Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
2291191783Srmacklemtryagain:
2292191783Srmacklem	if (NFSHASNFSV4(nmp) && ret == 0) {
2293191783Srmacklem		ret = nfscl_removedeleg(vp, p, &dstateid);
2294191783Srmacklem		if (ret == 1) {
2295191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp);
2296191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
2297191783Srmacklem			    NFSX_UNSIGNED);
2298244042Srmacklem			if (NFSHASNFSV4N(nmp))
2299244042Srmacklem				*tl++ = 0;
2300244042Srmacklem			else
2301244042Srmacklem				*tl++ = dstateid.seqid;
2302191783Srmacklem			*tl++ = dstateid.other[0];
2303191783Srmacklem			*tl++ = dstateid.other[1];
2304191783Srmacklem			*tl++ = dstateid.other[2];
2305191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2306191783Srmacklem			np = VTONFS(dvp);
2307191783Srmacklem			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2308191783Srmacklem			    np->n_fhp->nfh_len, 0);
2309191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2310191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_REMOVE);
2311191783Srmacklem		}
2312191783Srmacklem	} else {
2313191783Srmacklem		ret = 0;
2314191783Srmacklem	}
2315191783Srmacklem	if (ret == 0)
2316191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp);
2317191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2318191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2319191783Srmacklem	if (error)
2320191783Srmacklem		return (error);
2321191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2322191783Srmacklem		/* For NFSv4, parse out any Delereturn replies. */
2323191783Srmacklem		if (ret > 0 && nd->nd_repstat != 0 &&
2324191783Srmacklem		    (nd->nd_flag & ND_NOMOREDATA)) {
2325191783Srmacklem			/*
2326191783Srmacklem			 * If the Delegreturn failed, try again without
2327191783Srmacklem			 * it. The server will Recall, as required.
2328191783Srmacklem			 */
2329191783Srmacklem			mbuf_freem(nd->nd_mrep);
2330191783Srmacklem			goto tryagain;
2331191783Srmacklem		}
2332191783Srmacklem		for (i = 0; i < (ret * 2); i++) {
2333191783Srmacklem			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2334191783Srmacklem			    ND_NFSV4) {
2335191783Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2336191783Srmacklem			    if (*(tl + 1))
2337191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
2338191783Srmacklem			}
2339191783Srmacklem		}
2340191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2341191783Srmacklem	}
2342191783Srmacklem	if (nd->nd_repstat && !error)
2343191783Srmacklem		error = nd->nd_repstat;
2344191783Srmacklemnfsmout:
2345191783Srmacklem	mbuf_freem(nd->nd_mrep);
2346191783Srmacklem	return (error);
2347191783Srmacklem}
2348191783Srmacklem
2349191783Srmacklem/*
2350191783Srmacklem * Do an nfs rename rpc.
2351191783Srmacklem */
2352361236Sfreqlabsint
2353191783Srmacklemnfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen,
2354191783Srmacklem    vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred *cred,
2355191783Srmacklem    NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap,
2356191783Srmacklem    int *fattrflagp, int *tattrflagp, void *fstuff, void *tstuff)
2357191783Srmacklem{
2358191783Srmacklem	u_int32_t *tl;
2359191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2360191783Srmacklem	struct nfsmount *nmp;
2361191783Srmacklem	struct nfsnode *np;
2362191783Srmacklem	nfsattrbit_t attrbits;
2363191783Srmacklem	nfsv4stateid_t fdstateid, tdstateid;
2364191783Srmacklem	int error = 0, ret = 0, gottd = 0, gotfd = 0, i;
2365191783Srmacklem
2366191783Srmacklem	*fattrflagp = 0;
2367191783Srmacklem	*tattrflagp = 0;
2368191783Srmacklem	nmp = VFSTONFS(vnode_mount(fdvp));
2369191783Srmacklem	if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN)
2370191783Srmacklem		return (ENAMETOOLONG);
2371191783Srmacklemtryagain:
2372191783Srmacklem	if (NFSHASNFSV4(nmp) && ret == 0) {
2373191783Srmacklem		ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp,
2374191783Srmacklem		    &tdstateid, &gottd, p);
2375191783Srmacklem		if (gotfd && gottd) {
2376191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME2, fvp);
2377191783Srmacklem		} else if (gotfd) {
2378191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, fvp);
2379191783Srmacklem		} else if (gottd) {
2380191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_RETDELEGRENAME1, tvp);
2381191783Srmacklem		}
2382191783Srmacklem		if (gotfd) {
2383191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2384244042Srmacklem			if (NFSHASNFSV4N(nmp))
2385244042Srmacklem				*tl++ = 0;
2386244042Srmacklem			else
2387244042Srmacklem				*tl++ = fdstateid.seqid;
2388191783Srmacklem			*tl++ = fdstateid.other[0];
2389191783Srmacklem			*tl++ = fdstateid.other[1];
2390191783Srmacklem			*tl = fdstateid.other[2];
2391191783Srmacklem			if (gottd) {
2392191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2393191783Srmacklem				*tl = txdr_unsigned(NFSV4OP_PUTFH);
2394191783Srmacklem				np = VTONFS(tvp);
2395191783Srmacklem				(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2396191783Srmacklem				    np->n_fhp->nfh_len, 0);
2397191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2398191783Srmacklem				*tl = txdr_unsigned(NFSV4OP_DELEGRETURN);
2399191783Srmacklem			}
2400191783Srmacklem		}
2401191783Srmacklem		if (gottd) {
2402191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
2403244042Srmacklem			if (NFSHASNFSV4N(nmp))
2404244042Srmacklem				*tl++ = 0;
2405244042Srmacklem			else
2406244042Srmacklem				*tl++ = tdstateid.seqid;
2407191783Srmacklem			*tl++ = tdstateid.other[0];
2408191783Srmacklem			*tl++ = tdstateid.other[1];
2409191783Srmacklem			*tl = tdstateid.other[2];
2410191783Srmacklem		}
2411191783Srmacklem		if (ret > 0) {
2412191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2413191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_PUTFH);
2414191783Srmacklem			np = VTONFS(fdvp);
2415191783Srmacklem			(void) nfsm_fhtom(nd, np->n_fhp->nfh_fh,
2416191783Srmacklem			    np->n_fhp->nfh_len, 0);
2417191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2418191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_SAVEFH);
2419191783Srmacklem		}
2420191783Srmacklem	} else {
2421191783Srmacklem		ret = 0;
2422191783Srmacklem	}
2423191783Srmacklem	if (ret == 0)
2424191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp);
2425191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2426191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2427191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2428191783Srmacklem		NFSWCCATTR_ATTRBIT(&attrbits);
2429191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2430191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2431191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2432191783Srmacklem		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2433191783Srmacklem		    VTONFS(tdvp)->n_fhp->nfh_len, 0);
2434191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2435191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2436191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2437191783Srmacklem		nd->nd_flag |= ND_V4WCCATTR;
2438191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2439191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_RENAME);
2440191783Srmacklem	}
2441191783Srmacklem	(void) nfsm_strtom(nd, fnameptr, fnamelen);
2442191783Srmacklem	if (!(nd->nd_flag & ND_NFSV4))
2443191783Srmacklem		(void) nfsm_fhtom(nd, VTONFS(tdvp)->n_fhp->nfh_fh,
2444191783Srmacklem			VTONFS(tdvp)->n_fhp->nfh_len, 0);
2445191783Srmacklem	(void) nfsm_strtom(nd, tnameptr, tnamelen);
2446191783Srmacklem	error = nfscl_request(nd, fdvp, p, cred, fstuff);
2447191783Srmacklem	if (error)
2448191783Srmacklem		return (error);
2449191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
2450191783Srmacklem		/* For NFSv4, parse out any Delereturn replies. */
2451191783Srmacklem		if (ret > 0 && nd->nd_repstat != 0 &&
2452191783Srmacklem		    (nd->nd_flag & ND_NOMOREDATA)) {
2453191783Srmacklem			/*
2454191783Srmacklem			 * If the Delegreturn failed, try again without
2455191783Srmacklem			 * it. The server will Recall, as required.
2456191783Srmacklem			 */
2457191783Srmacklem			mbuf_freem(nd->nd_mrep);
2458191783Srmacklem			goto tryagain;
2459191783Srmacklem		}
2460191783Srmacklem		for (i = 0; i < (ret * 2); i++) {
2461191783Srmacklem			if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
2462191783Srmacklem			    ND_NFSV4) {
2463191783Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2464191783Srmacklem			    if (*(tl + 1)) {
2465191783Srmacklem				if (i == 0 && ret > 1) {
2466191783Srmacklem				    /*
2467191783Srmacklem				     * If the Delegreturn failed, try again
2468191783Srmacklem				     * without it. The server will Recall, as
2469191783Srmacklem				     * required.
2470191783Srmacklem				     * If ret > 1, the first iteration of this
2471191783Srmacklem				     * loop is the second DelegReturn result.
2472191783Srmacklem				     */
2473191783Srmacklem				    mbuf_freem(nd->nd_mrep);
2474191783Srmacklem				    goto tryagain;
2475191783Srmacklem				} else {
2476191783Srmacklem				    nd->nd_flag |= ND_NOMOREDATA;
2477191783Srmacklem				}
2478191783Srmacklem			    }
2479191783Srmacklem			}
2480191783Srmacklem		}
2481191783Srmacklem		/* Now, the first wcc attribute reply. */
2482191783Srmacklem		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2483191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2484191783Srmacklem			if (*(tl + 1))
2485191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
2486191783Srmacklem		}
2487191783Srmacklem		error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL,
2488191783Srmacklem		    fstuff);
2489191783Srmacklem		/* and the second wcc attribute reply. */
2490191783Srmacklem		if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
2491191783Srmacklem		    !error) {
2492191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2493191783Srmacklem			if (*(tl + 1))
2494191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
2495191783Srmacklem		}
2496191783Srmacklem		if (!error)
2497191783Srmacklem			error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp,
2498191783Srmacklem			    NULL, tstuff);
2499191783Srmacklem	}
2500191783Srmacklem	if (nd->nd_repstat && !error)
2501191783Srmacklem		error = nd->nd_repstat;
2502191783Srmacklemnfsmout:
2503191783Srmacklem	mbuf_freem(nd->nd_mrep);
2504191783Srmacklem	return (error);
2505191783Srmacklem}
2506191783Srmacklem
2507191783Srmacklem/*
2508191783Srmacklem * nfs hard link create rpc
2509191783Srmacklem */
2510361236Sfreqlabsint
2511191783Srmacklemnfsrpc_link(vnode_t dvp, vnode_t vp, char *name, int namelen,
2512191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2513191783Srmacklem    struct nfsvattr *nap, int *attrflagp, int *dattrflagp, void *dstuff)
2514191783Srmacklem{
2515191783Srmacklem	u_int32_t *tl;
2516191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2517191783Srmacklem	nfsattrbit_t attrbits;
2518191783Srmacklem	int error = 0;
2519191783Srmacklem
2520191783Srmacklem	*attrflagp = 0;
2521191783Srmacklem	*dattrflagp = 0;
2522191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2523191783Srmacklem		return (ENAMETOOLONG);
2524191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_LINK, vp);
2525191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2526191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2527191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2528191783Srmacklem	}
2529191783Srmacklem	(void) nfsm_fhtom(nd, VTONFS(dvp)->n_fhp->nfh_fh,
2530191783Srmacklem		VTONFS(dvp)->n_fhp->nfh_len, 0);
2531191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2532191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2533191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2534191783Srmacklem		NFSWCCATTR_ATTRBIT(&attrbits);
2535191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2536191783Srmacklem		nd->nd_flag |= ND_V4WCCATTR;
2537191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2538191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_LINK);
2539191783Srmacklem	}
2540191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2541191783Srmacklem	error = nfscl_request(nd, vp, p, cred, dstuff);
2542191783Srmacklem	if (error)
2543191783Srmacklem		return (error);
2544191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
2545191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, dstuff);
2546191783Srmacklem		if (!error)
2547191783Srmacklem			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2548191783Srmacklem			    NULL, dstuff);
2549191783Srmacklem	} else if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
2550191783Srmacklem		/*
2551191783Srmacklem		 * First, parse out the PutFH and Getattr result.
2552191783Srmacklem		 */
2553191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2554191783Srmacklem		if (!(*(tl + 1)))
2555191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2556191783Srmacklem		if (*(tl + 1))
2557191783Srmacklem			nd->nd_flag |= ND_NOMOREDATA;
2558191783Srmacklem		/*
2559191783Srmacklem		 * Get the pre-op attributes.
2560191783Srmacklem		 */
2561191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2562191783Srmacklem	}
2563191783Srmacklem	if (nd->nd_repstat && !error)
2564191783Srmacklem		error = nd->nd_repstat;
2565191783Srmacklemnfsmout:
2566191783Srmacklem	mbuf_freem(nd->nd_mrep);
2567191783Srmacklem	return (error);
2568191783Srmacklem}
2569191783Srmacklem
2570191783Srmacklem/*
2571191783Srmacklem * nfs symbolic link create rpc
2572191783Srmacklem */
2573361236Sfreqlabsint
2574191783Srmacklemnfsrpc_symlink(vnode_t dvp, char *name, int namelen, char *target,
2575191783Srmacklem    struct vattr *vap, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2576191783Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2577191783Srmacklem    int *dattrflagp, void *dstuff)
2578191783Srmacklem{
2579191783Srmacklem	u_int32_t *tl;
2580191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2581191783Srmacklem	struct nfsmount *nmp;
2582191783Srmacklem	int slen, error = 0;
2583191783Srmacklem
2584191783Srmacklem	*nfhpp = NULL;
2585191783Srmacklem	*attrflagp = 0;
2586191783Srmacklem	*dattrflagp = 0;
2587191783Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
2588191783Srmacklem	slen = strlen(target);
2589191783Srmacklem	if (slen > NFS_MAXPATHLEN || namelen > NFS_MAXNAMLEN)
2590191783Srmacklem		return (ENAMETOOLONG);
2591191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_SYMLINK, dvp);
2592191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2593191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2594191783Srmacklem		*tl = txdr_unsigned(NFLNK);
2595191783Srmacklem		(void) nfsm_strtom(nd, target, slen);
2596191783Srmacklem	}
2597191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2598191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2599191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, 0, 0);
2600191783Srmacklem	if (!(nd->nd_flag & ND_NFSV4))
2601191783Srmacklem		(void) nfsm_strtom(nd, target, slen);
2602191783Srmacklem	if (nd->nd_flag & ND_NFSV2)
2603191783Srmacklem		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2604191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2605191783Srmacklem	if (error)
2606191783Srmacklem		return (error);
2607191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
2608191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2609191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) && !error) {
2610191783Srmacklem		if (!nd->nd_repstat)
2611191783Srmacklem			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2612191783Srmacklem		if (!error)
2613191783Srmacklem			error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp,
2614191783Srmacklem			    NULL, dstuff);
2615191783Srmacklem	}
2616191783Srmacklem	if (nd->nd_repstat && !error)
2617191783Srmacklem		error = nd->nd_repstat;
2618191783Srmacklem	mbuf_freem(nd->nd_mrep);
2619191783Srmacklem	/*
2620191783Srmacklem	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2621285066Srmacklem	 * Only do this if vfs.nfs.ignore_eexist is set.
2622285066Srmacklem	 * Never do this for NFSv4.1 or later minor versions, since sessions
2623285066Srmacklem	 * should guarantee "exactly once" RPC semantics.
2624191783Srmacklem	 */
2625285066Srmacklem	if (error == EEXIST && nfsignore_eexist != 0 && (!NFSHASNFSV4(nmp) ||
2626285066Srmacklem	    nmp->nm_minorvers == 0))
2627191783Srmacklem		error = 0;
2628191783Srmacklem	return (error);
2629191783Srmacklem}
2630191783Srmacklem
2631191783Srmacklem/*
2632191783Srmacklem * nfs make dir rpc
2633191783Srmacklem */
2634361236Sfreqlabsint
2635191783Srmacklemnfsrpc_mkdir(vnode_t dvp, char *name, int namelen, struct vattr *vap,
2636191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
2637191783Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
2638191783Srmacklem    int *dattrflagp, void *dstuff)
2639191783Srmacklem{
2640191783Srmacklem	u_int32_t *tl;
2641191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2642191783Srmacklem	nfsattrbit_t attrbits;
2643191783Srmacklem	int error = 0;
2644264705Srmacklem	struct nfsfh *fhp;
2645285066Srmacklem	struct nfsmount *nmp;
2646191783Srmacklem
2647191783Srmacklem	*nfhpp = NULL;
2648191783Srmacklem	*attrflagp = 0;
2649191783Srmacklem	*dattrflagp = 0;
2650285066Srmacklem	nmp = VFSTONFS(vnode_mount(dvp));
2651264705Srmacklem	fhp = VTONFS(dvp)->n_fhp;
2652191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2653191783Srmacklem		return (ENAMETOOLONG);
2654191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_MKDIR, dvp);
2655191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2656191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2657191783Srmacklem		*tl = txdr_unsigned(NFDIR);
2658191783Srmacklem	}
2659191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2660191783Srmacklem	nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
2661191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
2662191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
2663191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2664191783Srmacklem		*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2665191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2666191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2667264705Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2668264705Srmacklem		*tl = txdr_unsigned(NFSV4OP_PUTFH);
2669264705Srmacklem		(void) nfsm_fhtom(nd, fhp->nfh_fh, fhp->nfh_len, 0);
2670264705Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2671264705Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
2672264705Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
2673191783Srmacklem	}
2674191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2675191783Srmacklem	if (error)
2676191783Srmacklem		return (error);
2677191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
2678191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2679191783Srmacklem	if (!nd->nd_repstat && !error) {
2680191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
2681191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2682191783Srmacklem			error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
2683191783Srmacklem		}
2684191783Srmacklem		if (!error)
2685191783Srmacklem			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
2686264749Srmacklem		if (error == 0 && (nd->nd_flag & ND_NFSV4) != 0) {
2687264705Srmacklem			/* Get rid of the PutFH and Getattr status values. */
2688264705Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
2689264705Srmacklem			/* Load the directory attributes. */
2690264705Srmacklem			error = nfsm_loadattr(nd, dnap);
2691264705Srmacklem			if (error == 0)
2692264705Srmacklem				*dattrflagp = 1;
2693264705Srmacklem		}
2694191783Srmacklem	}
2695191783Srmacklem	if ((nd->nd_flag & ND_NFSV3) && !error)
2696191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2697191783Srmacklem	if (nd->nd_repstat && !error)
2698191783Srmacklem		error = nd->nd_repstat;
2699191783Srmacklemnfsmout:
2700191783Srmacklem	mbuf_freem(nd->nd_mrep);
2701191783Srmacklem	/*
2702285066Srmacklem	 * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry.
2703285066Srmacklem	 * Only do this if vfs.nfs.ignore_eexist is set.
2704285066Srmacklem	 * Never do this for NFSv4.1 or later minor versions, since sessions
2705285066Srmacklem	 * should guarantee "exactly once" RPC semantics.
2706191783Srmacklem	 */
2707285066Srmacklem	if (error == EEXIST && nfsignore_eexist != 0 && (!NFSHASNFSV4(nmp) ||
2708285066Srmacklem	    nmp->nm_minorvers == 0))
2709191783Srmacklem		error = 0;
2710191783Srmacklem	return (error);
2711191783Srmacklem}
2712191783Srmacklem
2713191783Srmacklem/*
2714191783Srmacklem * nfs remove directory call
2715191783Srmacklem */
2716361236Sfreqlabsint
2717191783Srmacklemnfsrpc_rmdir(vnode_t dvp, char *name, int namelen, struct ucred *cred,
2718191783Srmacklem    NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp, void *dstuff)
2719191783Srmacklem{
2720191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2721191783Srmacklem	int error = 0;
2722191783Srmacklem
2723191783Srmacklem	*dattrflagp = 0;
2724191783Srmacklem	if (namelen > NFS_MAXNAMLEN)
2725191783Srmacklem		return (ENAMETOOLONG);
2726191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_RMDIR, dvp);
2727191783Srmacklem	(void) nfsm_strtom(nd, name, namelen);
2728191783Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
2729191783Srmacklem	if (error)
2730191783Srmacklem		return (error);
2731191783Srmacklem	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
2732191783Srmacklem		error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, dstuff);
2733191783Srmacklem	if (nd->nd_repstat && !error)
2734191783Srmacklem		error = nd->nd_repstat;
2735191783Srmacklem	mbuf_freem(nd->nd_mrep);
2736191783Srmacklem	/*
2737191783Srmacklem	 * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry.
2738191783Srmacklem	 */
2739191783Srmacklem	if (error == ENOENT)
2740191783Srmacklem		error = 0;
2741191783Srmacklem	return (error);
2742191783Srmacklem}
2743191783Srmacklem
2744191783Srmacklem/*
2745191783Srmacklem * Readdir rpc.
2746191783Srmacklem * Always returns with either uio_resid unchanged, if you are at the
2747191783Srmacklem * end of the directory, or uio_resid == 0, with all DIRBLKSIZ chunks
2748191783Srmacklem * filled in.
2749191783Srmacklem * I felt this would allow caching of directory blocks more easily
2750191783Srmacklem * than returning a pertially filled block.
2751191783Srmacklem * Directory offset cookies:
2752191783Srmacklem * Oh my, what to do with them...
2753191783Srmacklem * I can think of three ways to deal with them:
2754191783Srmacklem * 1 - have the layer above these RPCs maintain a map between logical
2755191783Srmacklem *     directory byte offsets and the NFS directory offset cookies
2756191783Srmacklem * 2 - pass the opaque directory offset cookies up into userland
2757191783Srmacklem *     and let the libc functions deal with them, via the system call
2758191783Srmacklem * 3 - return them to userland in the "struct dirent", so future versions
2759298788Spfg *     of libc can use them and do whatever is necessary to make things work
2760191783Srmacklem *     above these rpc calls, in the meantime
2761191783Srmacklem * For now, I do #3 by "hiding" the directory offset cookies after the
2762191783Srmacklem * d_name field in struct dirent. This is space inside d_reclen that
2763191783Srmacklem * will be ignored by anything that doesn't know about them.
2764191783Srmacklem * The directory offset cookies are filled in as the last 8 bytes of
2765191783Srmacklem * each directory entry, after d_name. Someday, the userland libc
2766191783Srmacklem * functions may be able to use these. In the meantime, it satisfies
2767191783Srmacklem * OpenBSD's requirements for cookies being returned.
2768191783Srmacklem * If expects the directory offset cookie for the read to be in uio_offset
2769191783Srmacklem * and returns the one for the next entry after this directory block in
2770191783Srmacklem * there, as well.
2771191783Srmacklem */
2772361236Sfreqlabsint
2773191783Srmacklemnfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
2774191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
2775191783Srmacklem    int *eofp, void *stuff)
2776191783Srmacklem{
2777191783Srmacklem	int len, left;
2778191783Srmacklem	struct dirent *dp = NULL;
2779191783Srmacklem	u_int32_t *tl;
2780191783Srmacklem	nfsquad_t cookie, ncookie;
2781191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
2782191783Srmacklem	struct nfsnode *dnp = VTONFS(vp);
2783191783Srmacklem	struct nfsvattr nfsva;
2784191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
2785191783Srmacklem	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
2786191783Srmacklem	int reqsize, tryformoredirs = 1, readsize, eof = 0, gotmnton = 0;
2787191783Srmacklem	long dotfileid, dotdotfileid = 0;
2788191783Srmacklem	u_int32_t fakefileno = 0xffffffff, rderr;
2789191783Srmacklem	char *cp;
2790191783Srmacklem	nfsattrbit_t attrbits, dattrbits;
2791191783Srmacklem	u_int32_t *tl2 = NULL;
2792191783Srmacklem	size_t tresid;
2793191783Srmacklem
2794209120Skib	KASSERT(uiop->uio_iovcnt == 1 &&
2795209120Skib	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
2796209120Skib	    ("nfs readdirrpc bad uio"));
2797191783Srmacklem
2798191783Srmacklem	/*
2799191783Srmacklem	 * There is no point in reading a lot more than uio_resid, however
2800191783Srmacklem	 * adding one additional DIRBLKSIZ makes sense. Since uio_resid
2801191783Srmacklem	 * and nm_readdirsize are both exact multiples of DIRBLKSIZ, this
2802191783Srmacklem	 * will never make readsize > nm_readdirsize.
2803191783Srmacklem	 */
2804191783Srmacklem	readsize = nmp->nm_readdirsize;
2805191783Srmacklem	if (readsize > uio_uio_resid(uiop))
2806191783Srmacklem		readsize = uio_uio_resid(uiop) + DIRBLKSIZ;
2807191783Srmacklem
2808191783Srmacklem	*attrflagp = 0;
2809191783Srmacklem	if (eofp)
2810191783Srmacklem		*eofp = 0;
2811191783Srmacklem	tresid = uio_uio_resid(uiop);
2812191783Srmacklem	cookie.lval[0] = cookiep->nfsuquad[0];
2813191783Srmacklem	cookie.lval[1] = cookiep->nfsuquad[1];
2814191783Srmacklem	nd->nd_mrep = NULL;
2815191783Srmacklem
2816191783Srmacklem	/*
2817191783Srmacklem	 * For NFSv4, first create the "." and ".." entries.
2818191783Srmacklem	 */
2819191783Srmacklem	if (NFSHASNFSV4(nmp)) {
2820191783Srmacklem		reqsize = 6 * NFSX_UNSIGNED;
2821191783Srmacklem		NFSGETATTR_ATTRBIT(&dattrbits);
2822191783Srmacklem		NFSZERO_ATTRBIT(&attrbits);
2823191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
2824191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TYPE);
2825191783Srmacklem		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
2826191783Srmacklem		    NFSATTRBIT_MOUNTEDONFILEID)) {
2827191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits,
2828191783Srmacklem			    NFSATTRBIT_MOUNTEDONFILEID);
2829191783Srmacklem			gotmnton = 1;
2830191783Srmacklem		} else {
2831191783Srmacklem			/*
2832191783Srmacklem			 * Must fake it. Use the fileno, except when the
2833191783Srmacklem			 * fsid is != to that of the directory. For that
2834191783Srmacklem			 * case, generate a fake fileno that is not the same.
2835191783Srmacklem			 */
2836191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
2837191783Srmacklem			gotmnton = 0;
2838191783Srmacklem		}
2839191783Srmacklem
2840191783Srmacklem		/*
2841191783Srmacklem		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
2842191783Srmacklem		 */
2843191783Srmacklem		if (uiop->uio_offset == 0) {
2844191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
2845191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2846191783Srmacklem			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
2847191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
2848191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
2849191783Srmacklem			error = nfscl_request(nd, vp, p, cred, stuff);
2850191783Srmacklem			if (error)
2851191783Srmacklem			    return (error);
2852264672Srmacklem			dotfileid = 0;	/* Fake out the compiler. */
2853264672Srmacklem			if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
2854264672Srmacklem			    error = nfsm_loadattr(nd, &nfsva);
2855264672Srmacklem			    if (error != 0)
2856264672Srmacklem				goto nfsmout;
2857264672Srmacklem			    dotfileid = nfsva.na_fileid;
2858264672Srmacklem			}
2859191783Srmacklem			if (nd->nd_repstat == 0) {
2860264672Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
2861264672Srmacklem			    len = fxdr_unsigned(int, *(tl + 4));
2862191783Srmacklem			    if (len > 0 && len <= NFSX_V4FHMAX)
2863191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
2864191783Srmacklem			    else
2865191783Srmacklem				error = EPERM;
2866191783Srmacklem			    if (!error) {
2867191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
2868191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
2869191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
2870191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
2871191783Srmacklem				    NULL, NULL, NULL, p, cred);
2872191783Srmacklem				if (error) {
2873191783Srmacklem				    dotdotfileid = dotfileid;
2874191783Srmacklem				} else if (gotmnton) {
2875191783Srmacklem				    if (nfsva.na_mntonfileno != 0xffffffff)
2876191783Srmacklem					dotdotfileid = nfsva.na_mntonfileno;
2877191783Srmacklem				    else
2878191783Srmacklem					dotdotfileid = nfsva.na_fileid;
2879191783Srmacklem				} else if (nfsva.na_filesid[0] ==
2880191783Srmacklem				    dnp->n_vattr.na_filesid[0] &&
2881191783Srmacklem				    nfsva.na_filesid[1] ==
2882191783Srmacklem				    dnp->n_vattr.na_filesid[1]) {
2883191783Srmacklem				    dotdotfileid = nfsva.na_fileid;
2884191783Srmacklem				} else {
2885191783Srmacklem				    do {
2886191783Srmacklem					fakefileno--;
2887191783Srmacklem				    } while (fakefileno ==
2888191783Srmacklem					nfsva.na_fileid);
2889191783Srmacklem				    dotdotfileid = fakefileno;
2890191783Srmacklem				}
2891191783Srmacklem			    }
2892191783Srmacklem			} else if (nd->nd_repstat == NFSERR_NOENT) {
2893191783Srmacklem			    /*
2894191783Srmacklem			     * Lookupp returns NFSERR_NOENT when we are
2895191783Srmacklem			     * at the root, so just use the current dir.
2896191783Srmacklem			     */
2897191783Srmacklem			    nd->nd_repstat = 0;
2898191783Srmacklem			    dotdotfileid = dotfileid;
2899191783Srmacklem			} else {
2900191783Srmacklem			    error = nd->nd_repstat;
2901191783Srmacklem			}
2902191783Srmacklem			mbuf_freem(nd->nd_mrep);
2903191783Srmacklem			if (error)
2904191783Srmacklem			    return (error);
2905191783Srmacklem			nd->nd_mrep = NULL;
2906191783Srmacklem			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2907191783Srmacklem			dp->d_type = DT_DIR;
2908191783Srmacklem			dp->d_fileno = dotfileid;
2909191783Srmacklem			dp->d_namlen = 1;
2910191783Srmacklem			dp->d_name[0] = '.';
2911191783Srmacklem			dp->d_name[1] = '\0';
2912191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2913191783Srmacklem			/*
2914191783Srmacklem			 * Just make these offset cookie 0.
2915191783Srmacklem			 */
2916191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
2917191783Srmacklem			*tl++ = 0;
2918191783Srmacklem			*tl = 0;
2919191783Srmacklem			blksiz += dp->d_reclen;
2920191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
2921191783Srmacklem			uiop->uio_offset += dp->d_reclen;
2922191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
2923191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
2924191783Srmacklem			dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
2925191783Srmacklem			dp->d_type = DT_DIR;
2926191783Srmacklem			dp->d_fileno = dotdotfileid;
2927191783Srmacklem			dp->d_namlen = 2;
2928191783Srmacklem			dp->d_name[0] = '.';
2929191783Srmacklem			dp->d_name[1] = '.';
2930191783Srmacklem			dp->d_name[2] = '\0';
2931191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
2932191783Srmacklem			/*
2933191783Srmacklem			 * Just make these offset cookie 0.
2934191783Srmacklem			 */
2935191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
2936191783Srmacklem			*tl++ = 0;
2937191783Srmacklem			*tl = 0;
2938191783Srmacklem			blksiz += dp->d_reclen;
2939191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
2940191783Srmacklem			uiop->uio_offset += dp->d_reclen;
2941191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
2942191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
2943191783Srmacklem		}
2944191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_RDATTRERROR);
2945191783Srmacklem	} else {
2946191783Srmacklem		reqsize = 5 * NFSX_UNSIGNED;
2947191783Srmacklem	}
2948191783Srmacklem
2949191783Srmacklem
2950191783Srmacklem	/*
2951191783Srmacklem	 * Loop around doing readdir rpc's of size readsize.
2952191783Srmacklem	 * The stopping criteria is EOF or buffer full.
2953191783Srmacklem	 */
2954191783Srmacklem	while (more_dirs && bigenough) {
2955191783Srmacklem		*attrflagp = 0;
2956191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_READDIR, vp);
2957191783Srmacklem		if (nd->nd_flag & ND_NFSV2) {
2958191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2959191783Srmacklem			*tl++ = cookie.lval[1];
2960191783Srmacklem			*tl = txdr_unsigned(readsize);
2961191783Srmacklem		} else {
2962191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, reqsize);
2963191783Srmacklem			*tl++ = cookie.lval[0];
2964191783Srmacklem			*tl++ = cookie.lval[1];
2965191783Srmacklem			if (cookie.qval == 0) {
2966191783Srmacklem				*tl++ = 0;
2967191783Srmacklem				*tl++ = 0;
2968191783Srmacklem			} else {
2969191783Srmacklem				NFSLOCKNODE(dnp);
2970191783Srmacklem				*tl++ = dnp->n_cookieverf.nfsuquad[0];
2971191783Srmacklem				*tl++ = dnp->n_cookieverf.nfsuquad[1];
2972191783Srmacklem				NFSUNLOCKNODE(dnp);
2973191783Srmacklem			}
2974191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
2975191783Srmacklem				*tl++ = txdr_unsigned(readsize);
2976191783Srmacklem				*tl = txdr_unsigned(readsize);
2977191783Srmacklem				(void) nfsrv_putattrbit(nd, &attrbits);
2978191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2979191783Srmacklem				*tl = txdr_unsigned(NFSV4OP_GETATTR);
2980191783Srmacklem				(void) nfsrv_putattrbit(nd, &dattrbits);
2981191783Srmacklem			} else {
2982191783Srmacklem				*tl = txdr_unsigned(readsize);
2983191783Srmacklem			}
2984191783Srmacklem		}
2985191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
2986191783Srmacklem		if (error)
2987191783Srmacklem			return (error);
2988191783Srmacklem		if (!(nd->nd_flag & ND_NFSV2)) {
2989191783Srmacklem			if (nd->nd_flag & ND_NFSV3)
2990191783Srmacklem				error = nfscl_postop_attr(nd, nap, attrflagp,
2991191783Srmacklem				    stuff);
2992191783Srmacklem			if (!nd->nd_repstat && !error) {
2993191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
2994191783Srmacklem				NFSLOCKNODE(dnp);
2995191783Srmacklem				dnp->n_cookieverf.nfsuquad[0] = *tl++;
2996191783Srmacklem				dnp->n_cookieverf.nfsuquad[1] = *tl;
2997191783Srmacklem				NFSUNLOCKNODE(dnp);
2998191783Srmacklem			}
2999191783Srmacklem		}
3000191783Srmacklem		if (nd->nd_repstat || error) {
3001191783Srmacklem			if (!error)
3002191783Srmacklem				error = nd->nd_repstat;
3003191783Srmacklem			goto nfsmout;
3004191783Srmacklem		}
3005191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3006191783Srmacklem		more_dirs = fxdr_unsigned(int, *tl);
3007191783Srmacklem		if (!more_dirs)
3008191783Srmacklem			tryformoredirs = 0;
3009191783Srmacklem
3010298788Spfg		/* loop through the dir entries, doctoring them to 4bsd form */
3011191783Srmacklem		while (more_dirs && bigenough) {
3012191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
3013191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3014191783Srmacklem				ncookie.lval[0] = *tl++;
3015191783Srmacklem				ncookie.lval[1] = *tl++;
3016191783Srmacklem				len = fxdr_unsigned(int, *tl);
3017191783Srmacklem			} else if (nd->nd_flag & ND_NFSV3) {
3018191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3019220152Szack				nfsva.na_fileid = fxdr_hyper(tl);
3020220152Szack				tl += 2;
3021220152Szack				len = fxdr_unsigned(int, *tl);
3022191783Srmacklem			} else {
3023191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3024191783Srmacklem				nfsva.na_fileid =
3025191783Srmacklem				    fxdr_unsigned(long, *tl++);
3026191783Srmacklem				len = fxdr_unsigned(int, *tl);
3027191783Srmacklem			}
3028191783Srmacklem			if (len <= 0 || len > NFS_MAXNAMLEN) {
3029191783Srmacklem				error = EBADRPC;
3030191783Srmacklem				goto nfsmout;
3031191783Srmacklem			}
3032191783Srmacklem			tlen = NFSM_RNDUP(len);
3033191783Srmacklem			if (tlen == len)
3034191783Srmacklem				tlen += 4;  /* To ensure null termination */
3035191783Srmacklem			left = DIRBLKSIZ - blksiz;
3036191783Srmacklem			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > left) {
3037341074Smarkj				NFSBZERO(uio_iov_base(uiop), left);
3038191783Srmacklem				dp->d_reclen += left;
3039191783Srmacklem				uio_iov_base_add(uiop, left);
3040191783Srmacklem				uio_iov_len_add(uiop, -(left));
3041191783Srmacklem				uio_uio_resid_add(uiop, -(left));
3042191783Srmacklem				uiop->uio_offset += left;
3043191783Srmacklem				blksiz = 0;
3044191783Srmacklem			}
3045191783Srmacklem			if ((int)(tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
3046191783Srmacklem				bigenough = 0;
3047191783Srmacklem			if (bigenough) {
3048191783Srmacklem				dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3049191783Srmacklem				dp->d_namlen = len;
3050191783Srmacklem				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
3051191783Srmacklem				dp->d_type = DT_UNKNOWN;
3052191783Srmacklem				blksiz += dp->d_reclen;
3053191783Srmacklem				if (blksiz == DIRBLKSIZ)
3054191783Srmacklem					blksiz = 0;
3055191783Srmacklem				uio_uio_resid_add(uiop, -(DIRHDSIZ));
3056191783Srmacklem				uiop->uio_offset += DIRHDSIZ;
3057191783Srmacklem				uio_iov_base_add(uiop, DIRHDSIZ);
3058191783Srmacklem				uio_iov_len_add(uiop, -(DIRHDSIZ));
3059191783Srmacklem				error = nfsm_mbufuio(nd, uiop, len);
3060191783Srmacklem				if (error)
3061191783Srmacklem					goto nfsmout;
3062191783Srmacklem				cp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
3063191783Srmacklem				tlen -= len;
3064341074Smarkj				NFSBZERO(cp, tlen);
3065191783Srmacklem				cp += tlen;	/* points to cookie storage */
3066191783Srmacklem				tl2 = (u_int32_t *)cp;
3067191783Srmacklem				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
3068191783Srmacklem				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
3069191783Srmacklem				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
3070191783Srmacklem				uiop->uio_offset += (tlen + NFSX_HYPER);
3071191783Srmacklem			} else {
3072191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3073191783Srmacklem				if (error)
3074191783Srmacklem					goto nfsmout;
3075191783Srmacklem			}
3076191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
3077191783Srmacklem				rderr = 0;
3078191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
3079191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
3080191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3081191783Srmacklem				    NULL, NULL, &rderr, p, cred);
3082191783Srmacklem				if (error)
3083191783Srmacklem					goto nfsmout;
3084191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3085191783Srmacklem			} else if (nd->nd_flag & ND_NFSV3) {
3086191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3087191783Srmacklem				ncookie.lval[0] = *tl++;
3088191783Srmacklem				ncookie.lval[1] = *tl++;
3089191783Srmacklem			} else {
3090191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3091191783Srmacklem				ncookie.lval[0] = 0;
3092191783Srmacklem				ncookie.lval[1] = *tl++;
3093191783Srmacklem			}
3094191783Srmacklem			if (bigenough) {
3095191783Srmacklem			    if (nd->nd_flag & ND_NFSV4) {
3096191783Srmacklem				if (rderr) {
3097191783Srmacklem				    dp->d_fileno = 0;
3098191783Srmacklem				} else {
3099191783Srmacklem				    if (gotmnton) {
3100191783Srmacklem					if (nfsva.na_mntonfileno != 0xffffffff)
3101191783Srmacklem					    dp->d_fileno = nfsva.na_mntonfileno;
3102191783Srmacklem					else
3103191783Srmacklem					    dp->d_fileno = nfsva.na_fileid;
3104191783Srmacklem				    } else if (nfsva.na_filesid[0] ==
3105191783Srmacklem					dnp->n_vattr.na_filesid[0] &&
3106191783Srmacklem					nfsva.na_filesid[1] ==
3107191783Srmacklem					dnp->n_vattr.na_filesid[1]) {
3108191783Srmacklem					dp->d_fileno = nfsva.na_fileid;
3109191783Srmacklem				    } else {
3110191783Srmacklem					do {
3111191783Srmacklem					    fakefileno--;
3112191783Srmacklem					} while (fakefileno ==
3113191783Srmacklem					    nfsva.na_fileid);
3114191783Srmacklem					dp->d_fileno = fakefileno;
3115191783Srmacklem				    }
3116191783Srmacklem				    dp->d_type = vtonfs_dtype(nfsva.na_type);
3117191783Srmacklem				}
3118191783Srmacklem			    } else {
3119191783Srmacklem				dp->d_fileno = nfsva.na_fileid;
3120191783Srmacklem			    }
3121191783Srmacklem			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
3122191783Srmacklem				ncookie.lval[0];
3123191783Srmacklem			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
3124191783Srmacklem				ncookie.lval[1];
3125191783Srmacklem			}
3126191783Srmacklem			more_dirs = fxdr_unsigned(int, *tl);
3127191783Srmacklem		}
3128191783Srmacklem		/*
3129191783Srmacklem		 * If at end of rpc data, get the eof boolean
3130191783Srmacklem		 */
3131191783Srmacklem		if (!more_dirs) {
3132191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3133191783Srmacklem			eof = fxdr_unsigned(int, *tl);
3134191783Srmacklem			if (tryformoredirs)
3135191783Srmacklem				more_dirs = !eof;
3136191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
3137191783Srmacklem				error = nfscl_postop_attr(nd, nap, attrflagp,
3138191783Srmacklem				    stuff);
3139191783Srmacklem				if (error)
3140191783Srmacklem					goto nfsmout;
3141191783Srmacklem			}
3142191783Srmacklem		}
3143191783Srmacklem		mbuf_freem(nd->nd_mrep);
3144191783Srmacklem		nd->nd_mrep = NULL;
3145191783Srmacklem	}
3146191783Srmacklem	/*
3147191783Srmacklem	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3148191783Srmacklem	 * by increasing d_reclen for the last record.
3149191783Srmacklem	 */
3150191783Srmacklem	if (blksiz > 0) {
3151191783Srmacklem		left = DIRBLKSIZ - blksiz;
3152341074Smarkj		NFSBZERO(uio_iov_base(uiop), left);
3153191783Srmacklem		dp->d_reclen += left;
3154191783Srmacklem		uio_iov_base_add(uiop, left);
3155191783Srmacklem		uio_iov_len_add(uiop, -(left));
3156191783Srmacklem		uio_uio_resid_add(uiop, -(left));
3157191783Srmacklem		uiop->uio_offset += left;
3158191783Srmacklem	}
3159191783Srmacklem
3160191783Srmacklem	/*
3161191783Srmacklem	 * If returning no data, assume end of file.
3162191783Srmacklem	 * If not bigenough, return not end of file, since you aren't
3163191783Srmacklem	 *    returning all the data
3164191783Srmacklem	 * Otherwise, return the eof flag from the server.
3165191783Srmacklem	 */
3166191783Srmacklem	if (eofp) {
3167191783Srmacklem		if (tresid == ((size_t)(uio_uio_resid(uiop))))
3168191783Srmacklem			*eofp = 1;
3169191783Srmacklem		else if (!bigenough)
3170191783Srmacklem			*eofp = 0;
3171191783Srmacklem		else
3172191783Srmacklem			*eofp = eof;
3173191783Srmacklem	}
3174191783Srmacklem
3175291117Srmacklem	/*
3176291117Srmacklem	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
3177291117Srmacklem	 */
3178291117Srmacklem	while (uio_uio_resid(uiop) > 0 && ((size_t)(uio_uio_resid(uiop))) != tresid) {
3179291117Srmacklem		dp = (struct dirent *) CAST_DOWN(caddr_t, uio_iov_base(uiop));
3180341074Smarkj		NFSBZERO(dp, DIRBLKSIZ);
3181291117Srmacklem		dp->d_type = DT_UNKNOWN;
3182291117Srmacklem		tl = (u_int32_t *)&dp->d_name[4];
3183291117Srmacklem		*tl++ = cookie.lval[0];
3184291117Srmacklem		*tl = cookie.lval[1];
3185291117Srmacklem		dp->d_reclen = DIRBLKSIZ;
3186291117Srmacklem		uio_iov_base_add(uiop, DIRBLKSIZ);
3187291117Srmacklem		uio_iov_len_add(uiop, -(DIRBLKSIZ));
3188291117Srmacklem		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
3189291117Srmacklem		uiop->uio_offset += DIRBLKSIZ;
3190291117Srmacklem	}
3191291117Srmacklem
3192191783Srmacklemnfsmout:
3193191783Srmacklem	if (nd->nd_mrep != NULL)
3194191783Srmacklem		mbuf_freem(nd->nd_mrep);
3195191783Srmacklem	return (error);
3196191783Srmacklem}
3197191783Srmacklem
3198191783Srmacklem#ifndef APPLE
3199191783Srmacklem/*
3200191783Srmacklem * NFS V3 readdir plus RPC. Used in place of nfsrpc_readdir().
3201191783Srmacklem * (Also used for NFS V4 when mount flag set.)
3202191783Srmacklem * (ditto above w.r.t. multiple of DIRBLKSIZ, etc.)
3203191783Srmacklem */
3204361236Sfreqlabsint
3205191783Srmacklemnfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
3206191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
3207191783Srmacklem    int *eofp, void *stuff)
3208191783Srmacklem{
3209191783Srmacklem	int len, left;
3210191783Srmacklem	struct dirent *dp = NULL;
3211191783Srmacklem	u_int32_t *tl;
3212191783Srmacklem	vnode_t newvp = NULLVP;
3213191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3214191783Srmacklem	struct nameidata nami, *ndp = &nami;
3215191783Srmacklem	struct componentname *cnp = &ndp->ni_cnd;
3216191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3217191783Srmacklem	struct nfsnode *dnp = VTONFS(vp), *np;
3218191783Srmacklem	struct nfsvattr nfsva;
3219191783Srmacklem	struct nfsfh *nfhp;
3220191783Srmacklem	nfsquad_t cookie, ncookie;
3221191783Srmacklem	int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1;
3222191783Srmacklem	int attrflag, tryformoredirs = 1, eof = 0, gotmnton = 0;
3223220735Srmacklem	int isdotdot = 0, unlocknewvp = 0;
3224191783Srmacklem	long dotfileid, dotdotfileid = 0, fileno = 0;
3225191783Srmacklem	char *cp;
3226191783Srmacklem	nfsattrbit_t attrbits, dattrbits;
3227191783Srmacklem	size_t tresid;
3228191783Srmacklem	u_int32_t *tl2 = NULL, fakefileno = 0xffffffff, rderr;
3229232420Srmacklem	struct timespec dctime;
3230191783Srmacklem
3231209120Skib	KASSERT(uiop->uio_iovcnt == 1 &&
3232209120Skib	    (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0,
3233209120Skib	    ("nfs readdirplusrpc bad uio"));
3234232420Srmacklem	timespecclear(&dctime);
3235191783Srmacklem	*attrflagp = 0;
3236191783Srmacklem	if (eofp != NULL)
3237191783Srmacklem		*eofp = 0;
3238191783Srmacklem	ndp->ni_dvp = vp;
3239191783Srmacklem	nd->nd_mrep = NULL;
3240191783Srmacklem	cookie.lval[0] = cookiep->nfsuquad[0];
3241191783Srmacklem	cookie.lval[1] = cookiep->nfsuquad[1];
3242191783Srmacklem	tresid = uio_uio_resid(uiop);
3243191783Srmacklem
3244191783Srmacklem	/*
3245191783Srmacklem	 * For NFSv4, first create the "." and ".." entries.
3246191783Srmacklem	 */
3247191783Srmacklem	if (NFSHASNFSV4(nmp)) {
3248191783Srmacklem		NFSGETATTR_ATTRBIT(&dattrbits);
3249191783Srmacklem		NFSZERO_ATTRBIT(&attrbits);
3250191783Srmacklem		NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FILEID);
3251191783Srmacklem		if (NFSISSET_ATTRBIT(&dnp->n_vattr.na_suppattr,
3252191783Srmacklem		    NFSATTRBIT_MOUNTEDONFILEID)) {
3253191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits,
3254191783Srmacklem			    NFSATTRBIT_MOUNTEDONFILEID);
3255191783Srmacklem			gotmnton = 1;
3256191783Srmacklem		} else {
3257191783Srmacklem			/*
3258191783Srmacklem			 * Must fake it. Use the fileno, except when the
3259191783Srmacklem			 * fsid is != to that of the directory. For that
3260191783Srmacklem			 * case, generate a fake fileno that is not the same.
3261191783Srmacklem			 */
3262191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_FSID);
3263191783Srmacklem			gotmnton = 0;
3264191783Srmacklem		}
3265191783Srmacklem
3266191783Srmacklem		/*
3267191783Srmacklem		 * Joy, oh joy. For V4 we get to hand craft '.' and '..'.
3268191783Srmacklem		 */
3269191783Srmacklem		if (uiop->uio_offset == 0) {
3270191783Srmacklem			NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp);
3271191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3272191783Srmacklem			*tl++ = txdr_unsigned(NFSV4OP_GETFH);
3273191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3274191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
3275191783Srmacklem			error = nfscl_request(nd, vp, p, cred, stuff);
3276191783Srmacklem			if (error)
3277191783Srmacklem			    return (error);
3278264672Srmacklem			dotfileid = 0;	/* Fake out the compiler. */
3279264672Srmacklem			if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
3280264672Srmacklem			    error = nfsm_loadattr(nd, &nfsva);
3281264672Srmacklem			    if (error != 0)
3282264672Srmacklem				goto nfsmout;
3283264672Srmacklem			    dctime = nfsva.na_ctime;
3284264672Srmacklem			    dotfileid = nfsva.na_fileid;
3285264672Srmacklem			}
3286191783Srmacklem			if (nd->nd_repstat == 0) {
3287264672Srmacklem			    NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
3288264672Srmacklem			    len = fxdr_unsigned(int, *(tl + 4));
3289191783Srmacklem			    if (len > 0 && len <= NFSX_V4FHMAX)
3290191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3291191783Srmacklem			    else
3292191783Srmacklem				error = EPERM;
3293191783Srmacklem			    if (!error) {
3294191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 2*NFSX_UNSIGNED);
3295191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
3296191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
3297191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3298191783Srmacklem				    NULL, NULL, NULL, p, cred);
3299191783Srmacklem				if (error) {
3300191783Srmacklem				    dotdotfileid = dotfileid;
3301191783Srmacklem				} else if (gotmnton) {
3302191783Srmacklem				    if (nfsva.na_mntonfileno != 0xffffffff)
3303191783Srmacklem					dotdotfileid = nfsva.na_mntonfileno;
3304191783Srmacklem				    else
3305191783Srmacklem					dotdotfileid = nfsva.na_fileid;
3306191783Srmacklem				} else if (nfsva.na_filesid[0] ==
3307191783Srmacklem				    dnp->n_vattr.na_filesid[0] &&
3308191783Srmacklem				    nfsva.na_filesid[1] ==
3309191783Srmacklem				    dnp->n_vattr.na_filesid[1]) {
3310191783Srmacklem				    dotdotfileid = nfsva.na_fileid;
3311191783Srmacklem				} else {
3312191783Srmacklem				    do {
3313191783Srmacklem					fakefileno--;
3314191783Srmacklem				    } while (fakefileno ==
3315191783Srmacklem					nfsva.na_fileid);
3316191783Srmacklem				    dotdotfileid = fakefileno;
3317191783Srmacklem				}
3318191783Srmacklem			    }
3319191783Srmacklem			} else if (nd->nd_repstat == NFSERR_NOENT) {
3320191783Srmacklem			    /*
3321191783Srmacklem			     * Lookupp returns NFSERR_NOENT when we are
3322191783Srmacklem			     * at the root, so just use the current dir.
3323191783Srmacklem			     */
3324191783Srmacklem			    nd->nd_repstat = 0;
3325191783Srmacklem			    dotdotfileid = dotfileid;
3326191783Srmacklem			} else {
3327191783Srmacklem			    error = nd->nd_repstat;
3328191783Srmacklem			}
3329191783Srmacklem			mbuf_freem(nd->nd_mrep);
3330191783Srmacklem			if (error)
3331191783Srmacklem			    return (error);
3332191783Srmacklem			nd->nd_mrep = NULL;
3333191783Srmacklem			dp = (struct dirent *)uio_iov_base(uiop);
3334191783Srmacklem			dp->d_type = DT_DIR;
3335191783Srmacklem			dp->d_fileno = dotfileid;
3336191783Srmacklem			dp->d_namlen = 1;
3337191783Srmacklem			dp->d_name[0] = '.';
3338191783Srmacklem			dp->d_name[1] = '\0';
3339191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3340191783Srmacklem			/*
3341191783Srmacklem			 * Just make these offset cookie 0.
3342191783Srmacklem			 */
3343191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
3344191783Srmacklem			*tl++ = 0;
3345191783Srmacklem			*tl = 0;
3346191783Srmacklem			blksiz += dp->d_reclen;
3347191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
3348191783Srmacklem			uiop->uio_offset += dp->d_reclen;
3349191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
3350191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
3351191783Srmacklem			dp = (struct dirent *)uio_iov_base(uiop);
3352191783Srmacklem			dp->d_type = DT_DIR;
3353191783Srmacklem			dp->d_fileno = dotdotfileid;
3354191783Srmacklem			dp->d_namlen = 2;
3355191783Srmacklem			dp->d_name[0] = '.';
3356191783Srmacklem			dp->d_name[1] = '.';
3357191783Srmacklem			dp->d_name[2] = '\0';
3358191783Srmacklem			dp->d_reclen = DIRENT_SIZE(dp) + NFSX_HYPER;
3359191783Srmacklem			/*
3360191783Srmacklem			 * Just make these offset cookie 0.
3361191783Srmacklem			 */
3362191783Srmacklem			tl = (u_int32_t *)&dp->d_name[4];
3363191783Srmacklem			*tl++ = 0;
3364191783Srmacklem			*tl = 0;
3365191783Srmacklem			blksiz += dp->d_reclen;
3366191783Srmacklem			uio_uio_resid_add(uiop, -(dp->d_reclen));
3367191783Srmacklem			uiop->uio_offset += dp->d_reclen;
3368191783Srmacklem			uio_iov_base_add(uiop, dp->d_reclen);
3369191783Srmacklem			uio_iov_len_add(uiop, -(dp->d_reclen));
3370191783Srmacklem		}
3371191783Srmacklem		NFSREADDIRPLUS_ATTRBIT(&attrbits);
3372191783Srmacklem		if (gotmnton)
3373191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits,
3374191783Srmacklem			    NFSATTRBIT_MOUNTEDONFILEID);
3375191783Srmacklem	}
3376191783Srmacklem
3377191783Srmacklem	/*
3378191783Srmacklem	 * Loop around doing readdir rpc's of size nm_readdirsize.
3379191783Srmacklem	 * The stopping criteria is EOF or buffer full.
3380191783Srmacklem	 */
3381191783Srmacklem	while (more_dirs && bigenough) {
3382191783Srmacklem		*attrflagp = 0;
3383191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_READDIRPLUS, vp);
3384191783Srmacklem 		NFSM_BUILD(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
3385191783Srmacklem		*tl++ = cookie.lval[0];
3386191783Srmacklem		*tl++ = cookie.lval[1];
3387191783Srmacklem		if (cookie.qval == 0) {
3388191783Srmacklem			*tl++ = 0;
3389191783Srmacklem			*tl++ = 0;
3390191783Srmacklem		} else {
3391191783Srmacklem			NFSLOCKNODE(dnp);
3392191783Srmacklem			*tl++ = dnp->n_cookieverf.nfsuquad[0];
3393191783Srmacklem			*tl++ = dnp->n_cookieverf.nfsuquad[1];
3394191783Srmacklem			NFSUNLOCKNODE(dnp);
3395191783Srmacklem		}
3396191783Srmacklem		*tl++ = txdr_unsigned(nmp->nm_readdirsize);
3397191783Srmacklem		*tl = txdr_unsigned(nmp->nm_readdirsize);
3398191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
3399191783Srmacklem			(void) nfsrv_putattrbit(nd, &attrbits);
3400191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3401191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_GETATTR);
3402191783Srmacklem			(void) nfsrv_putattrbit(nd, &dattrbits);
3403191783Srmacklem		}
3404191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
3405191783Srmacklem		if (error)
3406191783Srmacklem			return (error);
3407191783Srmacklem		if (nd->nd_flag & ND_NFSV3)
3408191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3409191783Srmacklem		if (nd->nd_repstat || error) {
3410191783Srmacklem			if (!error)
3411191783Srmacklem				error = nd->nd_repstat;
3412191783Srmacklem			goto nfsmout;
3413191783Srmacklem		}
3414232420Srmacklem		if ((nd->nd_flag & ND_NFSV3) != 0 && *attrflagp != 0)
3415232420Srmacklem			dctime = nap->na_ctime;
3416191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3417191783Srmacklem		NFSLOCKNODE(dnp);
3418191783Srmacklem		dnp->n_cookieverf.nfsuquad[0] = *tl++;
3419191783Srmacklem		dnp->n_cookieverf.nfsuquad[1] = *tl++;
3420191783Srmacklem		NFSUNLOCKNODE(dnp);
3421191783Srmacklem		more_dirs = fxdr_unsigned(int, *tl);
3422191783Srmacklem		if (!more_dirs)
3423191783Srmacklem			tryformoredirs = 0;
3424191783Srmacklem
3425298788Spfg		/* loop through the dir entries, doctoring them to 4bsd form */
3426191783Srmacklem		while (more_dirs && bigenough) {
3427191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3428191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
3429191783Srmacklem				ncookie.lval[0] = *tl++;
3430191783Srmacklem				ncookie.lval[1] = *tl++;
3431191783Srmacklem			} else {
3432191783Srmacklem				fileno = fxdr_unsigned(long, *++tl);
3433191783Srmacklem				tl++;
3434191783Srmacklem			}
3435191783Srmacklem			len = fxdr_unsigned(int, *tl);
3436191783Srmacklem			if (len <= 0 || len > NFS_MAXNAMLEN) {
3437191783Srmacklem				error = EBADRPC;
3438191783Srmacklem				goto nfsmout;
3439191783Srmacklem			}
3440191783Srmacklem			tlen = NFSM_RNDUP(len);
3441191783Srmacklem			if (tlen == len)
3442191783Srmacklem				tlen += 4;  /* To ensure null termination */
3443191783Srmacklem			left = DIRBLKSIZ - blksiz;
3444191783Srmacklem			if ((tlen + DIRHDSIZ + NFSX_HYPER) > left) {
3445341074Smarkj				NFSBZERO(uio_iov_base(uiop), left);
3446191783Srmacklem				dp->d_reclen += left;
3447191783Srmacklem				uio_iov_base_add(uiop, left);
3448191783Srmacklem				uio_iov_len_add(uiop, -(left));
3449191783Srmacklem				uio_uio_resid_add(uiop, -(left));
3450191783Srmacklem				uiop->uio_offset += left;
3451191783Srmacklem				blksiz = 0;
3452191783Srmacklem			}
3453191783Srmacklem			if ((tlen + DIRHDSIZ + NFSX_HYPER) > uio_uio_resid(uiop))
3454191783Srmacklem				bigenough = 0;
3455191783Srmacklem			if (bigenough) {
3456191783Srmacklem				dp = (struct dirent *)uio_iov_base(uiop);
3457191783Srmacklem				dp->d_namlen = len;
3458191783Srmacklem				dp->d_reclen = tlen + DIRHDSIZ + NFSX_HYPER;
3459191783Srmacklem				dp->d_type = DT_UNKNOWN;
3460191783Srmacklem				blksiz += dp->d_reclen;
3461191783Srmacklem				if (blksiz == DIRBLKSIZ)
3462191783Srmacklem					blksiz = 0;
3463191783Srmacklem				uio_uio_resid_add(uiop, -(DIRHDSIZ));
3464191783Srmacklem				uiop->uio_offset += DIRHDSIZ;
3465191783Srmacklem				uio_iov_base_add(uiop, DIRHDSIZ);
3466191783Srmacklem				uio_iov_len_add(uiop, -(DIRHDSIZ));
3467191783Srmacklem				cnp->cn_nameptr = uio_iov_base(uiop);
3468191783Srmacklem				cnp->cn_namelen = len;
3469191783Srmacklem				NFSCNHASHZERO(cnp);
3470191783Srmacklem				error = nfsm_mbufuio(nd, uiop, len);
3471191783Srmacklem				if (error)
3472191783Srmacklem					goto nfsmout;
3473191783Srmacklem				cp = uio_iov_base(uiop);
3474191783Srmacklem				tlen -= len;
3475341074Smarkj				NFSBZERO(cp, tlen);
3476191783Srmacklem				cp += tlen;	/* points to cookie storage */
3477191783Srmacklem				tl2 = (u_int32_t *)cp;
3478220735Srmacklem				if (len == 2 && cnp->cn_nameptr[0] == '.' &&
3479220735Srmacklem				    cnp->cn_nameptr[1] == '.')
3480220735Srmacklem					isdotdot = 1;
3481220735Srmacklem				else
3482220735Srmacklem					isdotdot = 0;
3483191783Srmacklem				uio_iov_base_add(uiop, (tlen + NFSX_HYPER));
3484191783Srmacklem				uio_iov_len_add(uiop, -(tlen + NFSX_HYPER));
3485191783Srmacklem				uio_uio_resid_add(uiop, -(tlen + NFSX_HYPER));
3486191783Srmacklem				uiop->uio_offset += (tlen + NFSX_HYPER);
3487191783Srmacklem			} else {
3488191783Srmacklem				error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
3489191783Srmacklem				if (error)
3490191783Srmacklem					goto nfsmout;
3491191783Srmacklem			}
3492191783Srmacklem			nfhp = NULL;
3493191783Srmacklem			if (nd->nd_flag & ND_NFSV3) {
3494191783Srmacklem				NFSM_DISSECT(tl, u_int32_t *, 3*NFSX_UNSIGNED);
3495191783Srmacklem				ncookie.lval[0] = *tl++;
3496191783Srmacklem				ncookie.lval[1] = *tl++;
3497191783Srmacklem				attrflag = fxdr_unsigned(int, *tl);
3498191783Srmacklem				if (attrflag) {
3499191783Srmacklem				  error = nfsm_loadattr(nd, &nfsva);
3500191783Srmacklem				  if (error)
3501191783Srmacklem					goto nfsmout;
3502191783Srmacklem				}
3503191783Srmacklem				NFSM_DISSECT(tl,u_int32_t *,NFSX_UNSIGNED);
3504191783Srmacklem				if (*tl) {
3505191783Srmacklem					error = nfsm_getfh(nd, &nfhp);
3506191783Srmacklem					if (error)
3507191783Srmacklem					    goto nfsmout;
3508191783Srmacklem				}
3509191783Srmacklem				if (!attrflag && nfhp != NULL) {
3510191783Srmacklem					FREE((caddr_t)nfhp, M_NFSFH);
3511191783Srmacklem					nfhp = NULL;
3512191783Srmacklem				}
3513191783Srmacklem			} else {
3514191783Srmacklem				rderr = 0;
3515191783Srmacklem				nfsva.na_mntonfileno = 0xffffffff;
3516191783Srmacklem				error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
3517191783Srmacklem				    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
3518191783Srmacklem				    NULL, NULL, &rderr, p, cred);
3519191783Srmacklem				if (error)
3520191783Srmacklem					goto nfsmout;
3521191783Srmacklem			}
3522191783Srmacklem
3523191783Srmacklem			if (bigenough) {
3524191783Srmacklem			    if (nd->nd_flag & ND_NFSV4) {
3525191783Srmacklem				if (rderr) {
3526191783Srmacklem				    dp->d_fileno = 0;
3527191783Srmacklem				} else if (gotmnton) {
3528191783Srmacklem				    if (nfsva.na_mntonfileno != 0xffffffff)
3529191783Srmacklem					dp->d_fileno = nfsva.na_mntonfileno;
3530191783Srmacklem				    else
3531191783Srmacklem					dp->d_fileno = nfsva.na_fileid;
3532191783Srmacklem				} else if (nfsva.na_filesid[0] ==
3533191783Srmacklem				    dnp->n_vattr.na_filesid[0] &&
3534191783Srmacklem				    nfsva.na_filesid[1] ==
3535191783Srmacklem				    dnp->n_vattr.na_filesid[1]) {
3536191783Srmacklem				    dp->d_fileno = nfsva.na_fileid;
3537191783Srmacklem				} else {
3538191783Srmacklem				    do {
3539191783Srmacklem					fakefileno--;
3540191783Srmacklem				    } while (fakefileno ==
3541191783Srmacklem					nfsva.na_fileid);
3542191783Srmacklem				    dp->d_fileno = fakefileno;
3543191783Srmacklem				}
3544191783Srmacklem			    } else {
3545191783Srmacklem				dp->d_fileno = fileno;
3546191783Srmacklem			    }
3547191783Srmacklem			    *tl2++ = cookiep->nfsuquad[0] = cookie.lval[0] =
3548191783Srmacklem				ncookie.lval[0];
3549191783Srmacklem			    *tl2 = cookiep->nfsuquad[1] = cookie.lval[1] =
3550191783Srmacklem				ncookie.lval[1];
3551191783Srmacklem
3552191783Srmacklem			    if (nfhp != NULL) {
3553191783Srmacklem				if (NFSRV_CMPFH(nfhp->nfh_fh, nfhp->nfh_len,
3554191783Srmacklem				    dnp->n_fhp->nfh_fh, dnp->n_fhp->nfh_len)) {
3555191783Srmacklem				    VREF(vp);
3556191783Srmacklem				    newvp = vp;
3557191783Srmacklem				    unlocknewvp = 0;
3558191783Srmacklem				    FREE((caddr_t)nfhp, M_NFSFH);
3559191783Srmacklem				    np = dnp;
3560220735Srmacklem				} else if (isdotdot != 0) {
3561220735Srmacklem				    /*
3562220735Srmacklem				     * Skip doing a nfscl_nget() call for "..".
3563220735Srmacklem				     * There's a race between acquiring the nfs
3564220735Srmacklem				     * node here and lookups that look for the
3565220735Srmacklem				     * directory being read (in the parent).
3566220735Srmacklem				     * It would try to get a lock on ".." here,
3567220735Srmacklem				     * owning the lock on the directory being
3568220735Srmacklem				     * read. Lookup will hold the lock on ".."
3569220735Srmacklem				     * and try to acquire the lock on the
3570220735Srmacklem				     * directory being read.
3571220735Srmacklem				     * If the directory is unlocked/relocked,
3572220735Srmacklem				     * then there is a LOR with the buflock
3573220735Srmacklem				     * vp is relocked.
3574220735Srmacklem				     */
3575220735Srmacklem				    free(nfhp, M_NFSFH);
3576191783Srmacklem				} else {
3577191783Srmacklem				    error = nfscl_nget(vnode_mount(vp), vp,
3578220732Srmacklem				      nfhp, cnp, p, &np, NULL, LK_EXCLUSIVE);
3579191783Srmacklem				    if (!error) {
3580191783Srmacklem					newvp = NFSTOV(np);
3581191783Srmacklem					unlocknewvp = 1;
3582191783Srmacklem				    }
3583191783Srmacklem				}
3584191783Srmacklem				nfhp = NULL;
3585191783Srmacklem				if (newvp != NULLVP) {
3586191783Srmacklem				    error = nfscl_loadattrcache(&newvp,
3587191783Srmacklem					&nfsva, NULL, NULL, 0, 0);
3588191783Srmacklem				    if (error) {
3589191783Srmacklem					if (unlocknewvp)
3590191783Srmacklem					    vput(newvp);
3591191783Srmacklem					else
3592191783Srmacklem					    vrele(newvp);
3593191783Srmacklem					goto nfsmout;
3594191783Srmacklem				    }
3595191783Srmacklem				    dp->d_type =
3596191783Srmacklem					vtonfs_dtype(np->n_vattr.na_type);
3597191783Srmacklem				    ndp->ni_vp = newvp;
3598191783Srmacklem				    NFSCNHASH(cnp, HASHINIT);
3599232420Srmacklem				    if (cnp->cn_namelen <= NCHNAMLEN &&
3600232420Srmacklem					(newvp->v_type != VDIR ||
3601232420Srmacklem					 dctime.tv_sec != 0)) {
3602230394Sjhb					cache_enter_time(ndp->ni_dvp,
3603230394Sjhb					    ndp->ni_vp, cnp,
3604232420Srmacklem					    &nfsva.na_ctime,
3605232420Srmacklem					    newvp->v_type != VDIR ? NULL :
3606232420Srmacklem					    &dctime);
3607191783Srmacklem				    }
3608191783Srmacklem				    if (unlocknewvp)
3609191783Srmacklem					vput(newvp);
3610191783Srmacklem				    else
3611191783Srmacklem					vrele(newvp);
3612191783Srmacklem				    newvp = NULLVP;
3613191783Srmacklem				}
3614191783Srmacklem			    }
3615191783Srmacklem			} else if (nfhp != NULL) {
3616191783Srmacklem			    FREE((caddr_t)nfhp, M_NFSFH);
3617191783Srmacklem			}
3618191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3619191783Srmacklem			more_dirs = fxdr_unsigned(int, *tl);
3620191783Srmacklem		}
3621191783Srmacklem		/*
3622191783Srmacklem		 * If at end of rpc data, get the eof boolean
3623191783Srmacklem		 */
3624191783Srmacklem		if (!more_dirs) {
3625191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3626191783Srmacklem			eof = fxdr_unsigned(int, *tl);
3627191783Srmacklem			if (tryformoredirs)
3628191783Srmacklem				more_dirs = !eof;
3629191783Srmacklem			if (nd->nd_flag & ND_NFSV4) {
3630191783Srmacklem				error = nfscl_postop_attr(nd, nap, attrflagp,
3631191783Srmacklem				    stuff);
3632191783Srmacklem				if (error)
3633191783Srmacklem					goto nfsmout;
3634191783Srmacklem			}
3635191783Srmacklem		}
3636191783Srmacklem		mbuf_freem(nd->nd_mrep);
3637191783Srmacklem		nd->nd_mrep = NULL;
3638191783Srmacklem	}
3639191783Srmacklem	/*
3640191783Srmacklem	 * Fill last record, iff any, out to a multiple of DIRBLKSIZ
3641191783Srmacklem	 * by increasing d_reclen for the last record.
3642191783Srmacklem	 */
3643191783Srmacklem	if (blksiz > 0) {
3644191783Srmacklem		left = DIRBLKSIZ - blksiz;
3645341074Smarkj		NFSBZERO(uio_iov_base(uiop), left);
3646191783Srmacklem		dp->d_reclen += left;
3647191783Srmacklem		uio_iov_base_add(uiop, left);
3648191783Srmacklem		uio_iov_len_add(uiop, -(left));
3649191783Srmacklem		uio_uio_resid_add(uiop, -(left));
3650191783Srmacklem		uiop->uio_offset += left;
3651191783Srmacklem	}
3652191783Srmacklem
3653191783Srmacklem	/*
3654191783Srmacklem	 * If returning no data, assume end of file.
3655191783Srmacklem	 * If not bigenough, return not end of file, since you aren't
3656191783Srmacklem	 *    returning all the data
3657191783Srmacklem	 * Otherwise, return the eof flag from the server.
3658191783Srmacklem	 */
3659191783Srmacklem	if (eofp != NULL) {
3660191783Srmacklem		if (tresid == uio_uio_resid(uiop))
3661191783Srmacklem			*eofp = 1;
3662191783Srmacklem		else if (!bigenough)
3663191783Srmacklem			*eofp = 0;
3664191783Srmacklem		else
3665191783Srmacklem			*eofp = eof;
3666191783Srmacklem	}
3667191783Srmacklem
3668291117Srmacklem	/*
3669291117Srmacklem	 * Add extra empty records to any remaining DIRBLKSIZ chunks.
3670291117Srmacklem	 */
3671291117Srmacklem	while (uio_uio_resid(uiop) > 0 && uio_uio_resid(uiop) != tresid) {
3672291117Srmacklem		dp = (struct dirent *)uio_iov_base(uiop);
3673341074Smarkj		NFSBZERO(dp, DIRBLKSIZ);
3674291117Srmacklem		dp->d_type = DT_UNKNOWN;
3675291117Srmacklem		tl = (u_int32_t *)&dp->d_name[4];
3676291117Srmacklem		*tl++ = cookie.lval[0];
3677291117Srmacklem		*tl = cookie.lval[1];
3678291117Srmacklem		dp->d_reclen = DIRBLKSIZ;
3679291117Srmacklem		uio_iov_base_add(uiop, DIRBLKSIZ);
3680291117Srmacklem		uio_iov_len_add(uiop, -(DIRBLKSIZ));
3681291117Srmacklem		uio_uio_resid_add(uiop, -(DIRBLKSIZ));
3682291117Srmacklem		uiop->uio_offset += DIRBLKSIZ;
3683291117Srmacklem	}
3684291117Srmacklem
3685191783Srmacklemnfsmout:
3686191783Srmacklem	if (nd->nd_mrep != NULL)
3687191783Srmacklem		mbuf_freem(nd->nd_mrep);
3688191783Srmacklem	return (error);
3689191783Srmacklem}
3690191783Srmacklem#endif	/* !APPLE */
3691191783Srmacklem
3692191783Srmacklem/*
3693191783Srmacklem * Nfs commit rpc
3694191783Srmacklem */
3695361236Sfreqlabsint
3696191783Srmacklemnfsrpc_commit(vnode_t vp, u_quad_t offset, int cnt, struct ucred *cred,
3697244042Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
3698191783Srmacklem{
3699191783Srmacklem	u_int32_t *tl;
3700191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3701191783Srmacklem	nfsattrbit_t attrbits;
3702191783Srmacklem	int error;
3703244042Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3704191783Srmacklem
3705191783Srmacklem	*attrflagp = 0;
3706191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_COMMIT, vp);
3707191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3708191783Srmacklem	txdr_hyper(offset, tl);
3709191783Srmacklem	tl += 2;
3710191783Srmacklem	*tl = txdr_unsigned(cnt);
3711191783Srmacklem	if (nd->nd_flag & ND_NFSV4) {
3712191783Srmacklem		/*
3713191783Srmacklem		 * And do a Getattr op.
3714191783Srmacklem		 */
3715191783Srmacklem		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3716191783Srmacklem		*tl = txdr_unsigned(NFSV4OP_GETATTR);
3717191783Srmacklem		NFSGETATTR_ATTRBIT(&attrbits);
3718191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
3719191783Srmacklem	}
3720191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
3721191783Srmacklem	if (error)
3722191783Srmacklem		return (error);
3723191783Srmacklem	error = nfscl_wcc_data(nd, vp, nap, attrflagp, NULL, stuff);
3724191783Srmacklem	if (!error && !nd->nd_repstat) {
3725191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
3726244042Srmacklem		NFSLOCKMNT(nmp);
3727244042Srmacklem		if (NFSBCMP(nmp->nm_verf, tl, NFSX_VERF)) {
3728244042Srmacklem			NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
3729244042Srmacklem			nd->nd_repstat = NFSERR_STALEWRITEVERF;
3730244042Srmacklem		}
3731244042Srmacklem		NFSUNLOCKMNT(nmp);
3732191783Srmacklem		if (nd->nd_flag & ND_NFSV4)
3733191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
3734191783Srmacklem	}
3735191783Srmacklemnfsmout:
3736191783Srmacklem	if (!error && nd->nd_repstat)
3737191783Srmacklem		error = nd->nd_repstat;
3738191783Srmacklem	mbuf_freem(nd->nd_mrep);
3739191783Srmacklem	return (error);
3740191783Srmacklem}
3741191783Srmacklem
3742191783Srmacklem/*
3743191783Srmacklem * NFS byte range lock rpc.
3744191783Srmacklem * (Mostly just calls one of the three lower level RPC routines.)
3745191783Srmacklem */
3746361236Sfreqlabsint
3747191783Srmacklemnfsrpc_advlock(vnode_t vp, off_t size, int op, struct flock *fl,
3748222719Srmacklem    int reclaim, struct ucred *cred, NFSPROC_T *p, void *id, int flags)
3749191783Srmacklem{
3750191783Srmacklem	struct nfscllockowner *lp;
3751191783Srmacklem	struct nfsclclient *clp;
3752191783Srmacklem	struct nfsfh *nfhp;
3753191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
3754191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
3755191783Srmacklem	u_int64_t off, len;
3756191783Srmacklem	off_t start, end;
3757191783Srmacklem	u_int32_t clidrev = 0;
3758191783Srmacklem	int error = 0, newone = 0, expireret = 0, retrycnt, donelocally;
3759191783Srmacklem	int callcnt, dorpc;
3760191783Srmacklem
3761191783Srmacklem	/*
3762191783Srmacklem	 * Convert the flock structure into a start and end and do POSIX
3763191783Srmacklem	 * bounds checking.
3764191783Srmacklem	 */
3765191783Srmacklem	switch (fl->l_whence) {
3766191783Srmacklem	case SEEK_SET:
3767191783Srmacklem	case SEEK_CUR:
3768191783Srmacklem		/*
3769191783Srmacklem		 * Caller is responsible for adding any necessary offset
3770191783Srmacklem		 * when SEEK_CUR is used.
3771191783Srmacklem		 */
3772191783Srmacklem		start = fl->l_start;
3773191783Srmacklem		off = fl->l_start;
3774191783Srmacklem		break;
3775191783Srmacklem	case SEEK_END:
3776191783Srmacklem		start = size + fl->l_start;
3777191783Srmacklem		off = size + fl->l_start;
3778191783Srmacklem		break;
3779191783Srmacklem	default:
3780191783Srmacklem		return (EINVAL);
3781297793Spfg	}
3782191783Srmacklem	if (start < 0)
3783191783Srmacklem		return (EINVAL);
3784191783Srmacklem	if (fl->l_len != 0) {
3785191783Srmacklem		end = start + fl->l_len - 1;
3786191783Srmacklem		if (end < start)
3787191783Srmacklem			return (EINVAL);
3788191783Srmacklem	}
3789191783Srmacklem
3790191783Srmacklem	len = fl->l_len;
3791191783Srmacklem	if (len == 0)
3792191783Srmacklem		len = NFS64BITSSET;
3793191783Srmacklem	retrycnt = 0;
3794191783Srmacklem	do {
3795191783Srmacklem	    nd->nd_repstat = 0;
3796191783Srmacklem	    if (op == F_GETLK) {
3797244042Srmacklem		error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
3798191783Srmacklem		if (error)
3799191783Srmacklem			return (error);
3800222719Srmacklem		error = nfscl_lockt(vp, clp, off, len, fl, p, id, flags);
3801191783Srmacklem		if (!error) {
3802191783Srmacklem			clidrev = clp->nfsc_clientidrev;
3803191783Srmacklem			error = nfsrpc_lockt(nd, vp, clp, off, len, fl, cred,
3804222719Srmacklem			    p, id, flags);
3805191783Srmacklem		} else if (error == -1) {
3806191783Srmacklem			error = 0;
3807191783Srmacklem		}
3808191783Srmacklem		nfscl_clientrelease(clp);
3809191783Srmacklem	    } else if (op == F_UNLCK && fl->l_type == F_UNLCK) {
3810191783Srmacklem		/*
3811191783Srmacklem		 * We must loop around for all lockowner cases.
3812191783Srmacklem		 */
3813191783Srmacklem		callcnt = 0;
3814244042Srmacklem		error = nfscl_getcl(vnode_mount(vp), cred, p, 1, &clp);
3815191783Srmacklem		if (error)
3816191783Srmacklem			return (error);
3817191783Srmacklem		do {
3818191783Srmacklem		    error = nfscl_relbytelock(vp, off, len, cred, p, callcnt,
3819222719Srmacklem			clp, id, flags, &lp, &dorpc);
3820191783Srmacklem		    /*
3821191783Srmacklem		     * If it returns a NULL lp, we're done.
3822191783Srmacklem		     */
3823191783Srmacklem		    if (lp == NULL) {
3824191783Srmacklem			if (callcnt == 0)
3825191783Srmacklem			    nfscl_clientrelease(clp);
3826191783Srmacklem			else
3827222719Srmacklem			    nfscl_releasealllocks(clp, vp, p, id, flags);
3828191783Srmacklem			return (error);
3829191783Srmacklem		    }
3830191783Srmacklem		    if (nmp->nm_clp != NULL)
3831191783Srmacklem			clidrev = nmp->nm_clp->nfsc_clientidrev;
3832191783Srmacklem		    else
3833191783Srmacklem			clidrev = 0;
3834191783Srmacklem		    /*
3835191783Srmacklem		     * If the server doesn't support Posix lock semantics,
3836191783Srmacklem		     * only allow locks on the entire file, since it won't
3837191783Srmacklem		     * handle overlapping byte ranges.
3838191783Srmacklem		     * There might still be a problem when a lock
3839191783Srmacklem		     * upgrade/downgrade (read<->write) occurs, since the
3840191783Srmacklem		     * server "might" expect an unlock first?
3841191783Srmacklem		     */
3842191783Srmacklem		    if (dorpc && (lp->nfsl_open->nfso_posixlock ||
3843191783Srmacklem			(off == 0 && len == NFS64BITSSET))) {
3844191783Srmacklem			/*
3845191783Srmacklem			 * Since the lock records will go away, we must
3846191783Srmacklem			 * wait for grace and delay here.
3847191783Srmacklem			 */
3848191783Srmacklem			do {
3849191783Srmacklem			    error = nfsrpc_locku(nd, nmp, lp, off, len,
3850191783Srmacklem				NFSV4LOCKT_READ, cred, p, 0);
3851191783Srmacklem			    if ((nd->nd_repstat == NFSERR_GRACE ||
3852191783Srmacklem				 nd->nd_repstat == NFSERR_DELAY) &&
3853191783Srmacklem				error == 0)
3854207170Srmacklem				(void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3855207170Srmacklem				    "nfs_advlock");
3856191783Srmacklem			} while ((nd->nd_repstat == NFSERR_GRACE ||
3857191783Srmacklem			    nd->nd_repstat == NFSERR_DELAY) && error == 0);
3858191783Srmacklem		    }
3859191783Srmacklem		    callcnt++;
3860191783Srmacklem		} while (error == 0 && nd->nd_repstat == 0);
3861222719Srmacklem		nfscl_releasealllocks(clp, vp, p, id, flags);
3862191783Srmacklem	    } else if (op == F_SETLK) {
3863191783Srmacklem		error = nfscl_getbytelock(vp, off, len, fl->l_type, cred, p,
3864222719Srmacklem		    NULL, 0, id, flags, NULL, NULL, &lp, &newone, &donelocally);
3865191783Srmacklem		if (error || donelocally) {
3866191783Srmacklem			return (error);
3867191783Srmacklem		}
3868191783Srmacklem		if (nmp->nm_clp != NULL)
3869191783Srmacklem			clidrev = nmp->nm_clp->nfsc_clientidrev;
3870191783Srmacklem		else
3871191783Srmacklem			clidrev = 0;
3872191783Srmacklem		nfhp = VTONFS(vp)->n_fhp;
3873191783Srmacklem		if (!lp->nfsl_open->nfso_posixlock &&
3874191783Srmacklem		    (off != 0 || len != NFS64BITSSET)) {
3875191783Srmacklem			error = EINVAL;
3876191783Srmacklem		} else {
3877191783Srmacklem			error = nfsrpc_lock(nd, nmp, vp, nfhp->nfh_fh,
3878191783Srmacklem			    nfhp->nfh_len, lp, newone, reclaim, off,
3879191783Srmacklem			    len, fl->l_type, cred, p, 0);
3880191783Srmacklem		}
3881191783Srmacklem		if (!error)
3882191783Srmacklem			error = nd->nd_repstat;
3883191783Srmacklem		nfscl_lockrelease(lp, error, newone);
3884191783Srmacklem	    } else {
3885191783Srmacklem		error = EINVAL;
3886191783Srmacklem	    }
3887191783Srmacklem	    if (!error)
3888191783Srmacklem	        error = nd->nd_repstat;
3889191783Srmacklem	    if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
3890191783Srmacklem		error == NFSERR_STALEDONTRECOVER ||
3891244042Srmacklem		error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
3892244042Srmacklem		error == NFSERR_BADSESSION) {
3893207170Srmacklem		(void) nfs_catnap(PZERO, error, "nfs_advlock");
3894191783Srmacklem	    } else if ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID)
3895191783Srmacklem		&& clidrev != 0) {
3896191783Srmacklem		expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p);
3897191783Srmacklem		retrycnt++;
3898191783Srmacklem	    }
3899191783Srmacklem	} while (error == NFSERR_GRACE ||
3900191783Srmacklem	    error == NFSERR_STALECLIENTID || error == NFSERR_DELAY ||
3901191783Srmacklem	    error == NFSERR_STALEDONTRECOVER || error == NFSERR_STALESTATEID ||
3902244042Srmacklem	    error == NFSERR_BADSESSION ||
3903191783Srmacklem	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
3904191783Srmacklem	     expireret == 0 && clidrev != 0 && retrycnt < 4));
3905191783Srmacklem	if (error && retrycnt >= 4)
3906191783Srmacklem		error = EIO;
3907191783Srmacklem	return (error);
3908191783Srmacklem}
3909191783Srmacklem
3910191783Srmacklem/*
3911191783Srmacklem * The lower level routine for the LockT case.
3912191783Srmacklem */
3913361236Sfreqlabsint
3914191783Srmacklemnfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
3915191783Srmacklem    struct nfsclclient *clp, u_int64_t off, u_int64_t len, struct flock *fl,
3916222719Srmacklem    struct ucred *cred, NFSPROC_T *p, void *id, int flags)
3917191783Srmacklem{
3918191783Srmacklem	u_int32_t *tl;
3919191783Srmacklem	int error, type, size;
3920223747Srmacklem	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
3921223747Srmacklem	struct nfsnode *np;
3922244042Srmacklem	struct nfsmount *nmp;
3923317393Srmacklem	struct nfsclsession *tsep;
3924191783Srmacklem
3925244042Srmacklem	nmp = VFSTONFS(vp->v_mount);
3926191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_LOCKT, vp);
3927191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
3928191783Srmacklem	if (fl->l_type == F_RDLCK)
3929191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
3930191783Srmacklem	else
3931191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
3932191783Srmacklem	txdr_hyper(off, tl);
3933191783Srmacklem	tl += 2;
3934191783Srmacklem	txdr_hyper(len, tl);
3935191783Srmacklem	tl += 2;
3936317393Srmacklem	tsep = nfsmnt_mdssession(nmp);
3937317393Srmacklem	*tl++ = tsep->nfsess_clientid.lval[0];
3938317393Srmacklem	*tl = tsep->nfsess_clientid.lval[1];
3939222719Srmacklem	nfscl_filllockowner(id, own, flags);
3940223747Srmacklem	np = VTONFS(vp);
3941223747Srmacklem	NFSBCOPY(np->n_fhp->nfh_fh, &own[NFSV4CL_LOCKNAMELEN],
3942223747Srmacklem	    np->n_fhp->nfh_len);
3943223747Srmacklem	(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + np->n_fhp->nfh_len);
3944191783Srmacklem	error = nfscl_request(nd, vp, p, cred, NULL);
3945191783Srmacklem	if (error)
3946191783Srmacklem		return (error);
3947191783Srmacklem	if (nd->nd_repstat == 0) {
3948191783Srmacklem		fl->l_type = F_UNLCK;
3949191783Srmacklem	} else if (nd->nd_repstat == NFSERR_DENIED) {
3950191783Srmacklem		nd->nd_repstat = 0;
3951191783Srmacklem		fl->l_whence = SEEK_SET;
3952191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
3953191783Srmacklem		fl->l_start = fxdr_hyper(tl);
3954191783Srmacklem		tl += 2;
3955191783Srmacklem		len = fxdr_hyper(tl);
3956191783Srmacklem		tl += 2;
3957191783Srmacklem		if (len == NFS64BITSSET)
3958191783Srmacklem			fl->l_len = 0;
3959191783Srmacklem		else
3960191783Srmacklem			fl->l_len = len;
3961191783Srmacklem		type = fxdr_unsigned(int, *tl++);
3962191783Srmacklem		if (type == NFSV4LOCKT_WRITE)
3963191783Srmacklem			fl->l_type = F_WRLCK;
3964191783Srmacklem		else
3965191783Srmacklem			fl->l_type = F_RDLCK;
3966191783Srmacklem		/*
3967191783Srmacklem		 * XXX For now, I have no idea what to do with the
3968191783Srmacklem		 * conflicting lock_owner, so I'll just set the pid == 0
3969191783Srmacklem		 * and skip over the lock_owner.
3970191783Srmacklem		 */
3971191783Srmacklem		fl->l_pid = (pid_t)0;
3972191783Srmacklem		tl += 2;
3973191783Srmacklem		size = fxdr_unsigned(int, *tl);
3974191783Srmacklem		if (size < 0 || size > NFSV4_OPAQUELIMIT)
3975191783Srmacklem			error = EBADRPC;
3976191783Srmacklem		if (!error)
3977191783Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
3978317393Srmacklem	} else if (nd->nd_repstat == NFSERR_STALECLIENTID)
3979191783Srmacklem		nfscl_initiate_recovery(clp);
3980191783Srmacklemnfsmout:
3981191783Srmacklem	mbuf_freem(nd->nd_mrep);
3982191783Srmacklem	return (error);
3983191783Srmacklem}
3984191783Srmacklem
3985191783Srmacklem/*
3986191783Srmacklem * Lower level function that performs the LockU RPC.
3987191783Srmacklem */
3988191783Srmacklemstatic int
3989191783Srmacklemnfsrpc_locku(struct nfsrv_descript *nd, struct nfsmount *nmp,
3990191783Srmacklem    struct nfscllockowner *lp, u_int64_t off, u_int64_t len,
3991191783Srmacklem    u_int32_t type, struct ucred *cred, NFSPROC_T *p, int syscred)
3992191783Srmacklem{
3993191783Srmacklem	u_int32_t *tl;
3994191783Srmacklem	int error;
3995191783Srmacklem
3996191783Srmacklem	nfscl_reqstart(nd, NFSPROC_LOCKU, nmp, lp->nfsl_open->nfso_fh,
3997244042Srmacklem	    lp->nfsl_open->nfso_fhlen, NULL, NULL);
3998191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
3999191783Srmacklem	*tl++ = txdr_unsigned(type);
4000191783Srmacklem	*tl = txdr_unsigned(lp->nfsl_seqid);
4001191783Srmacklem	if (nfstest_outofseq &&
4002191783Srmacklem	    (arc4random() % nfstest_outofseq) == 0)
4003191783Srmacklem		*tl = txdr_unsigned(lp->nfsl_seqid + 1);
4004191783Srmacklem	tl++;
4005244042Srmacklem	if (NFSHASNFSV4N(nmp))
4006244042Srmacklem		*tl++ = 0;
4007244042Srmacklem	else
4008244042Srmacklem		*tl++ = lp->nfsl_stateid.seqid;
4009191783Srmacklem	*tl++ = lp->nfsl_stateid.other[0];
4010191783Srmacklem	*tl++ = lp->nfsl_stateid.other[1];
4011191783Srmacklem	*tl++ = lp->nfsl_stateid.other[2];
4012191783Srmacklem	txdr_hyper(off, tl);
4013191783Srmacklem	tl += 2;
4014191783Srmacklem	txdr_hyper(len, tl);
4015191783Srmacklem	if (syscred)
4016191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
4017191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4018244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4019191783Srmacklem	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
4020191783Srmacklem	if (error)
4021191783Srmacklem		return (error);
4022191783Srmacklem	if (nd->nd_repstat == 0) {
4023191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
4024191783Srmacklem		lp->nfsl_stateid.seqid = *tl++;
4025191783Srmacklem		lp->nfsl_stateid.other[0] = *tl++;
4026191783Srmacklem		lp->nfsl_stateid.other[1] = *tl++;
4027191783Srmacklem		lp->nfsl_stateid.other[2] = *tl;
4028317393Srmacklem	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
4029191783Srmacklem		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
4030191783Srmacklemnfsmout:
4031191783Srmacklem	mbuf_freem(nd->nd_mrep);
4032191783Srmacklem	return (error);
4033191783Srmacklem}
4034191783Srmacklem
4035191783Srmacklem/*
4036191783Srmacklem * The actual Lock RPC.
4037191783Srmacklem */
4038361236Sfreqlabsint
4039191783Srmacklemnfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
4040191783Srmacklem    u_int8_t *nfhp, int fhlen, struct nfscllockowner *lp, int newone,
4041191783Srmacklem    int reclaim, u_int64_t off, u_int64_t len, short type, struct ucred *cred,
4042191783Srmacklem    NFSPROC_T *p, int syscred)
4043191783Srmacklem{
4044191783Srmacklem	u_int32_t *tl;
4045191783Srmacklem	int error, size;
4046223747Srmacklem	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
4047317393Srmacklem	struct nfsclsession *tsep;
4048191783Srmacklem
4049244042Srmacklem	nfscl_reqstart(nd, NFSPROC_LOCK, nmp, nfhp, fhlen, NULL, NULL);
4050191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
4051191783Srmacklem	if (type == F_RDLCK)
4052191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
4053191783Srmacklem	else
4054191783Srmacklem		*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
4055191783Srmacklem	*tl++ = txdr_unsigned(reclaim);
4056191783Srmacklem	txdr_hyper(off, tl);
4057191783Srmacklem	tl += 2;
4058191783Srmacklem	txdr_hyper(len, tl);
4059191783Srmacklem	tl += 2;
4060191783Srmacklem	if (newone) {
4061191783Srmacklem	    *tl = newnfs_true;
4062191783Srmacklem	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID +
4063191783Srmacklem		2 * NFSX_UNSIGNED + NFSX_HYPER);
4064191783Srmacklem	    *tl++ = txdr_unsigned(lp->nfsl_open->nfso_own->nfsow_seqid);
4065244042Srmacklem	    if (NFSHASNFSV4N(nmp))
4066244042Srmacklem		*tl++ = 0;
4067244042Srmacklem	    else
4068244042Srmacklem		*tl++ = lp->nfsl_open->nfso_stateid.seqid;
4069191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_stateid.other[0];
4070191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_stateid.other[1];
4071191783Srmacklem	    *tl++ = lp->nfsl_open->nfso_stateid.other[2];
4072191783Srmacklem	    *tl++ = txdr_unsigned(lp->nfsl_seqid);
4073317393Srmacklem	    tsep = nfsmnt_mdssession(nmp);
4074317393Srmacklem	    *tl++ = tsep->nfsess_clientid.lval[0];
4075317393Srmacklem	    *tl = tsep->nfsess_clientid.lval[1];
4076223747Srmacklem	    NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
4077223747Srmacklem	    NFSBCOPY(nfhp, &own[NFSV4CL_LOCKNAMELEN], fhlen);
4078223747Srmacklem	    (void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
4079191783Srmacklem	} else {
4080191783Srmacklem	    *tl = newnfs_false;
4081191783Srmacklem	    NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
4082244042Srmacklem	    if (NFSHASNFSV4N(nmp))
4083244042Srmacklem		*tl++ = 0;
4084244042Srmacklem	    else
4085244042Srmacklem		*tl++ = lp->nfsl_stateid.seqid;
4086191783Srmacklem	    *tl++ = lp->nfsl_stateid.other[0];
4087191783Srmacklem	    *tl++ = lp->nfsl_stateid.other[1];
4088191783Srmacklem	    *tl++ = lp->nfsl_stateid.other[2];
4089191783Srmacklem	    *tl = txdr_unsigned(lp->nfsl_seqid);
4090191783Srmacklem	    if (nfstest_outofseq &&
4091191783Srmacklem		(arc4random() % nfstest_outofseq) == 0)
4092191783Srmacklem		    *tl = txdr_unsigned(lp->nfsl_seqid + 1);
4093191783Srmacklem	}
4094191783Srmacklem	if (syscred)
4095191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
4096191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
4097244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4098191783Srmacklem	if (error)
4099191783Srmacklem		return (error);
4100191783Srmacklem	if (newone)
4101191783Srmacklem	    NFSCL_INCRSEQID(lp->nfsl_open->nfso_own->nfsow_seqid, nd);
4102191783Srmacklem	NFSCL_INCRSEQID(lp->nfsl_seqid, nd);
4103191783Srmacklem	if (nd->nd_repstat == 0) {
4104191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
4105191783Srmacklem		lp->nfsl_stateid.seqid = *tl++;
4106191783Srmacklem		lp->nfsl_stateid.other[0] = *tl++;
4107191783Srmacklem		lp->nfsl_stateid.other[1] = *tl++;
4108191783Srmacklem		lp->nfsl_stateid.other[2] = *tl;
4109191783Srmacklem	} else if (nd->nd_repstat == NFSERR_DENIED) {
4110191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
4111191783Srmacklem		size = fxdr_unsigned(int, *(tl + 7));
4112191783Srmacklem		if (size < 0 || size > NFSV4_OPAQUELIMIT)
4113191783Srmacklem			error = EBADRPC;
4114191783Srmacklem		if (!error)
4115191783Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
4116317393Srmacklem	} else if (nd->nd_repstat == NFSERR_STALESTATEID)
4117191783Srmacklem		nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
4118191783Srmacklemnfsmout:
4119191783Srmacklem	mbuf_freem(nd->nd_mrep);
4120191783Srmacklem	return (error);
4121191783Srmacklem}
4122191783Srmacklem
4123191783Srmacklem/*
4124191783Srmacklem * nfs statfs rpc
4125191783Srmacklem * (always called with the vp for the mount point)
4126191783Srmacklem */
4127361236Sfreqlabsint
4128191783Srmacklemnfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
4129191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
4130191783Srmacklem    void *stuff)
4131191783Srmacklem{
4132191783Srmacklem	u_int32_t *tl = NULL;
4133191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4134191783Srmacklem	struct nfsmount *nmp;
4135191783Srmacklem	nfsattrbit_t attrbits;
4136191783Srmacklem	int error;
4137191783Srmacklem
4138191783Srmacklem	*attrflagp = 0;
4139191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4140191783Srmacklem	if (NFSHASNFSV4(nmp)) {
4141191783Srmacklem		/*
4142191783Srmacklem		 * For V4, you actually do a getattr.
4143191783Srmacklem		 */
4144191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
4145191783Srmacklem		NFSSTATFS_GETATTRBIT(&attrbits);
4146191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
4147191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
4148191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
4149191783Srmacklem		if (error)
4150191783Srmacklem			return (error);
4151191783Srmacklem		if (nd->nd_repstat == 0) {
4152191783Srmacklem			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
4153191783Srmacklem			    NULL, NULL, sbp, fsp, NULL, 0, NULL, NULL, NULL, p,
4154191783Srmacklem			    cred);
4155191783Srmacklem			if (!error) {
4156191783Srmacklem				nmp->nm_fsid[0] = nap->na_filesid[0];
4157191783Srmacklem				nmp->nm_fsid[1] = nap->na_filesid[1];
4158191783Srmacklem				NFSSETHASSETFSID(nmp);
4159191783Srmacklem				*attrflagp = 1;
4160191783Srmacklem			}
4161191783Srmacklem		} else {
4162191783Srmacklem			error = nd->nd_repstat;
4163191783Srmacklem		}
4164191783Srmacklem		if (error)
4165191783Srmacklem			goto nfsmout;
4166191783Srmacklem	} else {
4167191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_FSSTAT, vp);
4168191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
4169191783Srmacklem		if (error)
4170191783Srmacklem			return (error);
4171191783Srmacklem		if (nd->nd_flag & ND_NFSV3) {
4172191783Srmacklem			error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
4173191783Srmacklem			if (error)
4174191783Srmacklem				goto nfsmout;
4175191783Srmacklem		}
4176191783Srmacklem		if (nd->nd_repstat) {
4177191783Srmacklem			error = nd->nd_repstat;
4178191783Srmacklem			goto nfsmout;
4179191783Srmacklem		}
4180191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *,
4181191783Srmacklem		    NFSX_STATFS(nd->nd_flag & ND_NFSV3));
4182191783Srmacklem	}
4183191783Srmacklem	if (NFSHASNFSV3(nmp)) {
4184191783Srmacklem		sbp->sf_tbytes = fxdr_hyper(tl); tl += 2;
4185191783Srmacklem		sbp->sf_fbytes = fxdr_hyper(tl); tl += 2;
4186191783Srmacklem		sbp->sf_abytes = fxdr_hyper(tl); tl += 2;
4187191783Srmacklem		sbp->sf_tfiles = fxdr_hyper(tl); tl += 2;
4188191783Srmacklem		sbp->sf_ffiles = fxdr_hyper(tl); tl += 2;
4189191783Srmacklem		sbp->sf_afiles = fxdr_hyper(tl); tl += 2;
4190191783Srmacklem		sbp->sf_invarsec = fxdr_unsigned(u_int32_t, *tl);
4191191783Srmacklem	} else if (NFSHASNFSV4(nmp) == 0) {
4192191783Srmacklem		sbp->sf_tsize = fxdr_unsigned(u_int32_t, *tl++);
4193191783Srmacklem		sbp->sf_bsize = fxdr_unsigned(u_int32_t, *tl++);
4194191783Srmacklem		sbp->sf_blocks = fxdr_unsigned(u_int32_t, *tl++);
4195191783Srmacklem		sbp->sf_bfree = fxdr_unsigned(u_int32_t, *tl++);
4196191783Srmacklem		sbp->sf_bavail = fxdr_unsigned(u_int32_t, *tl);
4197191783Srmacklem	}
4198191783Srmacklemnfsmout:
4199191783Srmacklem	mbuf_freem(nd->nd_mrep);
4200191783Srmacklem	return (error);
4201191783Srmacklem}
4202191783Srmacklem
4203191783Srmacklem/*
4204191783Srmacklem * nfs pathconf rpc
4205191783Srmacklem */
4206361236Sfreqlabsint
4207191783Srmacklemnfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc,
4208191783Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp,
4209191783Srmacklem    void *stuff)
4210191783Srmacklem{
4211191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4212191783Srmacklem	struct nfsmount *nmp;
4213191783Srmacklem	u_int32_t *tl;
4214191783Srmacklem	nfsattrbit_t attrbits;
4215191783Srmacklem	int error;
4216191783Srmacklem
4217191783Srmacklem	*attrflagp = 0;
4218191783Srmacklem	nmp = VFSTONFS(vnode_mount(vp));
4219191783Srmacklem	if (NFSHASNFSV4(nmp)) {
4220191783Srmacklem		/*
4221191783Srmacklem		 * For V4, you actually do a getattr.
4222191783Srmacklem		 */
4223191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_GETATTR, vp);
4224191783Srmacklem		NFSPATHCONF_GETATTRBIT(&attrbits);
4225191783Srmacklem		(void) nfsrv_putattrbit(nd, &attrbits);
4226191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
4227191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
4228191783Srmacklem		if (error)
4229191783Srmacklem			return (error);
4230191783Srmacklem		if (nd->nd_repstat == 0) {
4231191783Srmacklem			error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
4232191783Srmacklem			    pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p,
4233191783Srmacklem			    cred);
4234191783Srmacklem			if (!error)
4235191783Srmacklem				*attrflagp = 1;
4236191783Srmacklem		} else {
4237191783Srmacklem			error = nd->nd_repstat;
4238191783Srmacklem		}
4239191783Srmacklem	} else {
4240191783Srmacklem		NFSCL_REQSTART(nd, NFSPROC_PATHCONF, vp);
4241191783Srmacklem		error = nfscl_request(nd, vp, p, cred, stuff);
4242191783Srmacklem		if (error)
4243191783Srmacklem			return (error);
4244191783Srmacklem		error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
4245191783Srmacklem		if (nd->nd_repstat && !error)
4246191783Srmacklem			error = nd->nd_repstat;
4247191783Srmacklem		if (!error) {
4248191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_V3PATHCONF);
4249191783Srmacklem			pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl++);
4250191783Srmacklem			pc->pc_namemax = fxdr_unsigned(u_int32_t, *tl++);
4251191783Srmacklem			pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl++);
4252191783Srmacklem			pc->pc_chownrestricted =
4253191783Srmacklem			    fxdr_unsigned(u_int32_t, *tl++);
4254191783Srmacklem			pc->pc_caseinsensitive =
4255191783Srmacklem			    fxdr_unsigned(u_int32_t, *tl++);
4256191783Srmacklem			pc->pc_casepreserving = fxdr_unsigned(u_int32_t, *tl);
4257191783Srmacklem		}
4258191783Srmacklem	}
4259191783Srmacklemnfsmout:
4260191783Srmacklem	mbuf_freem(nd->nd_mrep);
4261191783Srmacklem	return (error);
4262191783Srmacklem}
4263191783Srmacklem
4264191783Srmacklem/*
4265191783Srmacklem * nfs version 3 fsinfo rpc call
4266191783Srmacklem */
4267361236Sfreqlabsint
4268191783Srmacklemnfsrpc_fsinfo(vnode_t vp, struct nfsfsinfo *fsp, struct ucred *cred,
4269191783Srmacklem    NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp, void *stuff)
4270191783Srmacklem{
4271191783Srmacklem	u_int32_t *tl;
4272191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4273191783Srmacklem	int error;
4274191783Srmacklem
4275191783Srmacklem	*attrflagp = 0;
4276191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_FSINFO, vp);
4277191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
4278191783Srmacklem	if (error)
4279191783Srmacklem		return (error);
4280191783Srmacklem	error = nfscl_postop_attr(nd, nap, attrflagp, stuff);
4281191783Srmacklem	if (nd->nd_repstat && !error)
4282191783Srmacklem		error = nd->nd_repstat;
4283191783Srmacklem	if (!error) {
4284191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_V3FSINFO);
4285191783Srmacklem		fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *tl++);
4286191783Srmacklem		fsp->fs_rtpref = fxdr_unsigned(u_int32_t, *tl++);
4287191783Srmacklem		fsp->fs_rtmult = fxdr_unsigned(u_int32_t, *tl++);
4288191783Srmacklem		fsp->fs_wtmax = fxdr_unsigned(u_int32_t, *tl++);
4289191783Srmacklem		fsp->fs_wtpref = fxdr_unsigned(u_int32_t, *tl++);
4290191783Srmacklem		fsp->fs_wtmult = fxdr_unsigned(u_int32_t, *tl++);
4291191783Srmacklem		fsp->fs_dtpref = fxdr_unsigned(u_int32_t, *tl++);
4292191783Srmacklem		fsp->fs_maxfilesize = fxdr_hyper(tl);
4293191783Srmacklem		tl += 2;
4294191783Srmacklem		fxdr_nfsv3time(tl, &fsp->fs_timedelta);
4295191783Srmacklem		tl += 2;
4296191783Srmacklem		fsp->fs_properties = fxdr_unsigned(u_int32_t, *tl);
4297191783Srmacklem	}
4298191783Srmacklemnfsmout:
4299191783Srmacklem	mbuf_freem(nd->nd_mrep);
4300191783Srmacklem	return (error);
4301191783Srmacklem}
4302191783Srmacklem
4303191783Srmacklem/*
4304191783Srmacklem * This function performs the Renew RPC.
4305191783Srmacklem */
4306361236Sfreqlabsint
4307244042Srmacklemnfsrpc_renew(struct nfsclclient *clp, struct nfsclds *dsp, struct ucred *cred,
4308244042Srmacklem    NFSPROC_T *p)
4309191783Srmacklem{
4310191783Srmacklem	u_int32_t *tl;
4311191783Srmacklem	struct nfsrv_descript nfsd;
4312191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
4313191783Srmacklem	struct nfsmount *nmp;
4314191783Srmacklem	int error;
4315244042Srmacklem	struct nfssockreq *nrp;
4316317393Srmacklem	struct nfsclsession *tsep;
4317191783Srmacklem
4318191783Srmacklem	nmp = clp->nfsc_nmp;
4319191783Srmacklem	if (nmp == NULL)
4320191783Srmacklem		return (0);
4321317393Srmacklem	if (dsp == NULL)
4322317393Srmacklem		nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL, NULL);
4323317393Srmacklem	else
4324317393Srmacklem		nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL,
4325317393Srmacklem		    &dsp->nfsclds_sess);
4326244042Srmacklem	if (!NFSHASNFSV4N(nmp)) {
4327244042Srmacklem		/* NFSv4.1 just uses a Sequence Op and not a Renew. */
4328244042Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4329317393Srmacklem		tsep = nfsmnt_mdssession(nmp);
4330317393Srmacklem		*tl++ = tsep->nfsess_clientid.lval[0];
4331317393Srmacklem		*tl = tsep->nfsess_clientid.lval[1];
4332244042Srmacklem	}
4333317393Srmacklem	nrp = NULL;
4334317393Srmacklem	if (dsp != NULL)
4335317393Srmacklem		nrp = dsp->nfsclds_sockp;
4336244042Srmacklem	if (nrp == NULL)
4337244042Srmacklem		/* If NULL, use the MDS socket. */
4338244042Srmacklem		nrp = &nmp->nm_sockreq;
4339191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4340317393Srmacklem	if (dsp == NULL)
4341317393Srmacklem		error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
4342317393Srmacklem		    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4343317393Srmacklem	else
4344317393Srmacklem		error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
4345317393Srmacklem		    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
4346191783Srmacklem	if (error)
4347191783Srmacklem		return (error);
4348191783Srmacklem	error = nd->nd_repstat;
4349191783Srmacklem	mbuf_freem(nd->nd_mrep);
4350191783Srmacklem	return (error);
4351191783Srmacklem}
4352191783Srmacklem
4353191783Srmacklem/*
4354191783Srmacklem * This function performs the Releaselockowner RPC.
4355191783Srmacklem */
4356361236Sfreqlabsint
4357191783Srmacklemnfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
4358227760Srmacklem    uint8_t *fh, int fhlen, struct ucred *cred, NFSPROC_T *p)
4359191783Srmacklem{
4360191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4361191783Srmacklem	u_int32_t *tl;
4362191783Srmacklem	int error;
4363223747Srmacklem	uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
4364317393Srmacklem	struct nfsclsession *tsep;
4365191783Srmacklem
4366244042Srmacklem	if (NFSHASNFSV4N(nmp)) {
4367244042Srmacklem		/* For NFSv4.1, do a FreeStateID. */
4368244042Srmacklem		nfscl_reqstart(nd, NFSPROC_FREESTATEID, nmp, NULL, 0, NULL,
4369244042Srmacklem		    NULL);
4370244042Srmacklem		nfsm_stateidtom(nd, &lp->nfsl_stateid, NFSSTATEID_PUTSTATEID);
4371244042Srmacklem	} else {
4372244042Srmacklem		nfscl_reqstart(nd, NFSPROC_RELEASELCKOWN, nmp, NULL, 0, NULL,
4373244042Srmacklem		    NULL);
4374244042Srmacklem		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
4375317393Srmacklem		tsep = nfsmnt_mdssession(nmp);
4376317393Srmacklem		*tl++ = tsep->nfsess_clientid.lval[0];
4377317393Srmacklem		*tl = tsep->nfsess_clientid.lval[1];
4378244042Srmacklem		NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
4379244042Srmacklem		NFSBCOPY(fh, &own[NFSV4CL_LOCKNAMELEN], fhlen);
4380244042Srmacklem		(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
4381244042Srmacklem	}
4382191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4383191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4384244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4385191783Srmacklem	if (error)
4386191783Srmacklem		return (error);
4387191783Srmacklem	error = nd->nd_repstat;
4388191783Srmacklem	mbuf_freem(nd->nd_mrep);
4389191783Srmacklem	return (error);
4390191783Srmacklem}
4391191783Srmacklem
4392191783Srmacklem/*
4393191783Srmacklem * This function performs the Compound to get the mount pt FH.
4394191783Srmacklem */
4395361236Sfreqlabsint
4396191783Srmacklemnfsrpc_getdirpath(struct nfsmount *nmp, u_char *dirpath, struct ucred *cred,
4397191783Srmacklem    NFSPROC_T *p)
4398191783Srmacklem{
4399191783Srmacklem	u_int32_t *tl;
4400191783Srmacklem	struct nfsrv_descript nfsd;
4401191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
4402191783Srmacklem	u_char *cp, *cp2;
4403191783Srmacklem	int error, cnt, len, setnil;
4404191783Srmacklem	u_int32_t *opcntp;
4405191783Srmacklem
4406244042Srmacklem	nfscl_reqstart(nd, NFSPROC_PUTROOTFH, nmp, NULL, 0, &opcntp, NULL);
4407191783Srmacklem	cp = dirpath;
4408191783Srmacklem	cnt = 0;
4409191783Srmacklem	do {
4410191783Srmacklem		setnil = 0;
4411191783Srmacklem		while (*cp == '/')
4412191783Srmacklem			cp++;
4413191783Srmacklem		cp2 = cp;
4414191783Srmacklem		while (*cp2 != '\0' && *cp2 != '/')
4415191783Srmacklem			cp2++;
4416191783Srmacklem		if (*cp2 == '/') {
4417191783Srmacklem			setnil = 1;
4418191783Srmacklem			*cp2 = '\0';
4419191783Srmacklem		}
4420191783Srmacklem		if (cp2 != cp) {
4421191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4422191783Srmacklem			*tl = txdr_unsigned(NFSV4OP_LOOKUP);
4423191783Srmacklem			nfsm_strtom(nd, cp, strlen(cp));
4424191783Srmacklem			cnt++;
4425191783Srmacklem		}
4426191783Srmacklem		if (setnil)
4427191783Srmacklem			*cp2++ = '/';
4428191783Srmacklem		cp = cp2;
4429191783Srmacklem	} while (*cp != '\0');
4430244042Srmacklem	if (NFSHASNFSV4N(nmp))
4431244042Srmacklem		/* Has a Sequence Op done by nfscl_reqstart(). */
4432244042Srmacklem		*opcntp = txdr_unsigned(3 + cnt);
4433244042Srmacklem	else
4434244042Srmacklem		*opcntp = txdr_unsigned(2 + cnt);
4435191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
4436191783Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETFH);
4437191783Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4438191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4439244042Srmacklem		NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4440191783Srmacklem	if (error)
4441191783Srmacklem		return (error);
4442191783Srmacklem	if (nd->nd_repstat == 0) {
4443191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, (3 + 2 * cnt) * NFSX_UNSIGNED);
4444191783Srmacklem		tl += (2 + 2 * cnt);
4445191783Srmacklem		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
4446191783Srmacklem			len > NFSX_FHMAX) {
4447191783Srmacklem			nd->nd_repstat = NFSERR_BADXDR;
4448191783Srmacklem		} else {
4449191783Srmacklem			nd->nd_repstat = nfsrv_mtostr(nd, nmp->nm_fh, len);
4450191783Srmacklem			if (nd->nd_repstat == 0)
4451191783Srmacklem				nmp->nm_fhsize = len;
4452191783Srmacklem		}
4453191783Srmacklem	}
4454191783Srmacklem	error = nd->nd_repstat;
4455191783Srmacklemnfsmout:
4456191783Srmacklem	mbuf_freem(nd->nd_mrep);
4457191783Srmacklem	return (error);
4458191783Srmacklem}
4459191783Srmacklem
4460191783Srmacklem/*
4461191783Srmacklem * This function performs the Delegreturn RPC.
4462191783Srmacklem */
4463361236Sfreqlabsint
4464191783Srmacklemnfsrpc_delegreturn(struct nfscldeleg *dp, struct ucred *cred,
4465191783Srmacklem    struct nfsmount *nmp, NFSPROC_T *p, int syscred)
4466191783Srmacklem{
4467191783Srmacklem	u_int32_t *tl;
4468191783Srmacklem	struct nfsrv_descript nfsd;
4469191783Srmacklem	struct nfsrv_descript *nd = &nfsd;
4470191783Srmacklem	int error;
4471191783Srmacklem
4472191783Srmacklem	nfscl_reqstart(nd, NFSPROC_DELEGRETURN, nmp, dp->nfsdl_fh,
4473244042Srmacklem	    dp->nfsdl_fhlen, NULL, NULL);
4474191783Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
4475244042Srmacklem	if (NFSHASNFSV4N(nmp))
4476244042Srmacklem		*tl++ = 0;
4477244042Srmacklem	else
4478244042Srmacklem		*tl++ = dp->nfsdl_stateid.seqid;
4479191783Srmacklem	*tl++ = dp->nfsdl_stateid.other[0];
4480191783Srmacklem	*tl++ = dp->nfsdl_stateid.other[1];
4481191783Srmacklem	*tl = dp->nfsdl_stateid.other[2];
4482191783Srmacklem	if (syscred)
4483191783Srmacklem		nd->nd_flag |= ND_USEGSSNAME;
4484191783Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4485244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4486191783Srmacklem	if (error)
4487191783Srmacklem		return (error);
4488191783Srmacklem	error = nd->nd_repstat;
4489191783Srmacklem	mbuf_freem(nd->nd_mrep);
4490191783Srmacklem	return (error);
4491191783Srmacklem}
4492191783Srmacklem
4493191783Srmacklem/*
4494191783Srmacklem * nfs getacl call.
4495191783Srmacklem */
4496361236Sfreqlabsint
4497191783Srmacklemnfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4498191783Srmacklem    struct acl *aclp, void *stuff)
4499191783Srmacklem{
4500191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4501191783Srmacklem	int error;
4502191783Srmacklem	nfsattrbit_t attrbits;
4503191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4504191783Srmacklem
4505191783Srmacklem	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4506191783Srmacklem		return (EOPNOTSUPP);
4507191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_GETACL, vp);
4508191783Srmacklem	NFSZERO_ATTRBIT(&attrbits);
4509191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4510191783Srmacklem	(void) nfsrv_putattrbit(nd, &attrbits);
4511191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
4512191783Srmacklem	if (error)
4513191783Srmacklem		return (error);
4514191783Srmacklem	if (!nd->nd_repstat)
4515191783Srmacklem		error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
4516191783Srmacklem		    NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, p, cred);
4517191783Srmacklem	else
4518191783Srmacklem		error = nd->nd_repstat;
4519191783Srmacklem	mbuf_freem(nd->nd_mrep);
4520191783Srmacklem	return (error);
4521191783Srmacklem}
4522191783Srmacklem
4523191783Srmacklem/*
4524191783Srmacklem * nfs setacl call.
4525191783Srmacklem */
4526361236Sfreqlabsint
4527191783Srmacklemnfsrpc_setacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4528191783Srmacklem    struct acl *aclp, void *stuff)
4529191783Srmacklem{
4530191783Srmacklem	int error;
4531191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4532191783Srmacklem
4533191783Srmacklem	if (nfsrv_useacl == 0 || !NFSHASNFSV4(nmp))
4534191783Srmacklem		return (EOPNOTSUPP);
4535191783Srmacklem	error = nfsrpc_setattr(vp, NULL, aclp, cred, p, NULL, NULL, stuff);
4536191783Srmacklem	return (error);
4537191783Srmacklem}
4538191783Srmacklem
4539191783Srmacklem/*
4540191783Srmacklem * nfs setacl call.
4541191783Srmacklem */
4542191783Srmacklemstatic int
4543191783Srmacklemnfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
4544191783Srmacklem    struct acl *aclp, nfsv4stateid_t *stateidp, void *stuff)
4545191783Srmacklem{
4546191783Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4547191783Srmacklem	int error;
4548191783Srmacklem	nfsattrbit_t attrbits;
4549191783Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
4550191783Srmacklem
4551191783Srmacklem	if (!NFSHASNFSV4(nmp))
4552191783Srmacklem		return (EOPNOTSUPP);
4553191783Srmacklem	NFSCL_REQSTART(nd, NFSPROC_SETACL, vp);
4554191783Srmacklem	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
4555191783Srmacklem	NFSZERO_ATTRBIT(&attrbits);
4556191783Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
4557220645Srmacklem	(void) nfsv4_fillattr(nd, vnode_mount(vp), vp, aclp, NULL, NULL, 0,
4558220648Srmacklem	    &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0);
4559191783Srmacklem	error = nfscl_request(nd, vp, p, cred, stuff);
4560191783Srmacklem	if (error)
4561191783Srmacklem		return (error);
4562191783Srmacklem	/* Don't care about the pre/postop attributes */
4563191783Srmacklem	mbuf_freem(nd->nd_mrep);
4564191783Srmacklem	return (nd->nd_repstat);
4565191783Srmacklem}
4566244042Srmacklem
4567244042Srmacklem/*
4568244042Srmacklem * Do the NFSv4.1 Exchange ID.
4569244042Srmacklem */
4570244042Srmacklemint
4571244042Srmacklemnfsrpc_exchangeid(struct nfsmount *nmp, struct nfsclclient *clp,
4572244042Srmacklem    struct nfssockreq *nrp, uint32_t exchflags, struct nfsclds **dspp,
4573244042Srmacklem    struct ucred *cred, NFSPROC_T *p)
4574244042Srmacklem{
4575244042Srmacklem	uint32_t *tl, v41flags;
4576244042Srmacklem	struct nfsrv_descript nfsd;
4577244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
4578244042Srmacklem	struct nfsclds *dsp;
4579244042Srmacklem	struct timespec verstime;
4580244042Srmacklem	int error, len;
4581244042Srmacklem
4582244042Srmacklem	*dspp = NULL;
4583244042Srmacklem	nfscl_reqstart(nd, NFSPROC_EXCHANGEID, nmp, NULL, 0, NULL, NULL);
4584244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4585244042Srmacklem	*tl++ = txdr_unsigned(nfsboottime.tv_sec);	/* Client owner */
4586244042Srmacklem	*tl = txdr_unsigned(clp->nfsc_rev);
4587244042Srmacklem	(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
4588244042Srmacklem
4589244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
4590244042Srmacklem	*tl++ = txdr_unsigned(exchflags);
4591244042Srmacklem	*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE);
4592244042Srmacklem
4593244042Srmacklem	/* Set the implementation id4 */
4594244042Srmacklem	*tl = txdr_unsigned(1);
4595244042Srmacklem	(void) nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
4596244042Srmacklem	(void) nfsm_strtom(nd, version, strlen(version));
4597244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
4598244042Srmacklem	verstime.tv_sec = 1293840000;		/* Jan 1, 2011 */
4599244042Srmacklem	verstime.tv_nsec = 0;
4600244042Srmacklem	txdr_nfsv4time(&verstime, tl);
4601244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4602244042Srmacklem	error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
4603244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4604244042Srmacklem	NFSCL_DEBUG(1, "exchangeid err=%d reps=%d\n", error,
4605244042Srmacklem	    (int)nd->nd_repstat);
4606244042Srmacklem	if (error != 0)
4607244042Srmacklem		return (error);
4608244042Srmacklem	if (nd->nd_repstat == 0) {
4609244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, 6 * NFSX_UNSIGNED + NFSX_HYPER);
4610244042Srmacklem		len = fxdr_unsigned(int, *(tl + 7));
4611244042Srmacklem		if (len < 0 || len > NFSV4_OPAQUELIMIT) {
4612244042Srmacklem			error = NFSERR_BADXDR;
4613244042Srmacklem			goto nfsmout;
4614244042Srmacklem		}
4615317393Srmacklem		dsp = malloc(sizeof(struct nfsclds) + len + 1, M_NFSCLDS,
4616244042Srmacklem		    M_WAITOK | M_ZERO);
4617244042Srmacklem		dsp->nfsclds_expire = NFSD_MONOSEC + clp->nfsc_renew;
4618244042Srmacklem		dsp->nfsclds_servownlen = len;
4619244042Srmacklem		dsp->nfsclds_sess.nfsess_clientid.lval[0] = *tl++;
4620244042Srmacklem		dsp->nfsclds_sess.nfsess_clientid.lval[1] = *tl++;
4621244042Srmacklem		dsp->nfsclds_sess.nfsess_sequenceid =
4622244042Srmacklem		    fxdr_unsigned(uint32_t, *tl++);
4623244042Srmacklem		v41flags = fxdr_unsigned(uint32_t, *tl);
4624244042Srmacklem		if ((v41flags & NFSV4EXCH_USEPNFSMDS) != 0 &&
4625244042Srmacklem		    NFSHASPNFSOPT(nmp)) {
4626244042Srmacklem			NFSCL_DEBUG(1, "set PNFS\n");
4627244042Srmacklem			NFSLOCKMNT(nmp);
4628244042Srmacklem			nmp->nm_state |= NFSSTA_PNFS;
4629244042Srmacklem			NFSUNLOCKMNT(nmp);
4630244042Srmacklem			dsp->nfsclds_flags |= NFSCLDS_MDS;
4631244042Srmacklem		}
4632244042Srmacklem		if ((v41flags & NFSV4EXCH_USEPNFSDS) != 0)
4633244042Srmacklem			dsp->nfsclds_flags |= NFSCLDS_DS;
4634244042Srmacklem		if (len > 0)
4635244042Srmacklem			nd->nd_repstat = nfsrv_mtostr(nd,
4636244042Srmacklem			    dsp->nfsclds_serverown, len);
4637244042Srmacklem		if (nd->nd_repstat == 0) {
4638244042Srmacklem			mtx_init(&dsp->nfsclds_mtx, "nfsds", NULL, MTX_DEF);
4639244042Srmacklem			mtx_init(&dsp->nfsclds_sess.nfsess_mtx, "nfssession",
4640244042Srmacklem			    NULL, MTX_DEF);
4641244042Srmacklem			nfscl_initsessionslots(&dsp->nfsclds_sess);
4642244042Srmacklem			*dspp = dsp;
4643244042Srmacklem		} else
4644244042Srmacklem			free(dsp, M_NFSCLDS);
4645244042Srmacklem	}
4646244042Srmacklem	error = nd->nd_repstat;
4647244042Srmacklemnfsmout:
4648244042Srmacklem	mbuf_freem(nd->nd_mrep);
4649244042Srmacklem	return (error);
4650244042Srmacklem}
4651244042Srmacklem
4652244042Srmacklem/*
4653244042Srmacklem * Do the NFSv4.1 Create Session.
4654244042Srmacklem */
4655244042Srmacklemint
4656244042Srmacklemnfsrpc_createsession(struct nfsmount *nmp, struct nfsclsession *sep,
4657244042Srmacklem    struct nfssockreq *nrp, uint32_t sequenceid, int mds, struct ucred *cred,
4658244042Srmacklem    NFSPROC_T *p)
4659244042Srmacklem{
4660320615Srmacklem	uint32_t crflags, maxval, *tl;
4661244042Srmacklem	struct nfsrv_descript nfsd;
4662244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
4663244042Srmacklem	int error, irdcnt;
4664244042Srmacklem
4665321632Srmacklem	/* Make sure nm_rsize, nm_wsize is set. */
4666321632Srmacklem	if (nmp->nm_rsize > NFS_MAXBSIZE || nmp->nm_rsize == 0)
4667321632Srmacklem		nmp->nm_rsize = NFS_MAXBSIZE;
4668321632Srmacklem	if (nmp->nm_wsize > NFS_MAXBSIZE || nmp->nm_wsize == 0)
4669321632Srmacklem		nmp->nm_wsize = NFS_MAXBSIZE;
4670244042Srmacklem	nfscl_reqstart(nd, NFSPROC_CREATESESSION, nmp, NULL, 0, NULL, NULL);
4671244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED);
4672244042Srmacklem	*tl++ = sep->nfsess_clientid.lval[0];
4673244042Srmacklem	*tl++ = sep->nfsess_clientid.lval[1];
4674244042Srmacklem	*tl++ = txdr_unsigned(sequenceid);
4675244042Srmacklem	crflags = (NFSMNT_RDONLY(nmp->nm_mountp) ? 0 : NFSV4CRSESS_PERSIST);
4676317926Srmacklem	if (nfscl_enablecallb != 0 && nfs_numnfscbd > 0 && mds != 0)
4677244042Srmacklem		crflags |= NFSV4CRSESS_CONNBACKCHAN;
4678244042Srmacklem	*tl = txdr_unsigned(crflags);
4679244042Srmacklem
4680244042Srmacklem	/* Fill in fore channel attributes. */
4681244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4682244042Srmacklem	*tl++ = 0;				/* Header pad size */
4683320615Srmacklem	*tl++ = txdr_unsigned(nmp->nm_wsize + NFS_MAXXDR);/* Max request size */
4684320615Srmacklem	*tl++ = txdr_unsigned(nmp->nm_rsize + NFS_MAXXDR);/* Max reply size */
4685244042Srmacklem	*tl++ = txdr_unsigned(4096);		/* Max response size cached */
4686244042Srmacklem	*tl++ = txdr_unsigned(20);		/* Max operations */
4687244042Srmacklem	*tl++ = txdr_unsigned(64);		/* Max slots */
4688244042Srmacklem	*tl = 0;				/* No rdma ird */
4689244042Srmacklem
4690244042Srmacklem	/* Fill in back channel attributes. */
4691244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4692244042Srmacklem	*tl++ = 0;				/* Header pad size */
4693244042Srmacklem	*tl++ = txdr_unsigned(10000);		/* Max request size */
4694244042Srmacklem	*tl++ = txdr_unsigned(10000);		/* Max response size */
4695244042Srmacklem	*tl++ = txdr_unsigned(4096);		/* Max response size cached */
4696244042Srmacklem	*tl++ = txdr_unsigned(4);		/* Max operations */
4697244042Srmacklem	*tl++ = txdr_unsigned(NFSV4_CBSLOTS);	/* Max slots */
4698244042Srmacklem	*tl = 0;				/* No rdma ird */
4699244042Srmacklem
4700244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 8 * NFSX_UNSIGNED);
4701244042Srmacklem	*tl++ = txdr_unsigned(NFS_CALLBCKPROG);	/* Call back prog # */
4702244042Srmacklem
4703244042Srmacklem	/* Allow AUTH_SYS callbacks as uid, gid == 0. */
4704244042Srmacklem	*tl++ = txdr_unsigned(1);		/* Auth_sys only */
4705244042Srmacklem	*tl++ = txdr_unsigned(AUTH_SYS);	/* AUTH_SYS type */
4706244042Srmacklem	*tl++ = txdr_unsigned(nfsboottime.tv_sec); /* time stamp */
4707244042Srmacklem	*tl++ = 0;				/* Null machine name */
4708244042Srmacklem	*tl++ = 0;				/* Uid == 0 */
4709244042Srmacklem	*tl++ = 0;				/* Gid == 0 */
4710244042Srmacklem	*tl = 0;				/* No additional gids */
4711244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4712244042Srmacklem	error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred, NFS_PROG,
4713244042Srmacklem	    NFS_VER4, NULL, 1, NULL, NULL);
4714244042Srmacklem	if (error != 0)
4715244042Srmacklem		return (error);
4716244042Srmacklem	if (nd->nd_repstat == 0) {
4717244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID +
4718244042Srmacklem		    2 * NFSX_UNSIGNED);
4719244042Srmacklem		bcopy(tl, sep->nfsess_sessionid, NFSX_V4SESSIONID);
4720244042Srmacklem		tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
4721244042Srmacklem		sep->nfsess_sequenceid = fxdr_unsigned(uint32_t, *tl++);
4722244042Srmacklem		crflags = fxdr_unsigned(uint32_t, *tl);
4723244042Srmacklem		if ((crflags & NFSV4CRSESS_PERSIST) != 0 && mds != 0) {
4724244042Srmacklem			NFSLOCKMNT(nmp);
4725244042Srmacklem			nmp->nm_state |= NFSSTA_SESSPERSIST;
4726244042Srmacklem			NFSUNLOCKMNT(nmp);
4727244042Srmacklem		}
4728244042Srmacklem
4729244042Srmacklem		/* Get the fore channel slot count. */
4730244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4731320615Srmacklem		tl++;			/* Skip the header pad size. */
4732320615Srmacklem
4733320615Srmacklem		/* Make sure nm_wsize is small enough. */
4734320615Srmacklem		maxval = fxdr_unsigned(uint32_t, *tl++);
4735320615Srmacklem		while (maxval < nmp->nm_wsize + NFS_MAXXDR) {
4736320615Srmacklem			if (nmp->nm_wsize > 8096)
4737320615Srmacklem				nmp->nm_wsize /= 2;
4738320615Srmacklem			else
4739320615Srmacklem				break;
4740320615Srmacklem		}
4741320615Srmacklem
4742320615Srmacklem		/* Make sure nm_rsize is small enough. */
4743320615Srmacklem		maxval = fxdr_unsigned(uint32_t, *tl++);
4744320615Srmacklem		while (maxval < nmp->nm_rsize + NFS_MAXXDR) {
4745320615Srmacklem			if (nmp->nm_rsize > 8096)
4746320615Srmacklem				nmp->nm_rsize /= 2;
4747320615Srmacklem			else
4748320615Srmacklem				break;
4749320615Srmacklem		}
4750320615Srmacklem
4751244042Srmacklem		sep->nfsess_maxcache = fxdr_unsigned(int, *tl++);
4752244042Srmacklem		tl++;
4753244042Srmacklem		sep->nfsess_foreslots = fxdr_unsigned(uint16_t, *tl++);
4754244042Srmacklem		NFSCL_DEBUG(4, "fore slots=%d\n", (int)sep->nfsess_foreslots);
4755244042Srmacklem		irdcnt = fxdr_unsigned(int, *tl);
4756244042Srmacklem		if (irdcnt > 0)
4757244042Srmacklem			NFSM_DISSECT(tl, uint32_t *, irdcnt * NFSX_UNSIGNED);
4758244042Srmacklem
4759244042Srmacklem		/* and the back channel slot count. */
4760244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
4761244042Srmacklem		tl += 5;
4762244042Srmacklem		sep->nfsess_backslots = fxdr_unsigned(uint16_t, *tl);
4763244042Srmacklem		NFSCL_DEBUG(4, "back slots=%d\n", (int)sep->nfsess_backslots);
4764244042Srmacklem	}
4765244042Srmacklem	error = nd->nd_repstat;
4766244042Srmacklemnfsmout:
4767244042Srmacklem	mbuf_freem(nd->nd_mrep);
4768244042Srmacklem	return (error);
4769244042Srmacklem}
4770244042Srmacklem
4771244042Srmacklem/*
4772244042Srmacklem * Do the NFSv4.1 Destroy Session.
4773244042Srmacklem */
4774244042Srmacklemint
4775244042Srmacklemnfsrpc_destroysession(struct nfsmount *nmp, struct nfsclclient *clp,
4776244042Srmacklem    struct ucred *cred, NFSPROC_T *p)
4777244042Srmacklem{
4778244042Srmacklem	uint32_t *tl;
4779244042Srmacklem	struct nfsrv_descript nfsd;
4780244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
4781244042Srmacklem	int error;
4782317393Srmacklem	struct nfsclsession *tsep;
4783244042Srmacklem
4784244042Srmacklem	nfscl_reqstart(nd, NFSPROC_DESTROYSESSION, nmp, NULL, 0, NULL, NULL);
4785244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
4786317393Srmacklem	tsep = nfsmnt_mdssession(nmp);
4787317393Srmacklem	bcopy(tsep->nfsess_sessionid, tl, NFSX_V4SESSIONID);
4788244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4789244042Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4790244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4791244042Srmacklem	if (error != 0)
4792244042Srmacklem		return (error);
4793244042Srmacklem	error = nd->nd_repstat;
4794244042Srmacklem	mbuf_freem(nd->nd_mrep);
4795244042Srmacklem	return (error);
4796244042Srmacklem}
4797244042Srmacklem
4798244042Srmacklem/*
4799244042Srmacklem * Do the NFSv4.1 Destroy Client.
4800244042Srmacklem */
4801244042Srmacklemint
4802244042Srmacklemnfsrpc_destroyclient(struct nfsmount *nmp, struct nfsclclient *clp,
4803244042Srmacklem    struct ucred *cred, NFSPROC_T *p)
4804244042Srmacklem{
4805244042Srmacklem	uint32_t *tl;
4806244042Srmacklem	struct nfsrv_descript nfsd;
4807244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
4808244042Srmacklem	int error;
4809317393Srmacklem	struct nfsclsession *tsep;
4810244042Srmacklem
4811244042Srmacklem	nfscl_reqstart(nd, NFSPROC_DESTROYCLIENT, nmp, NULL, 0, NULL, NULL);
4812244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
4813317393Srmacklem	tsep = nfsmnt_mdssession(nmp);
4814317393Srmacklem	*tl++ = tsep->nfsess_clientid.lval[0];
4815317393Srmacklem	*tl = tsep->nfsess_clientid.lval[1];
4816244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4817244042Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4818244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4819244042Srmacklem	if (error != 0)
4820244042Srmacklem		return (error);
4821244042Srmacklem	error = nd->nd_repstat;
4822244042Srmacklem	mbuf_freem(nd->nd_mrep);
4823244042Srmacklem	return (error);
4824244042Srmacklem}
4825244042Srmacklem
4826244042Srmacklem/*
4827244042Srmacklem * Do the NFSv4.1 LayoutGet.
4828244042Srmacklem */
4829244042Srmacklemint
4830244042Srmacklemnfsrpc_layoutget(struct nfsmount *nmp, uint8_t *fhp, int fhlen, int iomode,
4831244042Srmacklem    uint64_t offset, uint64_t len, uint64_t minlen, int layoutlen,
4832244042Srmacklem    nfsv4stateid_t *stateidp, int *retonclosep, struct nfsclflayouthead *flhp,
4833244042Srmacklem    struct ucred *cred, NFSPROC_T *p, void *stuff)
4834244042Srmacklem{
4835244042Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
4836320998Srmacklem	int error;
4837244042Srmacklem
4838244042Srmacklem	nfscl_reqstart(nd, NFSPROC_LAYOUTGET, nmp, fhp, fhlen, NULL, NULL);
4839320998Srmacklem	nfsrv_setuplayoutget(nd, iomode, offset, len, minlen, stateidp,
4840320998Srmacklem	    layoutlen, 0);
4841244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4842244042Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4843244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4844320998Srmacklem	NFSCL_DEBUG(4, "layget err=%d st=%d\n", error, nd->nd_repstat);
4845244042Srmacklem	if (error != 0)
4846244042Srmacklem		return (error);
4847320998Srmacklem	if (nd->nd_repstat == 0)
4848320998Srmacklem		error = nfsrv_parselayoutget(nd, stateidp, retonclosep, flhp);
4849320998Srmacklem	if (error == 0 && nd->nd_repstat != 0)
4850244042Srmacklem		error = nd->nd_repstat;
4851244042Srmacklem	mbuf_freem(nd->nd_mrep);
4852244042Srmacklem	return (error);
4853244042Srmacklem}
4854244042Srmacklem
4855244042Srmacklem/*
4856244042Srmacklem * Do the NFSv4.1 Get Device Info.
4857244042Srmacklem */
4858244042Srmacklemint
4859244042Srmacklemnfsrpc_getdeviceinfo(struct nfsmount *nmp, uint8_t *deviceid, int layouttype,
4860244042Srmacklem    uint32_t *notifybitsp, struct nfscldevinfo **ndip, struct ucred *cred,
4861244042Srmacklem    NFSPROC_T *p)
4862244042Srmacklem{
4863244042Srmacklem	uint32_t cnt, *tl;
4864244042Srmacklem	struct nfsrv_descript nfsd;
4865244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
4866244042Srmacklem	struct sockaddr_storage ss;
4867244042Srmacklem	struct nfsclds *dsp = NULL, **dspp;
4868244042Srmacklem	struct nfscldevinfo *ndi;
4869244042Srmacklem	int addrcnt, bitcnt, error, i, isudp, j, pos, safilled, stripecnt;
4870244042Srmacklem	uint8_t stripeindex;
4871244042Srmacklem
4872244042Srmacklem	*ndip = NULL;
4873244042Srmacklem	ndi = NULL;
4874244042Srmacklem	nfscl_reqstart(nd, NFSPROC_GETDEVICEINFO, nmp, NULL, 0, NULL, NULL);
4875244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_V4DEVICEID + 3 * NFSX_UNSIGNED);
4876244042Srmacklem	NFSBCOPY(deviceid, tl, NFSX_V4DEVICEID);
4877244042Srmacklem	tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
4878244042Srmacklem	*tl++ = txdr_unsigned(layouttype);
4879244042Srmacklem	*tl++ = txdr_unsigned(100000);
4880244042Srmacklem	if (notifybitsp != NULL && *notifybitsp != 0) {
4881244042Srmacklem		*tl = txdr_unsigned(1);		/* One word of bits. */
4882244042Srmacklem		NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
4883244042Srmacklem		*tl = txdr_unsigned(*notifybitsp);
4884244042Srmacklem	} else
4885244042Srmacklem		*tl = txdr_unsigned(0);
4886244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
4887244042Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
4888244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
4889244042Srmacklem	if (error != 0)
4890244042Srmacklem		return (error);
4891244042Srmacklem	if (nd->nd_repstat == 0) {
4892244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_UNSIGNED);
4893244042Srmacklem		if (layouttype != fxdr_unsigned(int, *tl++))
4894244042Srmacklem			printf("EEK! devinfo layout type not same!\n");
4895244042Srmacklem		stripecnt = fxdr_unsigned(int, *++tl);
4896244042Srmacklem		NFSCL_DEBUG(4, "stripecnt=%d\n", stripecnt);
4897244042Srmacklem		if (stripecnt < 1 || stripecnt > 4096) {
4898244042Srmacklem			printf("NFS devinfo stripecnt %d: out of range\n",
4899244042Srmacklem			    stripecnt);
4900244042Srmacklem			error = NFSERR_BADXDR;
4901244042Srmacklem			goto nfsmout;
4902244042Srmacklem		}
4903244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, (stripecnt + 1) * NFSX_UNSIGNED);
4904244042Srmacklem		addrcnt = fxdr_unsigned(int, *(tl + stripecnt));
4905244042Srmacklem		NFSCL_DEBUG(4, "addrcnt=%d\n", addrcnt);
4906244042Srmacklem		if (addrcnt < 1 || addrcnt > 128) {
4907244042Srmacklem			printf("NFS devinfo addrcnt %d: out of range\n",
4908244042Srmacklem			    addrcnt);
4909244042Srmacklem			error = NFSERR_BADXDR;
4910244042Srmacklem			goto nfsmout;
4911244042Srmacklem		}
4912244042Srmacklem
4913244042Srmacklem		/*
4914244042Srmacklem		 * Now we know how many stripe indices and addresses, so
4915244042Srmacklem		 * we can allocate the structure the correct size.
4916244042Srmacklem		 */
4917244042Srmacklem		i = (stripecnt * sizeof(uint8_t)) / sizeof(struct nfsclds *)
4918244042Srmacklem		    + 1;
4919244042Srmacklem		NFSCL_DEBUG(4, "stripeindices=%d\n", i);
4920244042Srmacklem		ndi = malloc(sizeof(*ndi) + (addrcnt + i) *
4921244042Srmacklem		    sizeof(struct nfsclds *), M_NFSDEVINFO, M_WAITOK | M_ZERO);
4922244042Srmacklem		NFSBCOPY(deviceid, ndi->nfsdi_deviceid, NFSX_V4DEVICEID);
4923244042Srmacklem		ndi->nfsdi_refcnt = 0;
4924244042Srmacklem		ndi->nfsdi_stripecnt = stripecnt;
4925244042Srmacklem		ndi->nfsdi_addrcnt = addrcnt;
4926244042Srmacklem		/* Fill in the stripe indices. */
4927244042Srmacklem		for (i = 0; i < stripecnt; i++) {
4928244042Srmacklem			stripeindex = fxdr_unsigned(uint8_t, *tl++);
4929244042Srmacklem			NFSCL_DEBUG(4, "stripeind=%d\n", stripeindex);
4930244042Srmacklem			if (stripeindex >= addrcnt) {
4931244042Srmacklem				printf("NFS devinfo stripeindex %d: too big\n",
4932244042Srmacklem				    (int)stripeindex);
4933244042Srmacklem				error = NFSERR_BADXDR;
4934244042Srmacklem				goto nfsmout;
4935244042Srmacklem			}
4936244042Srmacklem			nfsfldi_setstripeindex(ndi, i, stripeindex);
4937244042Srmacklem		}
4938244042Srmacklem
4939244042Srmacklem		/* Now, dissect the server address(es). */
4940244042Srmacklem		safilled = 0;
4941244042Srmacklem		for (i = 0; i < addrcnt; i++) {
4942244042Srmacklem			NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4943244042Srmacklem			cnt = fxdr_unsigned(uint32_t, *tl);
4944244042Srmacklem			if (cnt == 0) {
4945244042Srmacklem				printf("NFS devinfo 0 len addrlist\n");
4946244042Srmacklem				error = NFSERR_BADXDR;
4947244042Srmacklem				goto nfsmout;
4948244042Srmacklem			}
4949244042Srmacklem			dspp = nfsfldi_addr(ndi, i);
4950244042Srmacklem			pos = arc4random() % cnt;	/* Choose one. */
4951244042Srmacklem			safilled = 0;
4952244042Srmacklem			for (j = 0; j < cnt; j++) {
4953244042Srmacklem				error = nfsv4_getipaddr(nd, &ss, &isudp);
4954244042Srmacklem				if (error != 0 && error != EPERM) {
4955244042Srmacklem					error = NFSERR_BADXDR;
4956244042Srmacklem					goto nfsmout;
4957244042Srmacklem				}
4958244042Srmacklem				if (error == 0 && isudp == 0) {
4959244042Srmacklem					/*
4960244042Srmacklem					 * The algorithm is:
4961244042Srmacklem					 * - use "pos" entry if it is of the
4962244042Srmacklem					 *   same af_family or none of them
4963244042Srmacklem					 *   is of the same af_family
4964244042Srmacklem					 * else
4965244042Srmacklem					 * - use the first one of the same
4966244042Srmacklem					 *   af_family.
4967244042Srmacklem					 */
4968244042Srmacklem					if ((safilled == 0 && ss.ss_family ==
4969244042Srmacklem					     nmp->nm_nam->sa_family) ||
4970244042Srmacklem					    (j == pos &&
4971244042Srmacklem					     (safilled == 0 || ss.ss_family ==
4972244042Srmacklem					      nmp->nm_nam->sa_family)) ||
4973244042Srmacklem					    (safilled == 1 && ss.ss_family ==
4974244042Srmacklem					     nmp->nm_nam->sa_family)) {
4975244042Srmacklem						error = nfsrpc_fillsa(nmp, &ss,
4976244042Srmacklem						    &dsp, p);
4977244042Srmacklem						if (error == 0) {
4978244042Srmacklem							*dspp = dsp;
4979244042Srmacklem							if (ss.ss_family ==
4980244042Srmacklem							 nmp->nm_nam->sa_family)
4981244042Srmacklem								safilled = 2;
4982244042Srmacklem							else
4983244042Srmacklem								safilled = 1;
4984244042Srmacklem						}
4985244042Srmacklem					}
4986244042Srmacklem				}
4987244042Srmacklem			}
4988244042Srmacklem			if (safilled == 0)
4989244042Srmacklem				break;
4990244042Srmacklem		}
4991244042Srmacklem
4992244042Srmacklem		/* And the notify bits. */
4993244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4994244042Srmacklem		if (safilled != 0) {
4995244042Srmacklem			bitcnt = fxdr_unsigned(int, *tl);
4996244042Srmacklem			if (bitcnt > 0) {
4997244042Srmacklem				NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
4998244042Srmacklem				if (notifybitsp != NULL)
4999244042Srmacklem					*notifybitsp =
5000244042Srmacklem					    fxdr_unsigned(uint32_t, *tl);
5001244042Srmacklem			}
5002244042Srmacklem			*ndip = ndi;
5003244042Srmacklem		} else
5004244042Srmacklem			error = EPERM;
5005244042Srmacklem	}
5006244042Srmacklem	if (nd->nd_repstat != 0)
5007244042Srmacklem		error = nd->nd_repstat;
5008244042Srmacklemnfsmout:
5009244042Srmacklem	if (error != 0 && ndi != NULL)
5010244042Srmacklem		nfscl_freedevinfo(ndi);
5011244042Srmacklem	mbuf_freem(nd->nd_mrep);
5012244042Srmacklem	return (error);
5013244042Srmacklem}
5014244042Srmacklem
5015244042Srmacklem/*
5016244042Srmacklem * Do the NFSv4.1 LayoutCommit.
5017244042Srmacklem */
5018244042Srmacklemint
5019244042Srmacklemnfsrpc_layoutcommit(struct nfsmount *nmp, uint8_t *fh, int fhlen, int reclaim,
5020244042Srmacklem    uint64_t off, uint64_t len, uint64_t lastbyte, nfsv4stateid_t *stateidp,
5021244042Srmacklem    int layouttype, int layoutupdatecnt, uint8_t *layp, struct ucred *cred,
5022244042Srmacklem    NFSPROC_T *p, void *stuff)
5023244042Srmacklem{
5024244042Srmacklem	uint32_t *tl;
5025244042Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
5026244042Srmacklem	int error, outcnt, i;
5027244042Srmacklem	uint8_t *cp;
5028244042Srmacklem
5029244042Srmacklem	nfscl_reqstart(nd, NFSPROC_LAYOUTCOMMIT, nmp, fh, fhlen, NULL, NULL);
5030244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
5031244042Srmacklem	    NFSX_STATEID);
5032244042Srmacklem	txdr_hyper(off, tl);
5033244042Srmacklem	tl += 2;
5034244042Srmacklem	txdr_hyper(len, tl);
5035244042Srmacklem	tl += 2;
5036244042Srmacklem	if (reclaim != 0)
5037244042Srmacklem		*tl++ = newnfs_true;
5038244042Srmacklem	else
5039244042Srmacklem		*tl++ = newnfs_false;
5040244042Srmacklem	*tl++ = txdr_unsigned(stateidp->seqid);
5041244042Srmacklem	*tl++ = stateidp->other[0];
5042244042Srmacklem	*tl++ = stateidp->other[1];
5043244042Srmacklem	*tl++ = stateidp->other[2];
5044244042Srmacklem	*tl++ = newnfs_true;
5045244042Srmacklem	if (lastbyte < off)
5046244042Srmacklem		lastbyte = off;
5047244042Srmacklem	else if (lastbyte >= (off + len))
5048244042Srmacklem		lastbyte = off + len - 1;
5049244042Srmacklem	txdr_hyper(lastbyte, tl);
5050244042Srmacklem	tl += 2;
5051244042Srmacklem	*tl++ = newnfs_false;
5052244042Srmacklem	*tl++ = txdr_unsigned(layouttype);
5053244042Srmacklem	*tl = txdr_unsigned(layoutupdatecnt);
5054244042Srmacklem	if (layoutupdatecnt > 0) {
5055244042Srmacklem		KASSERT(layouttype != NFSLAYOUT_NFSV4_1_FILES,
5056244042Srmacklem		    ("Must be nil for Files Layout"));
5057244042Srmacklem		outcnt = NFSM_RNDUP(layoutupdatecnt);
5058244042Srmacklem		NFSM_BUILD(cp, uint8_t *, outcnt);
5059244042Srmacklem		NFSBCOPY(layp, cp, layoutupdatecnt);
5060244042Srmacklem		cp += layoutupdatecnt;
5061244042Srmacklem		for (i = 0; i < (outcnt - layoutupdatecnt); i++)
5062244042Srmacklem			*cp++ = 0x0;
5063244042Srmacklem	}
5064244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
5065244042Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
5066244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
5067244042Srmacklem	if (error != 0)
5068244042Srmacklem		return (error);
5069244042Srmacklem	error = nd->nd_repstat;
5070244042Srmacklem	mbuf_freem(nd->nd_mrep);
5071244042Srmacklem	return (error);
5072244042Srmacklem}
5073244042Srmacklem
5074244042Srmacklem/*
5075244042Srmacklem * Do the NFSv4.1 LayoutReturn.
5076244042Srmacklem */
5077244042Srmacklemint
5078244042Srmacklemnfsrpc_layoutreturn(struct nfsmount *nmp, uint8_t *fh, int fhlen, int reclaim,
5079244042Srmacklem    int layouttype, uint32_t iomode, int layoutreturn, uint64_t offset,
5080244042Srmacklem    uint64_t len, nfsv4stateid_t *stateidp, int layoutcnt, uint32_t *layp,
5081244042Srmacklem    struct ucred *cred, NFSPROC_T *p, void *stuff)
5082244042Srmacklem{
5083244042Srmacklem	uint32_t *tl;
5084244042Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
5085244042Srmacklem	int error, outcnt, i;
5086244042Srmacklem	uint8_t *cp;
5087244042Srmacklem
5088244042Srmacklem	nfscl_reqstart(nd, NFSPROC_LAYOUTRETURN, nmp, fh, fhlen, NULL, NULL);
5089244042Srmacklem	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED);
5090244042Srmacklem	if (reclaim != 0)
5091244042Srmacklem		*tl++ = newnfs_true;
5092244042Srmacklem	else
5093244042Srmacklem		*tl++ = newnfs_false;
5094244042Srmacklem	*tl++ = txdr_unsigned(layouttype);
5095244042Srmacklem	*tl++ = txdr_unsigned(iomode);
5096244042Srmacklem	*tl = txdr_unsigned(layoutreturn);
5097244042Srmacklem	if (layoutreturn == NFSLAYOUTRETURN_FILE) {
5098244042Srmacklem		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
5099244042Srmacklem		    NFSX_UNSIGNED);
5100244042Srmacklem		txdr_hyper(offset, tl);
5101244042Srmacklem		tl += 2;
5102244042Srmacklem		txdr_hyper(len, tl);
5103244042Srmacklem		tl += 2;
5104244042Srmacklem		NFSCL_DEBUG(4, "layoutret stseq=%d\n", (int)stateidp->seqid);
5105244042Srmacklem		*tl++ = txdr_unsigned(stateidp->seqid);
5106244042Srmacklem		*tl++ = stateidp->other[0];
5107244042Srmacklem		*tl++ = stateidp->other[1];
5108244042Srmacklem		*tl++ = stateidp->other[2];
5109244042Srmacklem		*tl = txdr_unsigned(layoutcnt);
5110244042Srmacklem		if (layoutcnt > 0) {
5111244042Srmacklem			outcnt = NFSM_RNDUP(layoutcnt);
5112244042Srmacklem			NFSM_BUILD(cp, uint8_t *, outcnt);
5113244042Srmacklem			NFSBCOPY(layp, cp, layoutcnt);
5114244042Srmacklem			cp += layoutcnt;
5115244042Srmacklem			for (i = 0; i < (outcnt - layoutcnt); i++)
5116244042Srmacklem				*cp++ = 0x0;
5117244042Srmacklem		}
5118244042Srmacklem	}
5119244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
5120244042Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
5121244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
5122244042Srmacklem	if (error != 0)
5123244042Srmacklem		return (error);
5124244042Srmacklem	if (nd->nd_repstat == 0) {
5125244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5126244042Srmacklem		if (*tl != 0) {
5127244042Srmacklem			NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
5128244042Srmacklem			stateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
5129244042Srmacklem			stateidp->other[0] = *tl++;
5130244042Srmacklem			stateidp->other[1] = *tl++;
5131244042Srmacklem			stateidp->other[2] = *tl;
5132244042Srmacklem		}
5133244042Srmacklem	} else
5134244042Srmacklem		error = nd->nd_repstat;
5135244042Srmacklemnfsmout:
5136244042Srmacklem	mbuf_freem(nd->nd_mrep);
5137244042Srmacklem	return (error);
5138244042Srmacklem}
5139244042Srmacklem
5140244042Srmacklem/*
5141244042Srmacklem * Acquire a layout and devinfo, if possible. The caller must have acquired
5142244042Srmacklem * a reference count on the nfsclclient structure before calling this.
5143244042Srmacklem * Return the layout in lypp with a reference count on it, if successful.
5144244042Srmacklem */
5145244042Srmacklemstatic int
5146244042Srmacklemnfsrpc_getlayout(struct nfsmount *nmp, vnode_t vp, struct nfsfh *nfhp,
5147244042Srmacklem    int iomode, uint32_t *notifybitsp, nfsv4stateid_t *stateidp, uint64_t off,
5148244042Srmacklem    struct nfscllayout **lypp, struct ucred *cred, NFSPROC_T *p)
5149244042Srmacklem{
5150244042Srmacklem	struct nfscllayout *lyp;
5151320998Srmacklem	struct nfsclflayout *flp;
5152244042Srmacklem	struct nfsclflayouthead flh;
5153244042Srmacklem	int error = 0, islocked, layoutlen, recalled, retonclose;
5154244042Srmacklem	nfsv4stateid_t stateid;
5155317393Srmacklem	struct nfsclsession *tsep;
5156244042Srmacklem
5157244042Srmacklem	*lypp = NULL;
5158244042Srmacklem	/*
5159244042Srmacklem	 * If lyp is returned non-NULL, there will be a refcnt (shared lock)
5160244042Srmacklem	 * on it, iff flp != NULL or a lock (exclusive lock) on it iff
5161244042Srmacklem	 * flp == NULL.
5162244042Srmacklem	 */
5163244042Srmacklem	lyp = nfscl_getlayout(nmp->nm_clp, nfhp->nfh_fh, nfhp->nfh_len,
5164244042Srmacklem	    off, &flp, &recalled);
5165244042Srmacklem	islocked = 0;
5166244042Srmacklem	if (lyp == NULL || flp == NULL) {
5167244042Srmacklem		if (recalled != 0)
5168244042Srmacklem			return (EIO);
5169244042Srmacklem		LIST_INIT(&flh);
5170317393Srmacklem		tsep = nfsmnt_mdssession(nmp);
5171317393Srmacklem		layoutlen = tsep->nfsess_maxcache -
5172244042Srmacklem		    (NFSX_STATEID + 3 * NFSX_UNSIGNED);
5173244042Srmacklem		if (lyp == NULL) {
5174244042Srmacklem			stateid.seqid = 0;
5175244042Srmacklem			stateid.other[0] = stateidp->other[0];
5176244042Srmacklem			stateid.other[1] = stateidp->other[1];
5177244042Srmacklem			stateid.other[2] = stateidp->other[2];
5178244042Srmacklem			error = nfsrpc_layoutget(nmp, nfhp->nfh_fh,
5179318260Srmacklem			    nfhp->nfh_len, iomode, (uint64_t)0, UINT64_MAX,
5180244042Srmacklem			    (uint64_t)0, layoutlen, &stateid, &retonclose,
5181244042Srmacklem			    &flh, cred, p, NULL);
5182244042Srmacklem		} else {
5183244042Srmacklem			islocked = 1;
5184244042Srmacklem			stateid.seqid = lyp->nfsly_stateid.seqid;
5185244042Srmacklem			stateid.other[0] = lyp->nfsly_stateid.other[0];
5186244042Srmacklem			stateid.other[1] = lyp->nfsly_stateid.other[1];
5187244042Srmacklem			stateid.other[2] = lyp->nfsly_stateid.other[2];
5188244042Srmacklem			error = nfsrpc_layoutget(nmp, nfhp->nfh_fh,
5189318260Srmacklem			    nfhp->nfh_len, iomode, off, UINT64_MAX,
5190244042Srmacklem			    (uint64_t)0, layoutlen, &stateid, &retonclose,
5191244042Srmacklem			    &flh, cred, p, NULL);
5192244042Srmacklem		}
5193320998Srmacklem		error = nfsrpc_layoutgetres(nmp, vp, nfhp->nfh_fh,
5194320998Srmacklem		    nfhp->nfh_len, &stateid, retonclose, notifybitsp, &lyp,
5195320998Srmacklem		    &flh, error, NULL, cred, p);
5196244042Srmacklem		if (error == 0)
5197320998Srmacklem			*lypp = lyp;
5198320998Srmacklem		else if (islocked != 0)
5199320998Srmacklem			nfscl_rellayout(lyp, 1);
5200244042Srmacklem	} else
5201244042Srmacklem		*lypp = lyp;
5202244042Srmacklem	return (error);
5203244042Srmacklem}
5204244042Srmacklem
5205244042Srmacklem/*
5206244042Srmacklem * Do a TCP connection plus exchange id and create session.
5207244042Srmacklem * If successful, a "struct nfsclds" is linked into the list for the
5208244042Srmacklem * mount point and a pointer to it is returned.
5209244042Srmacklem */
5210244042Srmacklemstatic int
5211244042Srmacklemnfsrpc_fillsa(struct nfsmount *nmp, struct sockaddr_storage *ssp,
5212244042Srmacklem    struct nfsclds **dspp, NFSPROC_T *p)
5213244042Srmacklem{
5214244042Srmacklem	struct sockaddr_in *msad, *sad, *ssd;
5215244042Srmacklem	struct sockaddr_in6 *msad6, *sad6, *ssd6;
5216244042Srmacklem	struct nfsclclient *clp;
5217244042Srmacklem	struct nfssockreq *nrp;
5218244042Srmacklem	struct nfsclds *dsp, *tdsp;
5219244042Srmacklem	int error;
5220244042Srmacklem	enum nfsclds_state retv;
5221244042Srmacklem	uint32_t sequenceid;
5222244042Srmacklem
5223244042Srmacklem	KASSERT(nmp->nm_sockreq.nr_cred != NULL,
5224244042Srmacklem	    ("nfsrpc_fillsa: NULL nr_cred"));
5225244042Srmacklem	NFSLOCKCLSTATE();
5226244042Srmacklem	clp = nmp->nm_clp;
5227244042Srmacklem	NFSUNLOCKCLSTATE();
5228244042Srmacklem	if (clp == NULL)
5229244042Srmacklem		return (EPERM);
5230244042Srmacklem	if (ssp->ss_family == AF_INET) {
5231244042Srmacklem		ssd = (struct sockaddr_in *)ssp;
5232244042Srmacklem		NFSLOCKMNT(nmp);
5233244042Srmacklem
5234244042Srmacklem		/*
5235244042Srmacklem		 * Check to see if we already have a session for this
5236244042Srmacklem		 * address that is usable for a DS.
5237244042Srmacklem		 * Note that the MDS's address is in a different place
5238244042Srmacklem		 * than the sessions already acquired for DS's.
5239244042Srmacklem		 */
5240244042Srmacklem		msad = (struct sockaddr_in *)nmp->nm_sockreq.nr_nam;
5241244042Srmacklem		tdsp = TAILQ_FIRST(&nmp->nm_sess);
5242244042Srmacklem		while (tdsp != NULL) {
5243244042Srmacklem			if (msad != NULL && msad->sin_family == AF_INET &&
5244244042Srmacklem			    ssd->sin_addr.s_addr == msad->sin_addr.s_addr &&
5245244042Srmacklem			    ssd->sin_port == msad->sin_port &&
5246317393Srmacklem			    (tdsp->nfsclds_flags & NFSCLDS_DS) != 0 &&
5247317393Srmacklem			    tdsp->nfsclds_sess.nfsess_defunct == 0) {
5248244042Srmacklem				*dspp = tdsp;
5249244042Srmacklem				NFSUNLOCKMNT(nmp);
5250244042Srmacklem				NFSCL_DEBUG(4, "fnd same addr\n");
5251244042Srmacklem				return (0);
5252244042Srmacklem			}
5253244042Srmacklem			tdsp = TAILQ_NEXT(tdsp, nfsclds_list);
5254244042Srmacklem			if (tdsp != NULL && tdsp->nfsclds_sockp != NULL)
5255244042Srmacklem				msad = (struct sockaddr_in *)
5256244042Srmacklem				    tdsp->nfsclds_sockp->nr_nam;
5257244042Srmacklem			else
5258244042Srmacklem				msad = NULL;
5259244042Srmacklem		}
5260244042Srmacklem		NFSUNLOCKMNT(nmp);
5261244042Srmacklem
5262244042Srmacklem		/* No IP address match, so look for new/trunked one. */
5263244042Srmacklem		sad = malloc(sizeof(*sad), M_SONAME, M_WAITOK | M_ZERO);
5264244042Srmacklem		sad->sin_len = sizeof(*sad);
5265244042Srmacklem		sad->sin_family = AF_INET;
5266244042Srmacklem		sad->sin_port = ssd->sin_port;
5267244042Srmacklem		sad->sin_addr.s_addr = ssd->sin_addr.s_addr;
5268244042Srmacklem		nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ, M_WAITOK | M_ZERO);
5269244042Srmacklem		nrp->nr_nam = (struct sockaddr *)sad;
5270244042Srmacklem	} else if (ssp->ss_family == AF_INET6) {
5271244042Srmacklem		ssd6 = (struct sockaddr_in6 *)ssp;
5272244042Srmacklem		NFSLOCKMNT(nmp);
5273244042Srmacklem
5274244042Srmacklem		/*
5275244042Srmacklem		 * Check to see if we already have a session for this
5276244042Srmacklem		 * address that is usable for a DS.
5277244042Srmacklem		 * Note that the MDS's address is in a different place
5278244042Srmacklem		 * than the sessions already acquired for DS's.
5279244042Srmacklem		 */
5280244042Srmacklem		msad6 = (struct sockaddr_in6 *)nmp->nm_sockreq.nr_nam;
5281244042Srmacklem		tdsp = TAILQ_FIRST(&nmp->nm_sess);
5282244042Srmacklem		while (tdsp != NULL) {
5283244042Srmacklem			if (msad6 != NULL && msad6->sin6_family == AF_INET6 &&
5284244042Srmacklem			    IN6_ARE_ADDR_EQUAL(&ssd6->sin6_addr,
5285244042Srmacklem			    &msad6->sin6_addr) &&
5286244042Srmacklem			    ssd6->sin6_port == msad6->sin6_port &&
5287317393Srmacklem			    (tdsp->nfsclds_flags & NFSCLDS_DS) != 0 &&
5288317393Srmacklem			    tdsp->nfsclds_sess.nfsess_defunct == 0) {
5289244042Srmacklem				*dspp = tdsp;
5290244042Srmacklem				NFSUNLOCKMNT(nmp);
5291244042Srmacklem				return (0);
5292244042Srmacklem			}
5293244042Srmacklem			tdsp = TAILQ_NEXT(tdsp, nfsclds_list);
5294244042Srmacklem			if (tdsp != NULL && tdsp->nfsclds_sockp != NULL)
5295244042Srmacklem				msad6 = (struct sockaddr_in6 *)
5296244042Srmacklem				    tdsp->nfsclds_sockp->nr_nam;
5297244042Srmacklem			else
5298244042Srmacklem				msad6 = NULL;
5299244042Srmacklem		}
5300244042Srmacklem		NFSUNLOCKMNT(nmp);
5301244042Srmacklem
5302244042Srmacklem		/* No IP address match, so look for new/trunked one. */
5303244042Srmacklem		sad6 = malloc(sizeof(*sad6), M_SONAME, M_WAITOK | M_ZERO);
5304244042Srmacklem		sad6->sin6_len = sizeof(*sad6);
5305244042Srmacklem		sad6->sin6_family = AF_INET6;
5306244042Srmacklem		sad6->sin6_port = ssd6->sin6_port;
5307244042Srmacklem		NFSBCOPY(&ssd6->sin6_addr, &sad6->sin6_addr,
5308244042Srmacklem		    sizeof(struct in6_addr));
5309244042Srmacklem		nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ, M_WAITOK | M_ZERO);
5310244042Srmacklem		nrp->nr_nam = (struct sockaddr *)sad6;
5311244042Srmacklem	} else
5312244042Srmacklem		return (EPERM);
5313244042Srmacklem
5314244042Srmacklem	nrp->nr_sotype = SOCK_STREAM;
5315244042Srmacklem	mtx_init(&nrp->nr_mtx, "nfssock", NULL, MTX_DEF);
5316244042Srmacklem	nrp->nr_prog = NFS_PROG;
5317244042Srmacklem	nrp->nr_vers = NFS_VER4;
5318244042Srmacklem
5319244042Srmacklem	/*
5320244042Srmacklem	 * Use the credentials that were used for the mount, which are
5321244042Srmacklem	 * in nmp->nm_sockreq.nr_cred for newnfs_connect() etc.
5322244042Srmacklem	 * Ref. counting the credentials with crhold() is probably not
5323244042Srmacklem	 * necessary, since nm_sockreq.nr_cred won't be crfree()'d until
5324244042Srmacklem	 * unmount, but I did it anyhow.
5325244042Srmacklem	 */
5326244042Srmacklem	nrp->nr_cred = crhold(nmp->nm_sockreq.nr_cred);
5327244042Srmacklem	error = newnfs_connect(nmp, nrp, NULL, p, 0);
5328244042Srmacklem	NFSCL_DEBUG(3, "DS connect=%d\n", error);
5329244042Srmacklem
5330244042Srmacklem	/* Now, do the exchangeid and create session. */
5331317973Srmacklem	if (error == 0) {
5332244042Srmacklem		error = nfsrpc_exchangeid(nmp, clp, nrp, NFSV4EXCH_USEPNFSDS,
5333244042Srmacklem		    &dsp, nrp->nr_cred, p);
5334317973Srmacklem		NFSCL_DEBUG(3, "DS exchangeid=%d\n", error);
5335317973Srmacklem		if (error != 0)
5336317973Srmacklem			newnfs_disconnect(nrp);
5337317973Srmacklem	}
5338244042Srmacklem	if (error == 0) {
5339244042Srmacklem		dsp->nfsclds_sockp = nrp;
5340244042Srmacklem		NFSLOCKMNT(nmp);
5341244042Srmacklem		retv = nfscl_getsameserver(nmp, dsp, &tdsp);
5342244042Srmacklem		NFSCL_DEBUG(3, "getsame ret=%d\n", retv);
5343244042Srmacklem		if (retv == NFSDSP_USETHISSESSION) {
5344244042Srmacklem			NFSUNLOCKMNT(nmp);
5345244042Srmacklem			/*
5346244042Srmacklem			 * If there is already a session for this server,
5347244042Srmacklem			 * use it.
5348244042Srmacklem			 */
5349244042Srmacklem			(void)newnfs_disconnect(nrp);
5350244042Srmacklem			nfscl_freenfsclds(dsp);
5351244042Srmacklem			*dspp = tdsp;
5352244042Srmacklem			return (0);
5353244042Srmacklem		}
5354244042Srmacklem		if (retv == NFSDSP_SEQTHISSESSION)
5355244042Srmacklem			sequenceid = tdsp->nfsclds_sess.nfsess_sequenceid;
5356244042Srmacklem		else
5357244042Srmacklem			sequenceid = dsp->nfsclds_sess.nfsess_sequenceid;
5358244042Srmacklem		NFSUNLOCKMNT(nmp);
5359244042Srmacklem		error = nfsrpc_createsession(nmp, &dsp->nfsclds_sess,
5360244042Srmacklem		    nrp, sequenceid, 0, nrp->nr_cred, p);
5361244042Srmacklem		NFSCL_DEBUG(3, "DS createsess=%d\n", error);
5362244042Srmacklem	} else {
5363244042Srmacklem		NFSFREECRED(nrp->nr_cred);
5364244042Srmacklem		NFSFREEMUTEX(&nrp->nr_mtx);
5365244042Srmacklem		free(nrp->nr_nam, M_SONAME);
5366244042Srmacklem		free(nrp, M_NFSSOCKREQ);
5367244042Srmacklem	}
5368244042Srmacklem	if (error == 0) {
5369244042Srmacklem		NFSCL_DEBUG(3, "add DS session\n");
5370244042Srmacklem		/*
5371244042Srmacklem		 * Put it at the end of the list. That way the list
5372244042Srmacklem		 * is ordered by when the entry was added. This matters
5373244042Srmacklem		 * since the one done first is the one that should be
5374244042Srmacklem		 * used for sequencid'ing any subsequent create sessions.
5375244042Srmacklem		 */
5376244042Srmacklem		NFSLOCKMNT(nmp);
5377244042Srmacklem		TAILQ_INSERT_TAIL(&nmp->nm_sess, dsp, nfsclds_list);
5378244042Srmacklem		NFSUNLOCKMNT(nmp);
5379244042Srmacklem		*dspp = dsp;
5380317973Srmacklem	} else if (dsp != NULL) {
5381317973Srmacklem		newnfs_disconnect(nrp);
5382244042Srmacklem		nfscl_freenfsclds(dsp);
5383317973Srmacklem	}
5384244042Srmacklem	return (error);
5385244042Srmacklem}
5386244042Srmacklem
5387244042Srmacklem/*
5388244042Srmacklem * Do the NFSv4.1 Reclaim Complete.
5389244042Srmacklem */
5390244042Srmacklemint
5391244042Srmacklemnfsrpc_reclaimcomplete(struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
5392244042Srmacklem{
5393244042Srmacklem	uint32_t *tl;
5394244042Srmacklem	struct nfsrv_descript nfsd;
5395244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
5396244042Srmacklem	int error;
5397244042Srmacklem
5398244042Srmacklem	nfscl_reqstart(nd, NFSPROC_RECLAIMCOMPL, nmp, NULL, 0, NULL, NULL);
5399244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
5400244042Srmacklem	*tl = newnfs_false;
5401244042Srmacklem	nd->nd_flag |= ND_USEGSSNAME;
5402244042Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
5403244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
5404244042Srmacklem	if (error != 0)
5405244042Srmacklem		return (error);
5406244042Srmacklem	error = nd->nd_repstat;
5407244042Srmacklem	mbuf_freem(nd->nd_mrep);
5408244042Srmacklem	return (error);
5409244042Srmacklem}
5410244042Srmacklem
5411244042Srmacklem/*
5412244042Srmacklem * Initialize the slot tables for a session.
5413244042Srmacklem */
5414244042Srmacklemstatic void
5415244042Srmacklemnfscl_initsessionslots(struct nfsclsession *sep)
5416244042Srmacklem{
5417244042Srmacklem	int i;
5418244042Srmacklem
5419244042Srmacklem	for (i = 0; i < NFSV4_CBSLOTS; i++) {
5420244042Srmacklem		if (sep->nfsess_cbslots[i].nfssl_reply != NULL)
5421244042Srmacklem			m_freem(sep->nfsess_cbslots[i].nfssl_reply);
5422244042Srmacklem		NFSBZERO(&sep->nfsess_cbslots[i], sizeof(struct nfsslot));
5423244042Srmacklem	}
5424244042Srmacklem	for (i = 0; i < 64; i++)
5425244042Srmacklem		sep->nfsess_slotseq[i] = 0;
5426244042Srmacklem	sep->nfsess_slots = 0;
5427244042Srmacklem}
5428244042Srmacklem
5429244042Srmacklem/*
5430244042Srmacklem * Called to try and do an I/O operation via an NFSv4.1 Data Server (DS).
5431244042Srmacklem */
5432244042Srmacklemint
5433244042Srmacklemnfscl_doiods(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
5434321029Srmacklem    uint32_t rwaccess, int docommit, struct ucred *cred, NFSPROC_T *p)
5435244042Srmacklem{
5436244042Srmacklem	struct nfsnode *np = VTONFS(vp);
5437244042Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5438244042Srmacklem	struct nfscllayout *layp;
5439244042Srmacklem	struct nfscldevinfo *dip;
5440244042Srmacklem	struct nfsclflayout *rflp;
5441244042Srmacklem	nfsv4stateid_t stateid;
5442244042Srmacklem	struct ucred *newcred;
5443244042Srmacklem	uint64_t lastbyte, len, off, oresid, xfer;
5444244042Srmacklem	int eof, error, iolaymode, recalled;
5445244042Srmacklem	void *lckp;
5446244042Srmacklem
5447244042Srmacklem	if (!NFSHASPNFS(nmp) || nfscl_enablecallb == 0 || nfs_numnfscbd == 0 ||
5448244042Srmacklem	    (np->n_flag & NNOLAYOUT) != 0)
5449244042Srmacklem		return (EIO);
5450244042Srmacklem	/* Now, get a reference cnt on the clientid for this mount. */
5451244042Srmacklem	if (nfscl_getref(nmp) == 0)
5452244042Srmacklem		return (EIO);
5453244042Srmacklem
5454244042Srmacklem	/* Find an appropriate stateid. */
5455244042Srmacklem	newcred = NFSNEWCRED(cred);
5456244042Srmacklem	error = nfscl_getstateid(vp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
5457244042Srmacklem	    rwaccess, 1, newcred, p, &stateid, &lckp);
5458244042Srmacklem	if (error != 0) {
5459244042Srmacklem		NFSFREECRED(newcred);
5460244042Srmacklem		nfscl_relref(nmp);
5461244042Srmacklem		return (error);
5462244042Srmacklem	}
5463244042Srmacklem	/* Search for a layout for this file. */
5464244042Srmacklem	off = uiop->uio_offset;
5465244042Srmacklem	layp = nfscl_getlayout(nmp->nm_clp, np->n_fhp->nfh_fh,
5466244042Srmacklem	    np->n_fhp->nfh_len, off, &rflp, &recalled);
5467244042Srmacklem	if (layp == NULL || rflp == NULL) {
5468244042Srmacklem		if (recalled != 0) {
5469244042Srmacklem			NFSFREECRED(newcred);
5470244042Srmacklem			nfscl_relref(nmp);
5471244042Srmacklem			return (EIO);
5472244042Srmacklem		}
5473244042Srmacklem		if (layp != NULL) {
5474244042Srmacklem			nfscl_rellayout(layp, (rflp == NULL) ? 1 : 0);
5475244042Srmacklem			layp = NULL;
5476244042Srmacklem		}
5477244042Srmacklem		/* Try and get a Layout, if it is supported. */
5478244042Srmacklem		if (rwaccess == NFSV4OPEN_ACCESSWRITE ||
5479244042Srmacklem		    (np->n_flag & NWRITEOPENED) != 0)
5480244042Srmacklem			iolaymode = NFSLAYOUTIOMODE_RW;
5481244042Srmacklem		else
5482244042Srmacklem			iolaymode = NFSLAYOUTIOMODE_READ;
5483244042Srmacklem		error = nfsrpc_getlayout(nmp, vp, np->n_fhp, iolaymode,
5484244042Srmacklem		    NULL, &stateid, off, &layp, newcred, p);
5485244042Srmacklem		if (error != 0) {
5486244042Srmacklem			NFSLOCKNODE(np);
5487244042Srmacklem			np->n_flag |= NNOLAYOUT;
5488244042Srmacklem			NFSUNLOCKNODE(np);
5489244042Srmacklem			if (lckp != NULL)
5490244042Srmacklem				nfscl_lockderef(lckp);
5491244042Srmacklem			NFSFREECRED(newcred);
5492244042Srmacklem			if (layp != NULL)
5493244042Srmacklem				nfscl_rellayout(layp, 0);
5494244042Srmacklem			nfscl_relref(nmp);
5495244042Srmacklem			return (error);
5496244042Srmacklem		}
5497244042Srmacklem	}
5498244042Srmacklem
5499244042Srmacklem	/*
5500244042Srmacklem	 * Loop around finding a layout that works for the first part of
5501244042Srmacklem	 * this I/O operation, and then call the function that actually
5502244042Srmacklem	 * does the RPC.
5503244042Srmacklem	 */
5504244042Srmacklem	eof = 0;
5505244042Srmacklem	len = (uint64_t)uiop->uio_resid;
5506244042Srmacklem	while (len > 0 && error == 0 && eof == 0) {
5507244042Srmacklem		off = uiop->uio_offset;
5508244042Srmacklem		error = nfscl_findlayoutforio(layp, off, rwaccess, &rflp);
5509244042Srmacklem		if (error == 0) {
5510244042Srmacklem			oresid = xfer = (uint64_t)uiop->uio_resid;
5511244042Srmacklem			if (xfer > (rflp->nfsfl_end - rflp->nfsfl_off))
5512244042Srmacklem				xfer = rflp->nfsfl_end - rflp->nfsfl_off;
5513244042Srmacklem			dip = nfscl_getdevinfo(nmp->nm_clp, rflp->nfsfl_dev,
5514244042Srmacklem			    rflp->nfsfl_devp);
5515244042Srmacklem			if (dip != NULL) {
5516244042Srmacklem				error = nfscl_doflayoutio(vp, uiop, iomode,
5517244042Srmacklem				    must_commit, &eof, &stateid, rwaccess, dip,
5518321029Srmacklem				    layp, rflp, off, xfer, docommit, newcred,
5519321029Srmacklem				    p);
5520244042Srmacklem				nfscl_reldevinfo(dip);
5521244042Srmacklem				lastbyte = off + xfer - 1;
5522244042Srmacklem				if (error == 0) {
5523244042Srmacklem					NFSLOCKCLSTATE();
5524244042Srmacklem					if (lastbyte > layp->nfsly_lastbyte)
5525244042Srmacklem						layp->nfsly_lastbyte = lastbyte;
5526244042Srmacklem					NFSUNLOCKCLSTATE();
5527317983Srmacklem				} else if (error == NFSERR_OPENMODE &&
5528317983Srmacklem				    rwaccess == NFSV4OPEN_ACCESSREAD) {
5529317983Srmacklem					NFSLOCKMNT(nmp);
5530317983Srmacklem					nmp->nm_state |= NFSSTA_OPENMODE;
5531317983Srmacklem					NFSUNLOCKMNT(nmp);
5532244042Srmacklem				}
5533244042Srmacklem			} else
5534244042Srmacklem				error = EIO;
5535244042Srmacklem			if (error == 0)
5536244042Srmacklem				len -= (oresid - (uint64_t)uiop->uio_resid);
5537244042Srmacklem		}
5538244042Srmacklem	}
5539244042Srmacklem	if (lckp != NULL)
5540244042Srmacklem		nfscl_lockderef(lckp);
5541244042Srmacklem	NFSFREECRED(newcred);
5542244042Srmacklem	nfscl_rellayout(layp, 0);
5543244042Srmacklem	nfscl_relref(nmp);
5544244042Srmacklem	return (error);
5545244042Srmacklem}
5546244042Srmacklem
5547244042Srmacklem/*
5548244042Srmacklem * Find a file layout that will handle the first bytes of the requested
5549330446Seadler * range and return the information from it needed to the I/O operation.
5550244042Srmacklem */
5551244042Srmacklemint
5552244042Srmacklemnfscl_findlayoutforio(struct nfscllayout *lyp, uint64_t off, uint32_t rwaccess,
5553244042Srmacklem    struct nfsclflayout **retflpp)
5554244042Srmacklem{
5555244042Srmacklem	struct nfsclflayout *flp, *nflp, *rflp;
5556244042Srmacklem	uint32_t rw;
5557244042Srmacklem
5558244042Srmacklem	rflp = NULL;
5559244042Srmacklem	rw = rwaccess;
5560244042Srmacklem	/* For reading, do the Read list first and then the Write list. */
5561244042Srmacklem	do {
5562244042Srmacklem		if (rw == NFSV4OPEN_ACCESSREAD)
5563244042Srmacklem			flp = LIST_FIRST(&lyp->nfsly_flayread);
5564244042Srmacklem		else
5565244042Srmacklem			flp = LIST_FIRST(&lyp->nfsly_flayrw);
5566244042Srmacklem		while (flp != NULL) {
5567244042Srmacklem			nflp = LIST_NEXT(flp, nfsfl_list);
5568244042Srmacklem			if (flp->nfsfl_off > off)
5569244042Srmacklem				break;
5570244042Srmacklem			if (flp->nfsfl_end > off &&
5571244042Srmacklem			    (rflp == NULL || rflp->nfsfl_end < flp->nfsfl_end))
5572244042Srmacklem				rflp = flp;
5573244042Srmacklem			flp = nflp;
5574244042Srmacklem		}
5575244042Srmacklem		if (rw == NFSV4OPEN_ACCESSREAD)
5576244042Srmacklem			rw = NFSV4OPEN_ACCESSWRITE;
5577244042Srmacklem		else
5578244042Srmacklem			rw = 0;
5579244042Srmacklem	} while (rw != 0);
5580244042Srmacklem	if (rflp != NULL) {
5581244042Srmacklem		/* This one covers the most bytes starting at off. */
5582244042Srmacklem		*retflpp = rflp;
5583244042Srmacklem		return (0);
5584244042Srmacklem	}
5585244042Srmacklem	return (EIO);
5586244042Srmacklem}
5587244042Srmacklem
5588244042Srmacklem/*
5589244042Srmacklem * Do I/O using an NFSv4.1 file layout.
5590244042Srmacklem */
5591244042Srmacklemstatic int
5592244042Srmacklemnfscl_doflayoutio(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
5593244042Srmacklem    int *eofp, nfsv4stateid_t *stateidp, int rwflag, struct nfscldevinfo *dp,
5594244042Srmacklem    struct nfscllayout *lyp, struct nfsclflayout *flp, uint64_t off,
5595321029Srmacklem    uint64_t len, int docommit, struct ucred *cred, NFSPROC_T *p)
5596244042Srmacklem{
5597244042Srmacklem	uint64_t io_off, rel_off, stripe_unit_size, transfer, xfer;
5598321029Srmacklem	int commit_thru_mds, error, stripe_index, stripe_pos;
5599244042Srmacklem	struct nfsnode *np;
5600244042Srmacklem	struct nfsfh *fhp;
5601244042Srmacklem	struct nfsclds **dspp;
5602244042Srmacklem
5603244042Srmacklem	np = VTONFS(vp);
5604244042Srmacklem	rel_off = off - flp->nfsfl_patoff;
5605244042Srmacklem	stripe_unit_size = (flp->nfsfl_util >> 6) & 0x3ffffff;
5606244042Srmacklem	stripe_pos = (rel_off / stripe_unit_size + flp->nfsfl_stripe1) %
5607244042Srmacklem	    dp->nfsdi_stripecnt;
5608244042Srmacklem	transfer = stripe_unit_size - (rel_off % stripe_unit_size);
5609321029Srmacklem	error = 0;
5610244042Srmacklem
5611244042Srmacklem	/* Loop around, doing I/O for each stripe unit. */
5612244042Srmacklem	while (len > 0 && error == 0) {
5613244042Srmacklem		stripe_index = nfsfldi_stripeindex(dp, stripe_pos);
5614244042Srmacklem		dspp = nfsfldi_addr(dp, stripe_index);
5615321029Srmacklem		if (len > transfer && docommit == 0)
5616244042Srmacklem			xfer = transfer;
5617244042Srmacklem		else
5618244042Srmacklem			xfer = len;
5619244042Srmacklem		if ((flp->nfsfl_util & NFSFLAYUTIL_DENSE) != 0) {
5620244042Srmacklem			/* Dense layout. */
5621244042Srmacklem			if (stripe_pos >= flp->nfsfl_fhcnt)
5622244042Srmacklem				return (EIO);
5623244042Srmacklem			fhp = flp->nfsfl_fh[stripe_pos];
5624244042Srmacklem			io_off = (rel_off / (stripe_unit_size *
5625244042Srmacklem			    dp->nfsdi_stripecnt)) * stripe_unit_size +
5626244042Srmacklem			    rel_off % stripe_unit_size;
5627244042Srmacklem		} else {
5628244042Srmacklem			/* Sparse layout. */
5629244042Srmacklem			if (flp->nfsfl_fhcnt > 1) {
5630244042Srmacklem				if (stripe_index >= flp->nfsfl_fhcnt)
5631244042Srmacklem					return (EIO);
5632244042Srmacklem				fhp = flp->nfsfl_fh[stripe_index];
5633244042Srmacklem			} else if (flp->nfsfl_fhcnt == 1)
5634244042Srmacklem				fhp = flp->nfsfl_fh[0];
5635244042Srmacklem			else
5636244042Srmacklem				fhp = np->n_fhp;
5637244042Srmacklem			io_off = off;
5638244042Srmacklem		}
5639321029Srmacklem		if ((flp->nfsfl_util & NFSFLAYUTIL_COMMIT_THRU_MDS) != 0) {
5640244042Srmacklem			commit_thru_mds = 1;
5641321029Srmacklem			if (docommit != 0)
5642321029Srmacklem				error = EIO;
5643321029Srmacklem		} else {
5644244042Srmacklem			commit_thru_mds = 0;
5645321029Srmacklem			mtx_lock(&np->n_mtx);
5646321029Srmacklem			np->n_flag |= NDSCOMMIT;
5647321029Srmacklem			mtx_unlock(&np->n_mtx);
5648321029Srmacklem		}
5649321029Srmacklem		if (docommit != 0) {
5650321029Srmacklem			if (error == 0)
5651321029Srmacklem				error = nfsrpc_commitds(vp, io_off, xfer,
5652321029Srmacklem				    *dspp, fhp, cred, p);
5653321029Srmacklem			if (error == 0) {
5654321029Srmacklem				/*
5655321029Srmacklem				 * Set both eof and uio_resid = 0 to end any
5656321029Srmacklem				 * loops.
5657321029Srmacklem				 */
5658321029Srmacklem				*eofp = 1;
5659321029Srmacklem				uiop->uio_resid = 0;
5660321029Srmacklem			} else {
5661321029Srmacklem				mtx_lock(&np->n_mtx);
5662321029Srmacklem				np->n_flag &= ~NDSCOMMIT;
5663321029Srmacklem				mtx_unlock(&np->n_mtx);
5664321029Srmacklem			}
5665324180Srmacklem		} else if (rwflag == NFSV4OPEN_ACCESSREAD)
5666244042Srmacklem			error = nfsrpc_readds(vp, uiop, stateidp, eofp, *dspp,
5667244042Srmacklem			    io_off, xfer, fhp, cred, p);
5668244042Srmacklem		else {
5669244042Srmacklem			error = nfsrpc_writeds(vp, uiop, iomode, must_commit,
5670244042Srmacklem			    stateidp, *dspp, io_off, xfer, fhp, commit_thru_mds,
5671244042Srmacklem			    cred, p);
5672244042Srmacklem			if (error == 0) {
5673244042Srmacklem				NFSLOCKCLSTATE();
5674244042Srmacklem				lyp->nfsly_flags |= NFSLY_WRITTEN;
5675244042Srmacklem				NFSUNLOCKCLSTATE();
5676244042Srmacklem			}
5677244042Srmacklem		}
5678244042Srmacklem		if (error == 0) {
5679244042Srmacklem			transfer = stripe_unit_size;
5680244042Srmacklem			stripe_pos = (stripe_pos + 1) % dp->nfsdi_stripecnt;
5681244042Srmacklem			len -= xfer;
5682244042Srmacklem			off += xfer;
5683244042Srmacklem		}
5684244042Srmacklem	}
5685244042Srmacklem	return (error);
5686244042Srmacklem}
5687244042Srmacklem
5688244042Srmacklem/*
5689244042Srmacklem * The actual read RPC done to a DS.
5690244042Srmacklem */
5691244042Srmacklemstatic int
5692244042Srmacklemnfsrpc_readds(vnode_t vp, struct uio *uiop, nfsv4stateid_t *stateidp, int *eofp,
5693244042Srmacklem    struct nfsclds *dsp, uint64_t io_off, int len, struct nfsfh *fhp,
5694244042Srmacklem    struct ucred *cred, NFSPROC_T *p)
5695244042Srmacklem{
5696244042Srmacklem	uint32_t *tl;
5697244042Srmacklem	int error, retlen;
5698244042Srmacklem	struct nfsrv_descript nfsd;
5699244042Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5700244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
5701244042Srmacklem	struct nfssockreq *nrp;
5702244042Srmacklem
5703244042Srmacklem	nd->nd_mrep = NULL;
5704244042Srmacklem	nfscl_reqstart(nd, NFSPROC_READDS, nmp, fhp->nfh_fh, fhp->nfh_len,
5705244042Srmacklem	    NULL, &dsp->nfsclds_sess);
5706244042Srmacklem	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSEQIDZERO);
5707244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED * 3);
5708244042Srmacklem	txdr_hyper(io_off, tl);
5709244042Srmacklem	*(tl + 2) = txdr_unsigned(len);
5710244042Srmacklem	nrp = dsp->nfsclds_sockp;
5711244042Srmacklem	if (nrp == NULL)
5712244042Srmacklem		/* If NULL, use the MDS socket. */
5713244042Srmacklem		nrp = &nmp->nm_sockreq;
5714244042Srmacklem	error = newnfs_request(nd, nmp, NULL, nrp, vp, p, cred,
5715244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
5716244042Srmacklem	if (error != 0)
5717244042Srmacklem		return (error);
5718244042Srmacklem	if (nd->nd_repstat != 0) {
5719244042Srmacklem		error = nd->nd_repstat;
5720244042Srmacklem		goto nfsmout;
5721244042Srmacklem	}
5722244042Srmacklem	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
5723244042Srmacklem	*eofp = fxdr_unsigned(int, *tl);
5724244042Srmacklem	NFSM_STRSIZ(retlen, len);
5725244042Srmacklem	error = nfsm_mbufuio(nd, uiop, retlen);
5726244042Srmacklemnfsmout:
5727244042Srmacklem	if (nd->nd_mrep != NULL)
5728244042Srmacklem		mbuf_freem(nd->nd_mrep);
5729244042Srmacklem	return (error);
5730244042Srmacklem}
5731244042Srmacklem
5732244042Srmacklem/*
5733244042Srmacklem * The actual write RPC done to a DS.
5734244042Srmacklem */
5735244042Srmacklemstatic int
5736244042Srmacklemnfsrpc_writeds(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
5737244042Srmacklem    nfsv4stateid_t *stateidp, struct nfsclds *dsp, uint64_t io_off, int len,
5738244042Srmacklem    struct nfsfh *fhp, int commit_thru_mds, struct ucred *cred, NFSPROC_T *p)
5739244042Srmacklem{
5740244042Srmacklem	uint32_t *tl;
5741244042Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5742244042Srmacklem	int error, rlen, commit, committed = NFSWRITE_FILESYNC;
5743244042Srmacklem	int32_t backup;
5744244042Srmacklem	struct nfsrv_descript nfsd;
5745244042Srmacklem	struct nfsrv_descript *nd = &nfsd;
5746244042Srmacklem	struct nfssockreq *nrp;
5747244042Srmacklem
5748244042Srmacklem	KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
5749244042Srmacklem	nd->nd_mrep = NULL;
5750244042Srmacklem	nfscl_reqstart(nd, NFSPROC_WRITEDS, nmp, fhp->nfh_fh, fhp->nfh_len,
5751244042Srmacklem	    NULL, &dsp->nfsclds_sess);
5752244042Srmacklem	nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSEQIDZERO);
5753244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
5754244042Srmacklem	txdr_hyper(io_off, tl);
5755244042Srmacklem	tl += 2;
5756244042Srmacklem	*tl++ = txdr_unsigned(*iomode);
5757244042Srmacklem	*tl = txdr_unsigned(len);
5758244042Srmacklem	nfsm_uiombuf(nd, uiop, len);
5759244042Srmacklem	nrp = dsp->nfsclds_sockp;
5760244042Srmacklem	if (nrp == NULL)
5761244042Srmacklem		/* If NULL, use the MDS socket. */
5762244042Srmacklem		nrp = &nmp->nm_sockreq;
5763244042Srmacklem	error = newnfs_request(nd, nmp, NULL, nrp, vp, p, cred,
5764244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
5765244042Srmacklem	if (error != 0)
5766244042Srmacklem		return (error);
5767244042Srmacklem	if (nd->nd_repstat != 0) {
5768244042Srmacklem		/*
5769244042Srmacklem		 * In case the rpc gets retried, roll
5770244042Srmacklem		 * the uio fileds changed by nfsm_uiombuf()
5771244042Srmacklem		 * back.
5772244042Srmacklem		 */
5773244042Srmacklem		uiop->uio_offset -= len;
5774244042Srmacklem		uio_uio_resid_add(uiop, len);
5775244042Srmacklem		uio_iov_base_add(uiop, -len);
5776244042Srmacklem		uio_iov_len_add(uiop, len);
5777244042Srmacklem		error = nd->nd_repstat;
5778244042Srmacklem	} else {
5779244042Srmacklem		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + NFSX_VERF);
5780244042Srmacklem		rlen = fxdr_unsigned(int, *tl++);
5781244042Srmacklem		if (rlen == 0) {
5782244042Srmacklem			error = NFSERR_IO;
5783244042Srmacklem			goto nfsmout;
5784244042Srmacklem		} else if (rlen < len) {
5785244042Srmacklem			backup = len - rlen;
5786244042Srmacklem			uio_iov_base_add(uiop, -(backup));
5787244042Srmacklem			uio_iov_len_add(uiop, backup);
5788244042Srmacklem			uiop->uio_offset -= backup;
5789244042Srmacklem			uio_uio_resid_add(uiop, backup);
5790244042Srmacklem			len = rlen;
5791244042Srmacklem		}
5792244042Srmacklem		commit = fxdr_unsigned(int, *tl++);
5793244042Srmacklem
5794244042Srmacklem		/*
5795298788Spfg		 * Return the lowest commitment level
5796244042Srmacklem		 * obtained by any of the RPCs.
5797244042Srmacklem		 */
5798244042Srmacklem		if (committed == NFSWRITE_FILESYNC)
5799244042Srmacklem			committed = commit;
5800244042Srmacklem		else if (committed == NFSWRITE_DATASYNC &&
5801244042Srmacklem		    commit == NFSWRITE_UNSTABLE)
5802244042Srmacklem			committed = commit;
5803244042Srmacklem		if (commit_thru_mds != 0) {
5804244042Srmacklem			NFSLOCKMNT(nmp);
5805244042Srmacklem			if (!NFSHASWRITEVERF(nmp)) {
5806244042Srmacklem				NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
5807244042Srmacklem				NFSSETWRITEVERF(nmp);
5808244042Srmacklem	    		} else if (NFSBCMP(tl, nmp->nm_verf, NFSX_VERF)) {
5809244042Srmacklem				*must_commit = 1;
5810244042Srmacklem				NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
5811244042Srmacklem			}
5812244042Srmacklem			NFSUNLOCKMNT(nmp);
5813244042Srmacklem		} else {
5814244042Srmacklem			NFSLOCKDS(dsp);
5815244042Srmacklem			if ((dsp->nfsclds_flags & NFSCLDS_HASWRITEVERF) == 0) {
5816244042Srmacklem				NFSBCOPY(tl, dsp->nfsclds_verf, NFSX_VERF);
5817244042Srmacklem				dsp->nfsclds_flags |= NFSCLDS_HASWRITEVERF;
5818244042Srmacklem			} else if (NFSBCMP(tl, dsp->nfsclds_verf, NFSX_VERF)) {
5819244042Srmacklem				*must_commit = 1;
5820244042Srmacklem				NFSBCOPY(tl, dsp->nfsclds_verf, NFSX_VERF);
5821244042Srmacklem			}
5822244042Srmacklem			NFSUNLOCKDS(dsp);
5823244042Srmacklem		}
5824244042Srmacklem	}
5825244042Srmacklemnfsmout:
5826244042Srmacklem	if (nd->nd_mrep != NULL)
5827244042Srmacklem		mbuf_freem(nd->nd_mrep);
5828244042Srmacklem	*iomode = committed;
5829244042Srmacklem	if (nd->nd_repstat != 0 && error == 0)
5830244042Srmacklem		error = nd->nd_repstat;
5831244042Srmacklem	return (error);
5832244042Srmacklem}
5833244042Srmacklem
5834244042Srmacklem/*
5835244042Srmacklem * Free up the nfsclds structure.
5836244042Srmacklem */
5837244042Srmacklemvoid
5838244042Srmacklemnfscl_freenfsclds(struct nfsclds *dsp)
5839244042Srmacklem{
5840244042Srmacklem	int i;
5841244042Srmacklem
5842244042Srmacklem	if (dsp == NULL)
5843244042Srmacklem		return;
5844244042Srmacklem	if (dsp->nfsclds_sockp != NULL) {
5845244042Srmacklem		NFSFREECRED(dsp->nfsclds_sockp->nr_cred);
5846244042Srmacklem		NFSFREEMUTEX(&dsp->nfsclds_sockp->nr_mtx);
5847244042Srmacklem		free(dsp->nfsclds_sockp->nr_nam, M_SONAME);
5848244042Srmacklem		free(dsp->nfsclds_sockp, M_NFSSOCKREQ);
5849244042Srmacklem	}
5850244042Srmacklem	NFSFREEMUTEX(&dsp->nfsclds_mtx);
5851244042Srmacklem	NFSFREEMUTEX(&dsp->nfsclds_sess.nfsess_mtx);
5852244042Srmacklem	for (i = 0; i < NFSV4_CBSLOTS; i++) {
5853244042Srmacklem		if (dsp->nfsclds_sess.nfsess_cbslots[i].nfssl_reply != NULL)
5854244042Srmacklem			m_freem(
5855244042Srmacklem			    dsp->nfsclds_sess.nfsess_cbslots[i].nfssl_reply);
5856244042Srmacklem	}
5857244042Srmacklem	free(dsp, M_NFSCLDS);
5858244042Srmacklem}
5859244042Srmacklem
5860244042Srmacklemstatic enum nfsclds_state
5861244042Srmacklemnfscl_getsameserver(struct nfsmount *nmp, struct nfsclds *newdsp,
5862244042Srmacklem    struct nfsclds **retdspp)
5863244042Srmacklem{
5864244042Srmacklem	struct nfsclds *dsp, *cur_dsp;
5865244042Srmacklem
5866244042Srmacklem	/*
5867244042Srmacklem	 * Search the list of nfsclds structures for one with the same
5868244042Srmacklem	 * server.
5869244042Srmacklem	 */
5870244042Srmacklem	cur_dsp = NULL;
5871244042Srmacklem	TAILQ_FOREACH(dsp, &nmp->nm_sess, nfsclds_list) {
5872244042Srmacklem		if (dsp->nfsclds_servownlen == newdsp->nfsclds_servownlen &&
5873244042Srmacklem		    dsp->nfsclds_servownlen != 0 &&
5874244042Srmacklem		    !NFSBCMP(dsp->nfsclds_serverown, newdsp->nfsclds_serverown,
5875317393Srmacklem		    dsp->nfsclds_servownlen) &&
5876317393Srmacklem		    dsp->nfsclds_sess.nfsess_defunct == 0) {
5877244042Srmacklem			NFSCL_DEBUG(4, "fnd same fdsp=%p dsp=%p flg=0x%x\n",
5878244042Srmacklem			    TAILQ_FIRST(&nmp->nm_sess), dsp,
5879244042Srmacklem			    dsp->nfsclds_flags);
5880244042Srmacklem			/* Server major id matches. */
5881244042Srmacklem			if ((dsp->nfsclds_flags & NFSCLDS_DS) != 0) {
5882244042Srmacklem				*retdspp = dsp;
5883244042Srmacklem				return (NFSDSP_USETHISSESSION);
5884244042Srmacklem			}
5885244042Srmacklem
5886244042Srmacklem			/*
5887244042Srmacklem			 * Note the first match, so it can be used for
5888244042Srmacklem			 * sequence'ing new sessions.
5889244042Srmacklem			 */
5890244042Srmacklem			if (cur_dsp == NULL)
5891244042Srmacklem				cur_dsp = dsp;
5892244042Srmacklem		}
5893244042Srmacklem	}
5894244042Srmacklem	if (cur_dsp != NULL) {
5895244042Srmacklem		*retdspp = cur_dsp;
5896244042Srmacklem		return (NFSDSP_SEQTHISSESSION);
5897244042Srmacklem	}
5898244042Srmacklem	return (NFSDSP_NOTFOUND);
5899244042Srmacklem}
5900244042Srmacklem
5901244042Srmacklem/*
5902321029Srmacklem * NFS commit rpc to a NFSv4.1 DS.
5903244042Srmacklem */
5904244042Srmacklemstatic int
5905244042Srmacklemnfsrpc_commitds(vnode_t vp, uint64_t offset, int cnt, struct nfsclds *dsp,
5906321029Srmacklem    struct nfsfh *fhp, struct ucred *cred, NFSPROC_T *p)
5907244042Srmacklem{
5908244042Srmacklem	uint32_t *tl;
5909244042Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
5910244042Srmacklem	struct nfsmount *nmp = VFSTONFS(vnode_mount(vp));
5911244042Srmacklem	struct nfssockreq *nrp;
5912244042Srmacklem	int error;
5913244042Srmacklem
5914321029Srmacklem	nd->nd_mrep = NULL;
5915244042Srmacklem	nfscl_reqstart(nd, NFSPROC_COMMITDS, nmp, fhp->nfh_fh, fhp->nfh_len,
5916244042Srmacklem	    NULL, &dsp->nfsclds_sess);
5917244042Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
5918244042Srmacklem	txdr_hyper(offset, tl);
5919244042Srmacklem	tl += 2;
5920244042Srmacklem	*tl = txdr_unsigned(cnt);
5921244042Srmacklem	nrp = dsp->nfsclds_sockp;
5922244042Srmacklem	if (nrp == NULL)
5923244042Srmacklem		/* If NULL, use the MDS socket. */
5924244042Srmacklem		nrp = &nmp->nm_sockreq;
5925244042Srmacklem	error = newnfs_request(nd, nmp, NULL, nrp, vp, p, cred,
5926244042Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
5927321029Srmacklem	if (error != 0)
5928244042Srmacklem		return (error);
5929244042Srmacklem	if (nd->nd_repstat == 0) {
5930244042Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
5931244042Srmacklem		NFSLOCKDS(dsp);
5932244042Srmacklem		if (NFSBCMP(tl, dsp->nfsclds_verf, NFSX_VERF)) {
5933244042Srmacklem			NFSBCOPY(tl, dsp->nfsclds_verf, NFSX_VERF);
5934244042Srmacklem			error = NFSERR_STALEWRITEVERF;
5935244042Srmacklem		}
5936244042Srmacklem		NFSUNLOCKDS(dsp);
5937244042Srmacklem	}
5938244042Srmacklemnfsmout:
5939244042Srmacklem	if (error == 0 && nd->nd_repstat != 0)
5940244042Srmacklem		error = nd->nd_repstat;
5941244042Srmacklem	mbuf_freem(nd->nd_mrep);
5942244042Srmacklem	return (error);
5943244042Srmacklem}
5944244042Srmacklem
5945320998Srmacklem/*
5946320998Srmacklem * Set up the XDR arguments for the LayoutGet operation.
5947320998Srmacklem */
5948320998Srmacklemstatic void
5949320998Srmacklemnfsrv_setuplayoutget(struct nfsrv_descript *nd, int iomode, uint64_t offset,
5950320998Srmacklem    uint64_t len, uint64_t minlen, nfsv4stateid_t *stateidp, int layoutlen,
5951320998Srmacklem    int usecurstateid)
5952320998Srmacklem{
5953320998Srmacklem	uint32_t *tl;
5954320998Srmacklem
5955320998Srmacklem	NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
5956320998Srmacklem	    NFSX_STATEID);
5957320998Srmacklem	*tl++ = newnfs_false;		/* Don't signal availability. */
5958320998Srmacklem	*tl++ = txdr_unsigned(NFSLAYOUT_NFSV4_1_FILES);
5959320998Srmacklem	*tl++ = txdr_unsigned(iomode);
5960320998Srmacklem	txdr_hyper(offset, tl);
5961320998Srmacklem	tl += 2;
5962320998Srmacklem	txdr_hyper(len, tl);
5963320998Srmacklem	tl += 2;
5964320998Srmacklem	txdr_hyper(minlen, tl);
5965320998Srmacklem	tl += 2;
5966320998Srmacklem	if (usecurstateid != 0) {
5967320998Srmacklem		/* Special stateid for Current stateid. */
5968320998Srmacklem		*tl++ = txdr_unsigned(1);
5969320998Srmacklem		*tl++ = 0;
5970320998Srmacklem		*tl++ = 0;
5971320998Srmacklem		*tl++ = 0;
5972320998Srmacklem	} else {
5973320998Srmacklem		*tl++ = txdr_unsigned(stateidp->seqid);
5974320998Srmacklem		NFSCL_DEBUG(4, "layget seq=%d\n", (int)stateidp->seqid);
5975320998Srmacklem		*tl++ = stateidp->other[0];
5976320998Srmacklem		*tl++ = stateidp->other[1];
5977320998Srmacklem		*tl++ = stateidp->other[2];
5978320998Srmacklem	}
5979320998Srmacklem	*tl = txdr_unsigned(layoutlen);
5980320998Srmacklem}
5981320998Srmacklem
5982320998Srmacklem/*
5983320998Srmacklem * Parse the reply for a successful LayoutGet operation.
5984320998Srmacklem */
5985320998Srmacklemstatic int
5986320998Srmacklemnfsrv_parselayoutget(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp,
5987320998Srmacklem    int *retonclosep, struct nfsclflayouthead *flhp)
5988320998Srmacklem{
5989320998Srmacklem	uint32_t *tl;
5990320998Srmacklem	struct nfsclflayout *flp, *prevflp, *tflp;
5991320998Srmacklem	int cnt, error, gotiomode, fhcnt, nfhlen, i, j;
5992320998Srmacklem	uint64_t retlen;
5993320998Srmacklem	struct nfsfh *nfhp;
5994320998Srmacklem	uint8_t *cp;
5995320998Srmacklem
5996320998Srmacklem	error = 0;
5997320998Srmacklem	flp = NULL;
5998320998Srmacklem	gotiomode = -1;
5999320998Srmacklem	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + NFSX_STATEID);
6000320998Srmacklem	if (*tl++ != 0)
6001320998Srmacklem		*retonclosep = 1;
6002320998Srmacklem	else
6003320998Srmacklem		*retonclosep = 0;
6004320998Srmacklem	stateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
6005320998Srmacklem	NFSCL_DEBUG(4, "retoncls=%d stseq=%d\n", *retonclosep,
6006320998Srmacklem	    (int)stateidp->seqid);
6007320998Srmacklem	stateidp->other[0] = *tl++;
6008320998Srmacklem	stateidp->other[1] = *tl++;
6009320998Srmacklem	stateidp->other[2] = *tl++;
6010320998Srmacklem	cnt = fxdr_unsigned(int, *tl);
6011320998Srmacklem	NFSCL_DEBUG(4, "layg cnt=%d\n", cnt);
6012320998Srmacklem	if (cnt <= 0 || cnt > 10000) {
6013320998Srmacklem		/* Don't accept more than 10000 layouts in reply. */
6014320998Srmacklem		error = NFSERR_BADXDR;
6015320998Srmacklem		goto nfsmout;
6016320998Srmacklem	}
6017320998Srmacklem	for (i = 0; i < cnt; i++) {
6018320998Srmacklem		/* Dissect all the way to the file handle cnt. */
6019320998Srmacklem		NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_HYPER +
6020320998Srmacklem		    6 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
6021320998Srmacklem		fhcnt = fxdr_unsigned(int, *(tl + 11 +
6022320998Srmacklem		    NFSX_V4DEVICEID / NFSX_UNSIGNED));
6023320998Srmacklem		NFSCL_DEBUG(4, "fhcnt=%d\n", fhcnt);
6024320998Srmacklem		if (fhcnt < 0 || fhcnt > 100) {
6025320998Srmacklem			/* Don't accept more than 100 file handles. */
6026320998Srmacklem			error = NFSERR_BADXDR;
6027320998Srmacklem			goto nfsmout;
6028320998Srmacklem		}
6029320998Srmacklem		if (fhcnt > 1)
6030320998Srmacklem			flp = malloc(sizeof(*flp) + (fhcnt - 1) *
6031320998Srmacklem			    sizeof(struct nfsfh *), M_NFSFLAYOUT, M_WAITOK);
6032320998Srmacklem		else
6033320998Srmacklem			flp = malloc(sizeof(*flp), M_NFSFLAYOUT, M_WAITOK);
6034320998Srmacklem		flp->nfsfl_flags = 0;
6035320998Srmacklem		flp->nfsfl_fhcnt = 0;
6036320998Srmacklem		flp->nfsfl_devp = NULL;
6037320998Srmacklem		flp->nfsfl_off = fxdr_hyper(tl); tl += 2;
6038320998Srmacklem		retlen = fxdr_hyper(tl); tl += 2;
6039320998Srmacklem		if (flp->nfsfl_off + retlen < flp->nfsfl_off)
6040320998Srmacklem			flp->nfsfl_end = UINT64_MAX - flp->nfsfl_off;
6041320998Srmacklem		else
6042320998Srmacklem			flp->nfsfl_end = flp->nfsfl_off + retlen;
6043320998Srmacklem		flp->nfsfl_iomode = fxdr_unsigned(int, *tl++);
6044320998Srmacklem		if (gotiomode == -1)
6045320998Srmacklem			gotiomode = flp->nfsfl_iomode;
6046320998Srmacklem		if (fxdr_unsigned(int, *tl++) != NFSLAYOUT_NFSV4_1_FILES) {
6047320998Srmacklem			printf("NFSv4.1: got non-files layout\n");
6048320998Srmacklem			error = NFSERR_BADXDR;
6049320998Srmacklem			goto nfsmout;
6050320998Srmacklem		}
6051320998Srmacklem		NFSBCOPY(++tl, flp->nfsfl_dev, NFSX_V4DEVICEID);
6052320998Srmacklem		tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
6053320998Srmacklem		flp->nfsfl_util = fxdr_unsigned(uint32_t, *tl++);
6054320998Srmacklem		NFSCL_DEBUG(4, "flutil=0x%x\n", flp->nfsfl_util);
6055320998Srmacklem		flp->nfsfl_stripe1 = fxdr_unsigned(uint32_t, *tl++);
6056320998Srmacklem		flp->nfsfl_patoff = fxdr_hyper(tl); tl += 2;
6057320998Srmacklem		if (fxdr_unsigned(int, *tl) != fhcnt) {
6058320998Srmacklem			printf("EEK! bad fhcnt\n");
6059320998Srmacklem			error = NFSERR_BADXDR;
6060320998Srmacklem			goto nfsmout;
6061320998Srmacklem		}
6062320998Srmacklem		for (j = 0; j < fhcnt; j++) {
6063320998Srmacklem			NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
6064320998Srmacklem			nfhlen = fxdr_unsigned(int, *tl);
6065320998Srmacklem			if (nfhlen <= 0 || nfhlen > NFSX_V4FHMAX) {
6066320998Srmacklem				error = NFSERR_BADXDR;
6067320998Srmacklem				goto nfsmout;
6068320998Srmacklem			}
6069320998Srmacklem			nfhp = malloc(sizeof(*nfhp) + nfhlen - 1, M_NFSFH,
6070320998Srmacklem			    M_WAITOK);
6071320998Srmacklem			flp->nfsfl_fh[j] = nfhp;
6072320998Srmacklem			flp->nfsfl_fhcnt++;
6073320998Srmacklem			nfhp->nfh_len = nfhlen;
6074320998Srmacklem			NFSM_DISSECT(cp, uint8_t *, NFSM_RNDUP(nfhlen));
6075320998Srmacklem			NFSBCOPY(cp, nfhp->nfh_fh, nfhlen);
6076320998Srmacklem		}
6077320998Srmacklem		if (flp->nfsfl_iomode == gotiomode) {
6078320998Srmacklem			/* Keep the list in increasing offset order. */
6079320998Srmacklem			tflp = LIST_FIRST(flhp);
6080320998Srmacklem			prevflp = NULL;
6081320998Srmacklem			while (tflp != NULL &&
6082320998Srmacklem			    tflp->nfsfl_off < flp->nfsfl_off) {
6083320998Srmacklem				prevflp = tflp;
6084320998Srmacklem				tflp = LIST_NEXT(tflp, nfsfl_list);
6085320998Srmacklem			}
6086320998Srmacklem			if (prevflp == NULL)
6087320998Srmacklem				LIST_INSERT_HEAD(flhp, flp, nfsfl_list);
6088320998Srmacklem			else
6089320998Srmacklem				LIST_INSERT_AFTER(prevflp, flp,
6090320998Srmacklem				    nfsfl_list);
6091320998Srmacklem		} else {
6092320998Srmacklem			printf("nfscl_layoutget(): got wrong iomode\n");
6093320998Srmacklem			nfscl_freeflayout(flp);
6094320998Srmacklem		}
6095320998Srmacklem		flp = NULL;
6096320998Srmacklem	}
6097320998Srmacklemnfsmout:
6098320998Srmacklem	if (error != 0 && flp != NULL)
6099320998Srmacklem		nfscl_freeflayout(flp);
6100320998Srmacklem	return (error);
6101320998Srmacklem}
6102320998Srmacklem
6103320998Srmacklem/*
6104320998Srmacklem * Similar to nfsrpc_getlayout(), except that it uses nfsrpc_openlayget(),
6105320998Srmacklem * so that it does both an Open and a Layoutget.
6106320998Srmacklem */
6107320998Srmacklemstatic int
6108320998Srmacklemnfsrpc_getopenlayout(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp,
6109320998Srmacklem    int fhlen, uint8_t *newfhp, int newfhlen, uint32_t mode,
6110320998Srmacklem    struct nfsclopen *op, uint8_t *name, int namelen, struct nfscldeleg **dpp,
6111320998Srmacklem    struct ucred *cred, NFSPROC_T *p)
6112320998Srmacklem{
6113320998Srmacklem	struct nfscllayout *lyp;
6114320998Srmacklem	struct nfsclflayout *flp;
6115320998Srmacklem	struct nfsclflayouthead flh;
6116320998Srmacklem	int error, islocked, layoutlen, recalled, retonclose, usecurstateid;
6117320998Srmacklem	int laystat;
6118320998Srmacklem	nfsv4stateid_t stateid;
6119320998Srmacklem	struct nfsclsession *tsep;
6120320998Srmacklem
6121320998Srmacklem	error = 0;
6122320998Srmacklem	/*
6123320998Srmacklem	 * If lyp is returned non-NULL, there will be a refcnt (shared lock)
6124320998Srmacklem	 * on it, iff flp != NULL or a lock (exclusive lock) on it iff
6125320998Srmacklem	 * flp == NULL.
6126320998Srmacklem	 */
6127320998Srmacklem	lyp = nfscl_getlayout(nmp->nm_clp, newfhp, newfhlen, 0, &flp,
6128320998Srmacklem	    &recalled);
6129320998Srmacklem	NFSCL_DEBUG(4, "nfsrpc_getopenlayout nfscl_getlayout lyp=%p\n", lyp);
6130320998Srmacklem	if (lyp == NULL)
6131320998Srmacklem		islocked = 0;
6132320998Srmacklem	else if (flp != NULL)
6133320998Srmacklem		islocked = 1;
6134320998Srmacklem	else
6135320998Srmacklem		islocked = 2;
6136320998Srmacklem	if ((lyp == NULL || flp == NULL) && recalled == 0) {
6137320998Srmacklem		LIST_INIT(&flh);
6138320998Srmacklem		tsep = nfsmnt_mdssession(nmp);
6139320998Srmacklem		layoutlen = tsep->nfsess_maxcache - (NFSX_STATEID +
6140320998Srmacklem		    3 * NFSX_UNSIGNED);
6141320998Srmacklem		if (lyp == NULL)
6142320998Srmacklem			usecurstateid = 1;
6143320998Srmacklem		else {
6144320998Srmacklem			usecurstateid = 0;
6145320998Srmacklem			stateid.seqid = lyp->nfsly_stateid.seqid;
6146320998Srmacklem			stateid.other[0] = lyp->nfsly_stateid.other[0];
6147320998Srmacklem			stateid.other[1] = lyp->nfsly_stateid.other[1];
6148320998Srmacklem			stateid.other[2] = lyp->nfsly_stateid.other[2];
6149320998Srmacklem		}
6150320998Srmacklem		error = nfsrpc_openlayoutrpc(nmp, vp, nfhp, fhlen,
6151320998Srmacklem		    newfhp, newfhlen, mode, op, name, namelen,
6152320998Srmacklem		    dpp, &stateid, usecurstateid, layoutlen,
6153320998Srmacklem		    &retonclose, &flh, &laystat, cred, p);
6154320998Srmacklem		NFSCL_DEBUG(4, "aft nfsrpc_openlayoutrpc laystat=%d err=%d\n",
6155320998Srmacklem		    laystat, error);
6156320998Srmacklem		laystat = nfsrpc_layoutgetres(nmp, vp, newfhp, newfhlen,
6157320998Srmacklem		    &stateid, retonclose, NULL, &lyp, &flh, laystat, &islocked,
6158320998Srmacklem		    cred, p);
6159320998Srmacklem	} else
6160320998Srmacklem		error = nfsrpc_openrpc(nmp, vp, nfhp, fhlen, newfhp, newfhlen,
6161320998Srmacklem		    mode, op, name, namelen, dpp, 0, 0, cred, p, 0, 0);
6162320998Srmacklem	if (islocked == 2)
6163320998Srmacklem		nfscl_rellayout(lyp, 1);
6164320998Srmacklem	else if (islocked == 1)
6165320998Srmacklem		nfscl_rellayout(lyp, 0);
6166320998Srmacklem	return (error);
6167320998Srmacklem}
6168320998Srmacklem
6169320998Srmacklem/*
6170320998Srmacklem * This function does an Open+LayoutGet for an NFSv4.1 mount with pNFS
6171320998Srmacklem * enabled, only for the CLAIM_NULL case.  All other NFSv4 Opens are
6172320998Srmacklem * handled by nfsrpc_openrpc().
6173320998Srmacklem * For the case where op == NULL, dvp is the directory.  When op != NULL, it
6174320998Srmacklem * can be NULL.
6175320998Srmacklem */
6176320998Srmacklemstatic int
6177320998Srmacklemnfsrpc_openlayoutrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp,
6178320998Srmacklem    int fhlen, uint8_t *newfhp, int newfhlen, uint32_t mode,
6179320998Srmacklem    struct nfsclopen *op, uint8_t *name, int namelen, struct nfscldeleg **dpp,
6180320998Srmacklem    nfsv4stateid_t *stateidp, int usecurstateid,
6181320998Srmacklem    int layoutlen, int *retonclosep, struct nfsclflayouthead *flhp,
6182320998Srmacklem    int *laystatp, struct ucred *cred, NFSPROC_T *p)
6183320998Srmacklem{
6184320998Srmacklem	uint32_t *tl;
6185320998Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
6186320998Srmacklem	struct nfscldeleg *ndp = NULL;
6187320998Srmacklem	struct nfsvattr nfsva;
6188320998Srmacklem	struct nfsclsession *tsep;
6189320998Srmacklem	uint32_t rflags, deleg;
6190320998Srmacklem	nfsattrbit_t attrbits;
6191320998Srmacklem	int error, ret, acesize, limitby, iomode;
6192320998Srmacklem
6193320998Srmacklem	*dpp = NULL;
6194320998Srmacklem	*laystatp = ENXIO;
6195320998Srmacklem	nfscl_reqstart(nd, NFSPROC_OPENLAYGET, nmp, nfhp, fhlen, NULL, NULL);
6196320998Srmacklem	NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
6197320998Srmacklem	*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
6198320998Srmacklem	*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
6199320998Srmacklem	*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
6200320998Srmacklem	tsep = nfsmnt_mdssession(nmp);
6201320998Srmacklem	*tl++ = tsep->nfsess_clientid.lval[0];
6202320998Srmacklem	*tl = tsep->nfsess_clientid.lval[1];
6203320998Srmacklem	nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
6204320998Srmacklem	NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
6205320998Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
6206320998Srmacklem	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
6207320998Srmacklem	nfsm_strtom(nd, name, namelen);
6208320998Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
6209320998Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
6210320998Srmacklem	NFSZERO_ATTRBIT(&attrbits);
6211320998Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
6212320998Srmacklem	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFY);
6213320998Srmacklem	nfsrv_putattrbit(nd, &attrbits);
6214320998Srmacklem	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
6215320998Srmacklem	*tl = txdr_unsigned(NFSV4OP_LAYOUTGET);
6216320998Srmacklem	if ((mode & NFSV4OPEN_ACCESSWRITE) != 0)
6217320998Srmacklem		iomode = NFSLAYOUTIOMODE_RW;
6218320998Srmacklem	else
6219320998Srmacklem		iomode = NFSLAYOUTIOMODE_READ;
6220320998Srmacklem	nfsrv_setuplayoutget(nd, iomode, 0, UINT64_MAX, 0, stateidp,
6221320998Srmacklem	    layoutlen, usecurstateid);
6222320998Srmacklem	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
6223320998Srmacklem	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
6224320998Srmacklem	if (error != 0)
6225320998Srmacklem		return (error);
6226320998Srmacklem	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
6227320998Srmacklem	if (nd->nd_repstat != 0)
6228320998Srmacklem		*laystatp = nd->nd_repstat;
6229320998Srmacklem	if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
6230320998Srmacklem		/* ND_NOMOREDATA will be set if the Open operation failed. */
6231320998Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
6232320998Srmacklem		    6 * NFSX_UNSIGNED);
6233320998Srmacklem		op->nfso_stateid.seqid = *tl++;
6234320998Srmacklem		op->nfso_stateid.other[0] = *tl++;
6235320998Srmacklem		op->nfso_stateid.other[1] = *tl++;
6236320998Srmacklem		op->nfso_stateid.other[2] = *tl;
6237320998Srmacklem		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
6238320998Srmacklem		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
6239320998Srmacklem		if (error != 0)
6240320998Srmacklem			goto nfsmout;
6241320998Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
6242320998Srmacklem		deleg = fxdr_unsigned(u_int32_t, *tl);
6243320998Srmacklem		if (deleg == NFSV4OPEN_DELEGATEREAD ||
6244320998Srmacklem		    deleg == NFSV4OPEN_DELEGATEWRITE) {
6245320998Srmacklem			if (!(op->nfso_own->nfsow_clp->nfsc_flags &
6246320998Srmacklem			      NFSCLFLAGS_FIRSTDELEG))
6247320998Srmacklem				op->nfso_own->nfsow_clp->nfsc_flags |=
6248320998Srmacklem				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
6249320998Srmacklem			ndp = malloc(sizeof(struct nfscldeleg) + newfhlen,
6250320998Srmacklem			    M_NFSCLDELEG, M_WAITOK);
6251320998Srmacklem			LIST_INIT(&ndp->nfsdl_owner);
6252320998Srmacklem			LIST_INIT(&ndp->nfsdl_lock);
6253320998Srmacklem			ndp->nfsdl_clp = op->nfso_own->nfsow_clp;
6254320998Srmacklem			ndp->nfsdl_fhlen = newfhlen;
6255320998Srmacklem			NFSBCOPY(newfhp, ndp->nfsdl_fh, newfhlen);
6256320998Srmacklem			newnfs_copyincred(cred, &ndp->nfsdl_cred);
6257320998Srmacklem			nfscl_lockinit(&ndp->nfsdl_rwlock);
6258320998Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
6259320998Srmacklem			    NFSX_UNSIGNED);
6260320998Srmacklem			ndp->nfsdl_stateid.seqid = *tl++;
6261320998Srmacklem			ndp->nfsdl_stateid.other[0] = *tl++;
6262320998Srmacklem			ndp->nfsdl_stateid.other[1] = *tl++;
6263320998Srmacklem			ndp->nfsdl_stateid.other[2] = *tl++;
6264320998Srmacklem			ret = fxdr_unsigned(int, *tl);
6265320998Srmacklem			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
6266320998Srmacklem				ndp->nfsdl_flags = NFSCLDL_WRITE;
6267320998Srmacklem				/*
6268320998Srmacklem				 * Indicates how much the file can grow.
6269320998Srmacklem				 */
6270320998Srmacklem				NFSM_DISSECT(tl, u_int32_t *,
6271320998Srmacklem				    3 * NFSX_UNSIGNED);
6272320998Srmacklem				limitby = fxdr_unsigned(int, *tl++);
6273320998Srmacklem				switch (limitby) {
6274320998Srmacklem				case NFSV4OPEN_LIMITSIZE:
6275320998Srmacklem					ndp->nfsdl_sizelimit = fxdr_hyper(tl);
6276320998Srmacklem					break;
6277320998Srmacklem				case NFSV4OPEN_LIMITBLOCKS:
6278320998Srmacklem					ndp->nfsdl_sizelimit =
6279320998Srmacklem					    fxdr_unsigned(u_int64_t, *tl++);
6280320998Srmacklem					ndp->nfsdl_sizelimit *=
6281320998Srmacklem					    fxdr_unsigned(u_int64_t, *tl);
6282320998Srmacklem					break;
6283320998Srmacklem				default:
6284320998Srmacklem					error = NFSERR_BADXDR;
6285320998Srmacklem					goto nfsmout;
6286320998Srmacklem				};
6287320998Srmacklem			} else
6288320998Srmacklem				ndp->nfsdl_flags = NFSCLDL_READ;
6289320998Srmacklem			if (ret != 0)
6290320998Srmacklem				ndp->nfsdl_flags |= NFSCLDL_RECALL;
6291320998Srmacklem			error = nfsrv_dissectace(nd, &ndp->nfsdl_ace, &ret,
6292320998Srmacklem			    &acesize, p);
6293320998Srmacklem			if (error != 0)
6294320998Srmacklem				goto nfsmout;
6295320998Srmacklem		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
6296320998Srmacklem			error = NFSERR_BADXDR;
6297320998Srmacklem			goto nfsmout;
6298320998Srmacklem		}
6299320998Srmacklem		if ((rflags & NFSV4OPEN_LOCKTYPEPOSIX) != 0 ||
6300320998Srmacklem		    nfscl_assumeposixlocks)
6301320998Srmacklem			op->nfso_posixlock = 1;
6302320998Srmacklem		else
6303320998Srmacklem			op->nfso_posixlock = 0;
6304320998Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
6305320998Srmacklem		/* If the 2nd element == NFS_OK, the Getattr succeeded. */
6306320998Srmacklem		if (*++tl == 0) {
6307320998Srmacklem			error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
6308320998Srmacklem			    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
6309320998Srmacklem			    NULL, NULL, NULL, p, cred);
6310320998Srmacklem			if (error != 0)
6311320998Srmacklem				goto nfsmout;
6312320998Srmacklem			if (ndp != NULL) {
6313320998Srmacklem				ndp->nfsdl_change = nfsva.na_filerev;
6314320998Srmacklem				ndp->nfsdl_modtime = nfsva.na_mtime;
6315320998Srmacklem				ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
6316320998Srmacklem				*dpp = ndp;
6317320998Srmacklem				ndp = NULL;
6318320998Srmacklem			}
6319320998Srmacklem			/*
6320320998Srmacklem			 * At this point, the Open has succeeded, so set
6321320998Srmacklem			 * nd_repstat = NFS_OK.  If the Layoutget failed,
6322320998Srmacklem			 * this function just won't return a layout.
6323320998Srmacklem			 */
6324320998Srmacklem			if (nd->nd_repstat == 0) {
6325320998Srmacklem				NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
6326320998Srmacklem				*laystatp = fxdr_unsigned(int, *++tl);
6327320998Srmacklem				if (*laystatp == 0) {
6328320998Srmacklem					error = nfsrv_parselayoutget(nd,
6329320998Srmacklem					    stateidp, retonclosep, flhp);
6330320998Srmacklem					if (error != 0)
6331320998Srmacklem						*laystatp = error;
6332320998Srmacklem				}
6333320998Srmacklem			} else
6334320998Srmacklem				nd->nd_repstat = 0;	/* Return 0 for Open. */
6335320998Srmacklem		}
6336320998Srmacklem	}
6337320998Srmacklem	if (nd->nd_repstat != 0 && error == 0)
6338320998Srmacklem		error = nd->nd_repstat;
6339320998Srmacklemnfsmout:
6340320998Srmacklem	free(ndp, M_NFSCLDELEG);
6341320998Srmacklem	mbuf_freem(nd->nd_mrep);
6342320998Srmacklem	return (error);
6343320998Srmacklem}
6344320998Srmacklem
6345320998Srmacklem/*
6346320998Srmacklem * Similar nfsrpc_createv4(), but also does the LayoutGet operation.
6347320998Srmacklem * Used only for mounts with pNFS enabled.
6348320998Srmacklem */
6349320998Srmacklemstatic int
6350320998Srmacklemnfsrpc_createlayout(vnode_t dvp, char *name, int namelen, struct vattr *vap,
6351320998Srmacklem    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
6352320998Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
6353320998Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
6354320998Srmacklem    int *dattrflagp, void *dstuff, int *unlockedp, nfsv4stateid_t *stateidp,
6355320998Srmacklem    int usecurstateid, int layoutlen, int *retonclosep,
6356320998Srmacklem    struct nfsclflayouthead *flhp, int *laystatp)
6357320998Srmacklem{
6358320998Srmacklem	uint32_t *tl;
6359320998Srmacklem	int error = 0, deleg, newone, ret, acesize, limitby;
6360320998Srmacklem	struct nfsrv_descript nfsd, *nd = &nfsd;
6361320998Srmacklem	struct nfsclopen *op;
6362320998Srmacklem	struct nfscldeleg *dp = NULL;
6363320998Srmacklem	struct nfsnode *np;
6364320998Srmacklem	struct nfsfh *nfhp;
6365320998Srmacklem	struct nfsclsession *tsep;
6366320998Srmacklem	nfsattrbit_t attrbits;
6367320998Srmacklem	nfsv4stateid_t stateid;
6368320998Srmacklem	uint32_t rflags;
6369320998Srmacklem	struct nfsmount *nmp;
6370320998Srmacklem
6371320998Srmacklem	nmp = VFSTONFS(dvp->v_mount);
6372320998Srmacklem	np = VTONFS(dvp);
6373320998Srmacklem	*laystatp = ENXIO;
6374320998Srmacklem	*unlockedp = 0;
6375320998Srmacklem	*nfhpp = NULL;
6376320998Srmacklem	*dpp = NULL;
6377320998Srmacklem	*attrflagp = 0;
6378320998Srmacklem	*dattrflagp = 0;
6379320998Srmacklem	if (namelen > NFS_MAXNAMLEN)
6380320998Srmacklem		return (ENAMETOOLONG);
6381320998Srmacklem	NFSCL_REQSTART(nd, NFSPROC_CREATELAYGET, dvp);
6382320998Srmacklem	/*
6383320998Srmacklem	 * For V4, this is actually an Open op.
6384320998Srmacklem	 */
6385320998Srmacklem	NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
6386320998Srmacklem	*tl++ = txdr_unsigned(owp->nfsow_seqid);
6387320998Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
6388320998Srmacklem	    NFSV4OPEN_ACCESSREAD);
6389320998Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
6390320998Srmacklem	tsep = nfsmnt_mdssession(nmp);
6391320998Srmacklem	*tl++ = tsep->nfsess_clientid.lval[0];
6392320998Srmacklem	*tl = tsep->nfsess_clientid.lval[1];
6393320998Srmacklem	nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
6394320998Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
6395320998Srmacklem	*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
6396320998Srmacklem	if ((fmode & O_EXCL) != 0) {
6397320998Srmacklem		if (NFSHASSESSPERSIST(nmp)) {
6398320998Srmacklem			/* Use GUARDED for persistent sessions. */
6399320998Srmacklem			*tl = txdr_unsigned(NFSCREATE_GUARDED);
6400320998Srmacklem			nfscl_fillsattr(nd, vap, dvp, 0, 0);
6401320998Srmacklem		} else {
6402320998Srmacklem			/* Otherwise, use EXCLUSIVE4_1. */
6403320998Srmacklem			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE41);
6404320998Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
6405320998Srmacklem			*tl++ = cverf.lval[0];
6406320998Srmacklem			*tl = cverf.lval[1];
6407320998Srmacklem			nfscl_fillsattr(nd, vap, dvp, 0, 0);
6408320998Srmacklem		}
6409320998Srmacklem	} else {
6410320998Srmacklem		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
6411320998Srmacklem		nfscl_fillsattr(nd, vap, dvp, 0, 0);
6412320998Srmacklem	}
6413320998Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
6414320998Srmacklem	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
6415320998Srmacklem	nfsm_strtom(nd, name, namelen);
6416320998Srmacklem	/* Get the new file's handle and attributes, plus save the FH. */
6417320998Srmacklem	NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
6418320998Srmacklem	*tl++ = txdr_unsigned(NFSV4OP_SAVEFH);
6419320998Srmacklem	*tl++ = txdr_unsigned(NFSV4OP_GETFH);
6420320998Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
6421320998Srmacklem	NFSGETATTR_ATTRBIT(&attrbits);
6422320998Srmacklem	nfsrv_putattrbit(nd, &attrbits);
6423320998Srmacklem	/* Get the directory's post-op attributes. */
6424320998Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
6425320998Srmacklem	*tl = txdr_unsigned(NFSV4OP_PUTFH);
6426320998Srmacklem	nfsm_fhtom(nd, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, 0);
6427320998Srmacklem	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
6428320998Srmacklem	*tl = txdr_unsigned(NFSV4OP_GETATTR);
6429320998Srmacklem	nfsrv_putattrbit(nd, &attrbits);
6430320998Srmacklem	NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
6431320998Srmacklem	*tl++ = txdr_unsigned(NFSV4OP_RESTOREFH);
6432320998Srmacklem	*tl = txdr_unsigned(NFSV4OP_LAYOUTGET);
6433320998Srmacklem	nfsrv_setuplayoutget(nd, NFSLAYOUTIOMODE_RW, 0, UINT64_MAX, 0, stateidp,
6434320998Srmacklem	    layoutlen, usecurstateid);
6435320998Srmacklem	error = nfscl_request(nd, dvp, p, cred, dstuff);
6436320998Srmacklem	if (error != 0)
6437320998Srmacklem		return (error);
6438320998Srmacklem	NFSCL_DEBUG(4, "nfsrpc_createlayout stat=%d err=%d\n", nd->nd_repstat,
6439320998Srmacklem	    error);
6440320998Srmacklem	if (nd->nd_repstat != 0)
6441320998Srmacklem		*laystatp = nd->nd_repstat;
6442320998Srmacklem	NFSCL_INCRSEQID(owp->nfsow_seqid, nd);
6443320998Srmacklem	if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
6444320998Srmacklem		NFSCL_DEBUG(4, "nfsrpc_createlayout open succeeded\n");
6445320998Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
6446320998Srmacklem		    6 * NFSX_UNSIGNED);
6447320998Srmacklem		stateid.seqid = *tl++;
6448320998Srmacklem		stateid.other[0] = *tl++;
6449320998Srmacklem		stateid.other[1] = *tl++;
6450320998Srmacklem		stateid.other[2] = *tl;
6451320998Srmacklem		rflags = fxdr_unsigned(u_int32_t, *(tl + 6));
6452320998Srmacklem		nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
6453320998Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
6454320998Srmacklem		deleg = fxdr_unsigned(int, *tl);
6455320998Srmacklem		if (deleg == NFSV4OPEN_DELEGATEREAD ||
6456320998Srmacklem		    deleg == NFSV4OPEN_DELEGATEWRITE) {
6457320998Srmacklem			if (!(owp->nfsow_clp->nfsc_flags &
6458320998Srmacklem			      NFSCLFLAGS_FIRSTDELEG))
6459320998Srmacklem				owp->nfsow_clp->nfsc_flags |=
6460320998Srmacklem				  (NFSCLFLAGS_FIRSTDELEG | NFSCLFLAGS_GOTDELEG);
6461320998Srmacklem			dp = malloc(sizeof(struct nfscldeleg) + NFSX_V4FHMAX,
6462320998Srmacklem			    M_NFSCLDELEG, M_WAITOK);
6463320998Srmacklem			LIST_INIT(&dp->nfsdl_owner);
6464320998Srmacklem			LIST_INIT(&dp->nfsdl_lock);
6465320998Srmacklem			dp->nfsdl_clp = owp->nfsow_clp;
6466320998Srmacklem			newnfs_copyincred(cred, &dp->nfsdl_cred);
6467320998Srmacklem			nfscl_lockinit(&dp->nfsdl_rwlock);
6468320998Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
6469320998Srmacklem			    NFSX_UNSIGNED);
6470320998Srmacklem			dp->nfsdl_stateid.seqid = *tl++;
6471320998Srmacklem			dp->nfsdl_stateid.other[0] = *tl++;
6472320998Srmacklem			dp->nfsdl_stateid.other[1] = *tl++;
6473320998Srmacklem			dp->nfsdl_stateid.other[2] = *tl++;
6474320998Srmacklem			ret = fxdr_unsigned(int, *tl);
6475320998Srmacklem			if (deleg == NFSV4OPEN_DELEGATEWRITE) {
6476320998Srmacklem				dp->nfsdl_flags = NFSCLDL_WRITE;
6477320998Srmacklem				/*
6478320998Srmacklem				 * Indicates how much the file can grow.
6479320998Srmacklem				 */
6480320998Srmacklem				NFSM_DISSECT(tl, u_int32_t *,
6481320998Srmacklem				    3 * NFSX_UNSIGNED);
6482320998Srmacklem				limitby = fxdr_unsigned(int, *tl++);
6483320998Srmacklem				switch (limitby) {
6484320998Srmacklem				case NFSV4OPEN_LIMITSIZE:
6485320998Srmacklem					dp->nfsdl_sizelimit = fxdr_hyper(tl);
6486320998Srmacklem					break;
6487320998Srmacklem				case NFSV4OPEN_LIMITBLOCKS:
6488320998Srmacklem					dp->nfsdl_sizelimit =
6489320998Srmacklem					    fxdr_unsigned(u_int64_t, *tl++);
6490320998Srmacklem					dp->nfsdl_sizelimit *=
6491320998Srmacklem					    fxdr_unsigned(u_int64_t, *tl);
6492320998Srmacklem					break;
6493320998Srmacklem				default:
6494320998Srmacklem					error = NFSERR_BADXDR;
6495320998Srmacklem					goto nfsmout;
6496320998Srmacklem				};
6497320998Srmacklem			} else {
6498320998Srmacklem				dp->nfsdl_flags = NFSCLDL_READ;
6499320998Srmacklem			}
6500320998Srmacklem			if (ret != 0)
6501320998Srmacklem				dp->nfsdl_flags |= NFSCLDL_RECALL;
6502320998Srmacklem			error = nfsrv_dissectace(nd, &dp->nfsdl_ace, &ret,
6503320998Srmacklem			    &acesize, p);
6504320998Srmacklem			if (error != 0)
6505320998Srmacklem				goto nfsmout;
6506320998Srmacklem		} else if (deleg != NFSV4OPEN_DELEGATENONE) {
6507320998Srmacklem			error = NFSERR_BADXDR;
6508320998Srmacklem			goto nfsmout;
6509320998Srmacklem		}
6510320998Srmacklem
6511320998Srmacklem		/* Now, we should have the status for the SaveFH. */
6512320998Srmacklem		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
6513320998Srmacklem		if (*++tl == 0) {
6514320998Srmacklem			NFSCL_DEBUG(4, "nfsrpc_createlayout SaveFH ok\n");
6515320998Srmacklem			/*
6516320998Srmacklem			 * Now, process the GetFH and Getattr for the newly
6517320998Srmacklem			 * created file. nfscl_mtofh() will set
6518320998Srmacklem			 * ND_NOMOREDATA if these weren't successful.
6519320998Srmacklem			 */
6520320998Srmacklem			error = nfscl_mtofh(nd, nfhpp, nnap, attrflagp);
6521320998Srmacklem			NFSCL_DEBUG(4, "aft nfscl_mtofh err=%d\n", error);
6522320998Srmacklem			if (error != 0)
6523320998Srmacklem				goto nfsmout;
6524320998Srmacklem		} else
6525320998Srmacklem			nd->nd_flag |= ND_NOMOREDATA;
6526320998Srmacklem		/* Now we have the PutFH and Getattr for the directory. */
6527320998Srmacklem		if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
6528320998Srmacklem			NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
6529320998Srmacklem			if (*++tl != 0)
6530320998Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
6531320998Srmacklem			else {
6532320998Srmacklem				NFSM_DISSECT(tl, uint32_t *, 2 *
6533320998Srmacklem				    NFSX_UNSIGNED);
6534320998Srmacklem				if (*++tl != 0)
6535320998Srmacklem					nd->nd_flag |= ND_NOMOREDATA;
6536320998Srmacklem			}
6537320998Srmacklem		}
6538320998Srmacklem		if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
6539320998Srmacklem			/* Load the directory attributes. */
6540320998Srmacklem			error = nfsm_loadattr(nd, dnap);
6541320998Srmacklem			NFSCL_DEBUG(4, "aft nfsm_loadattr err=%d\n", error);
6542320998Srmacklem			if (error != 0)
6543320998Srmacklem				goto nfsmout;
6544320998Srmacklem			*dattrflagp = 1;
6545320998Srmacklem			if (dp != NULL && *attrflagp != 0) {
6546320998Srmacklem				dp->nfsdl_change = nnap->na_filerev;
6547320998Srmacklem				dp->nfsdl_modtime = nnap->na_mtime;
6548320998Srmacklem				dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
6549320998Srmacklem			}
6550320998Srmacklem			/*
6551320998Srmacklem			 * We can now complete the Open state.
6552320998Srmacklem			 */
6553320998Srmacklem			nfhp = *nfhpp;
6554320998Srmacklem			if (dp != NULL) {
6555320998Srmacklem				dp->nfsdl_fhlen = nfhp->nfh_len;
6556320998Srmacklem				NFSBCOPY(nfhp->nfh_fh, dp->nfsdl_fh,
6557320998Srmacklem				    nfhp->nfh_len);
6558320998Srmacklem			}
6559320998Srmacklem			/*
6560320998Srmacklem			 * Get an Open structure that will be
6561320998Srmacklem			 * attached to the OpenOwner, acquired already.
6562320998Srmacklem			 */
6563320998Srmacklem			error = nfscl_open(dvp, nfhp->nfh_fh, nfhp->nfh_len,
6564320998Srmacklem			    (NFSV4OPEN_ACCESSWRITE | NFSV4OPEN_ACCESSREAD), 0,
6565320998Srmacklem			    cred, p, NULL, &op, &newone, NULL, 0);
6566320998Srmacklem			if (error != 0)
6567320998Srmacklem				goto nfsmout;
6568320998Srmacklem			op->nfso_stateid = stateid;
6569320998Srmacklem			newnfs_copyincred(cred, &op->nfso_cred);
6570320998Srmacklem
6571320998Srmacklem			nfscl_openrelease(nmp, op, error, newone);
6572320998Srmacklem			*unlockedp = 1;
6573320998Srmacklem
6574320998Srmacklem			/* Now, handle the RestoreFH and LayoutGet. */
6575320998Srmacklem			if (nd->nd_repstat == 0) {
6576320998Srmacklem				NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
6577320998Srmacklem				*laystatp = fxdr_unsigned(int, *(tl + 3));
6578320998Srmacklem				if (*laystatp == 0) {
6579320998Srmacklem					error = nfsrv_parselayoutget(nd,
6580320998Srmacklem					    stateidp, retonclosep, flhp);
6581320998Srmacklem					if (error != 0)
6582320998Srmacklem						*laystatp = error;
6583320998Srmacklem				}
6584320998Srmacklem				NFSCL_DEBUG(4, "aft nfsrv_parselayout err=%d\n",
6585320998Srmacklem				    error);
6586320998Srmacklem			} else
6587320998Srmacklem				nd->nd_repstat = 0;
6588320998Srmacklem		}
6589320998Srmacklem	}
6590320998Srmacklem	if (nd->nd_repstat != 0 && error == 0)
6591320998Srmacklem		error = nd->nd_repstat;
6592320998Srmacklem	if (error == NFSERR_STALECLIENTID || error == NFSERR_BADSESSION)
6593320998Srmacklem		nfscl_initiate_recovery(owp->nfsow_clp);
6594320998Srmacklemnfsmout:
6595320998Srmacklem	NFSCL_DEBUG(4, "eo nfsrpc_createlayout err=%d\n", error);
6596320998Srmacklem	if (error == 0)
6597320998Srmacklem		*dpp = dp;
6598320998Srmacklem	else
6599320998Srmacklem		free(dp, M_NFSCLDELEG);
6600320998Srmacklem	mbuf_freem(nd->nd_mrep);
6601320998Srmacklem	return (error);
6602320998Srmacklem}
6603320998Srmacklem
6604320998Srmacklem/*
6605320998Srmacklem * Similar to nfsrpc_getopenlayout(), except that it used for the Create case.
6606320998Srmacklem */
6607320998Srmacklemstatic int
6608320998Srmacklemnfsrpc_getcreatelayout(vnode_t dvp, char *name, int namelen, struct vattr *vap,
6609320998Srmacklem    nfsquad_t cverf, int fmode, struct nfsclowner *owp, struct nfscldeleg **dpp,
6610320998Srmacklem    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap,
6611320998Srmacklem    struct nfsvattr *nnap, struct nfsfh **nfhpp, int *attrflagp,
6612320998Srmacklem    int *dattrflagp, void *dstuff, int *unlockedp)
6613320998Srmacklem{
6614320998Srmacklem	struct nfscllayout *lyp;
6615320998Srmacklem	struct nfsclflayouthead flh;
6616320998Srmacklem	struct nfsfh *nfhp;
6617320998Srmacklem	struct nfsclsession *tsep;
6618320998Srmacklem	struct nfsmount *nmp;
6619320998Srmacklem	nfsv4stateid_t stateid;
6620320998Srmacklem	int error, layoutlen, retonclose, laystat;
6621320998Srmacklem
6622320998Srmacklem	error = 0;
6623320998Srmacklem	nmp = VFSTONFS(dvp->v_mount);
6624320998Srmacklem	LIST_INIT(&flh);
6625320998Srmacklem	tsep = nfsmnt_mdssession(nmp);
6626320998Srmacklem	layoutlen = tsep->nfsess_maxcache - (NFSX_STATEID + 3 * NFSX_UNSIGNED);
6627320998Srmacklem	error = nfsrpc_createlayout(dvp, name, namelen, vap, cverf, fmode,
6628320998Srmacklem	    owp, dpp, cred, p, dnap, nnap, nfhpp, attrflagp, dattrflagp,
6629320998Srmacklem	    dstuff, unlockedp, &stateid, 1, layoutlen, &retonclose, &flh,
6630320998Srmacklem	    &laystat);
6631320998Srmacklem	NFSCL_DEBUG(4, "aft nfsrpc_createlayoutrpc laystat=%d err=%d\n",
6632320998Srmacklem	    laystat, error);
6633320998Srmacklem	lyp = NULL;
6634322908Srmacklem	if (laystat == 0) {
6635322908Srmacklem		nfhp = *nfhpp;
6636322908Srmacklem		laystat = nfsrpc_layoutgetres(nmp, dvp, nfhp->nfh_fh,
6637322908Srmacklem		    nfhp->nfh_len, &stateid, retonclose, NULL, &lyp, &flh,
6638322908Srmacklem		    laystat, NULL, cred, p);
6639322908Srmacklem	} else
6640322908Srmacklem		laystat = nfsrpc_layoutgetres(nmp, dvp, NULL, 0, &stateid,
6641322908Srmacklem		    retonclose, NULL, &lyp, &flh, laystat, NULL, cred, p);
6642320998Srmacklem	if (laystat == 0)
6643320998Srmacklem		nfscl_rellayout(lyp, 0);
6644320998Srmacklem	return (error);
6645320998Srmacklem}
6646320998Srmacklem
6647320998Srmacklem/*
6648320998Srmacklem * Process the results of a layoutget() operation.
6649320998Srmacklem */
6650320998Srmacklemstatic int
6651320998Srmacklemnfsrpc_layoutgetres(struct nfsmount *nmp, vnode_t vp, uint8_t *newfhp,
6652320998Srmacklem    int newfhlen, nfsv4stateid_t *stateidp, int retonclose, uint32_t *notifybit,
6653320998Srmacklem    struct nfscllayout **lypp, struct nfsclflayouthead *flhp,
6654320998Srmacklem    int laystat, int *islockedp, struct ucred *cred, NFSPROC_T *p)
6655320998Srmacklem{
6656320998Srmacklem	struct nfsclflayout *tflp;
6657320998Srmacklem	struct nfscldevinfo *dip;
6658320998Srmacklem
6659320998Srmacklem	if (laystat == NFSERR_UNKNLAYOUTTYPE) {
6660320998Srmacklem		/* Disable PNFS. */
6661320998Srmacklem		NFSCL_DEBUG(1, "disable PNFS\n");
6662320998Srmacklem		NFSLOCKMNT(nmp);
6663320998Srmacklem		nmp->nm_state &= ~NFSSTA_PNFS;
6664320998Srmacklem		NFSUNLOCKMNT(nmp);
6665320998Srmacklem	}
6666320998Srmacklem	if (laystat == 0) {
6667320998Srmacklem		NFSCL_DEBUG(4, "nfsrpc_layoutgetres at FOREACH\n");
6668320998Srmacklem		LIST_FOREACH(tflp, flhp, nfsfl_list) {
6669320998Srmacklem			laystat = nfscl_adddevinfo(nmp, NULL, tflp);
6670320998Srmacklem			NFSCL_DEBUG(4, "aft adddev=%d\n", laystat);
6671320998Srmacklem			if (laystat != 0) {
6672320998Srmacklem				laystat = nfsrpc_getdeviceinfo(nmp,
6673320998Srmacklem				    tflp->nfsfl_dev, NFSLAYOUT_NFSV4_1_FILES,
6674320998Srmacklem				    notifybit, &dip, cred, p);
6675320998Srmacklem				NFSCL_DEBUG(4, "aft nfsrpc_gdi=%d\n",
6676320998Srmacklem				    laystat);
6677320998Srmacklem				if (laystat != 0)
6678320998Srmacklem					break;
6679320998Srmacklem				laystat = nfscl_adddevinfo(nmp, dip, tflp);
6680320998Srmacklem				if (laystat != 0)
6681320998Srmacklem					printf("getlayout: cannot add\n");
6682320998Srmacklem			}
6683320998Srmacklem		}
6684320998Srmacklem	}
6685320998Srmacklem	if (laystat == 0) {
6686320998Srmacklem		/*
6687320998Srmacklem		 * nfscl_layout() always returns with the nfsly_lock
6688320998Srmacklem		 * set to a refcnt (shared lock).
6689320998Srmacklem		 * Passing in dvp is sufficient, since it is only used to
6690320998Srmacklem		 * get the fsid for the file system.
6691320998Srmacklem		 */
6692320998Srmacklem		laystat = nfscl_layout(nmp, vp, newfhp, newfhlen, stateidp,
6693320998Srmacklem		    retonclose, flhp, lypp, cred, p);
6694320998Srmacklem		NFSCL_DEBUG(4, "nfsrpc_layoutgetres: aft nfscl_layout=%d\n",
6695320998Srmacklem		    laystat);
6696320998Srmacklem		if (laystat == 0 && islockedp != NULL)
6697320998Srmacklem			*islockedp = 1;
6698320998Srmacklem	}
6699320998Srmacklem	return (laystat);
6700320998Srmacklem}
6701320998Srmacklem
6702