nfs_clcomsubs.c revision 317925
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: stable/10/sys/fs/nfsclient/nfs_clcomsubs.c 317925 2017-05-07 21:32:55Z rmacklem $");
36
37/*
38 * These functions support the macros and help fiddle mbuf chains for
39 * the nfs op functions. They do things like create the rpc header and
40 * copy data between mbuf chains and uio lists.
41 */
42#ifndef APPLEKEXT
43#include <fs/nfs/nfsport.h>
44
45extern struct nfsstats newnfsstats;
46extern struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS];
47extern int ncl_mbuf_mlen;
48extern enum vtype newnv2tov_type[8];
49extern enum vtype nv34tov_type[8];
50extern int	nfs_bigreply[NFSV41_NPROCS];
51NFSCLSTATEMUTEX;
52#endif	/* !APPLEKEXT */
53
54static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
55static struct {
56	int	op;
57	int	opcnt;
58	const u_char *tag;
59	int	taglen;
60} nfsv4_opmap[NFSV41_NPROCS] = {
61	{ 0, 1, "Null", 4 },
62	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
63	{ NFSV4OP_SETATTR, 2, "Setattr", 7, },
64	{ NFSV4OP_LOOKUP, 3, "Lookup", 6, },
65	{ NFSV4OP_ACCESS, 2, "Access", 6, },
66	{ NFSV4OP_READLINK, 2, "Readlink", 8, },
67	{ NFSV4OP_READ, 1, "Read", 4, },
68	{ NFSV4OP_WRITE, 2, "Write", 5, },
69	{ NFSV4OP_OPEN, 5, "Open", 4, },
70	{ NFSV4OP_CREATE, 5, "Create", 6, },
71	{ NFSV4OP_CREATE, 1, "Create", 6, },
72	{ NFSV4OP_CREATE, 3, "Create", 6, },
73	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
74	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
75	{ NFSV4OP_SAVEFH, 5, "Rename", 6, },
76	{ NFSV4OP_SAVEFH, 4, "Link", 4, },
77	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
78	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
79	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
80	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
81	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
82	{ NFSV4OP_COMMIT, 2, "Commit", 6, },
83	{ NFSV4OP_LOOKUPP, 3, "Lookupp", 7, },
84	{ NFSV4OP_SETCLIENTID, 1, "SetClientID", 11, },
85	{ NFSV4OP_SETCLIENTIDCFRM, 1, "SetClientIDConfirm", 18, },
86	{ NFSV4OP_LOCK, 1, "Lock", 4, },
87	{ NFSV4OP_LOCKU, 1, "LockU", 5, },
88	{ NFSV4OP_OPEN, 2, "Open", 4, },
89	{ NFSV4OP_CLOSE, 1, "Close", 5, },
90	{ NFSV4OP_OPENCONFIRM, 1, "Openconfirm", 11, },
91	{ NFSV4OP_LOCKT, 1, "LockT", 5, },
92	{ NFSV4OP_OPENDOWNGRADE, 1, "Opendowngrade", 13, },
93	{ NFSV4OP_RENEW, 1, "Renew", 5, },
94	{ NFSV4OP_PUTROOTFH, 1, "Dirpath", 7, },
95	{ NFSV4OP_RELEASELCKOWN, 1, "Rellckown", 9, },
96	{ NFSV4OP_DELEGRETURN, 1, "Delegret", 8, },
97	{ NFSV4OP_DELEGRETURN, 3, "DelegRemove", 11, },
98	{ NFSV4OP_DELEGRETURN, 7, "DelegRename1", 12, },
99	{ NFSV4OP_DELEGRETURN, 9, "DelegRename2", 12, },
100	{ NFSV4OP_GETATTR, 1, "Getacl", 6, },
101	{ NFSV4OP_SETATTR, 1, "Setacl", 6, },
102	{ NFSV4OP_EXCHANGEID, 1, "ExchangeID", 10, },
103	{ NFSV4OP_CREATESESSION, 1, "CreateSession", 13, },
104	{ NFSV4OP_DESTROYSESSION, 1, "DestroySession", 14, },
105	{ NFSV4OP_DESTROYCLIENTID, 1, "DestroyClient", 13, },
106	{ NFSV4OP_FREESTATEID, 1, "FreeStateID", 11, },
107	{ NFSV4OP_LAYOUTGET, 1, "LayoutGet", 9, },
108	{ NFSV4OP_GETDEVINFO, 1, "GetDeviceInfo", 13, },
109	{ NFSV4OP_LAYOUTCOMMIT, 1, "LayoutCommit", 12, },
110	{ NFSV4OP_LAYOUTRETURN, 1, "LayoutReturn", 12, },
111	{ NFSV4OP_RECLAIMCOMPL, 1, "ReclaimComplete", 15, },
112	{ NFSV4OP_WRITE, 1, "WriteDS", 7, },
113	{ NFSV4OP_READ, 1, "ReadDS", 6, },
114	{ NFSV4OP_COMMIT, 1, "CommitDS", 8, },
115};
116
117/*
118 * NFS RPCS that have large request message size.
119 */
120static int nfs_bigrequest[NFSV41_NPROCS] = {
121	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123	0, 0, 0, 0, 0, 0, 1, 0, 0
124};
125
126/*
127 * Start building a request. Mostly just put the first file handle in
128 * place.
129 */
130APPLESTATIC void
131nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
132    u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp, struct nfsclsession *sep)
133{
134	struct mbuf *mb;
135	u_int32_t *tl;
136	int opcnt;
137	nfsattrbit_t attrbits;
138
139	/*
140	 * First, fill in some of the fields of nd.
141	 */
142	nd->nd_slotseq = NULL;
143	if (NFSHASNFSV4(nmp)) {
144		nd->nd_flag = ND_NFSV4 | ND_NFSCL;
145		if (NFSHASNFSV4N(nmp))
146			nd->nd_flag |= ND_NFSV41;
147	} else if (NFSHASNFSV3(nmp))
148		nd->nd_flag = ND_NFSV3 | ND_NFSCL;
149	else
150		nd->nd_flag = ND_NFSV2 | ND_NFSCL;
151	nd->nd_procnum = procnum;
152	nd->nd_repstat = 0;
153
154	/*
155	 * Get the first mbuf for the request.
156	 */
157	if (nfs_bigrequest[procnum])
158		NFSMCLGET(mb, M_WAITOK);
159	else
160		NFSMGET(mb);
161	mbuf_setlen(mb, 0);
162	nd->nd_mreq = nd->nd_mb = mb;
163	nd->nd_bpos = NFSMTOD(mb, caddr_t);
164
165	/*
166	 * And fill the first file handle into the request.
167	 */
168	if (nd->nd_flag & ND_NFSV4) {
169		opcnt = nfsv4_opmap[procnum].opcnt +
170		    nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
171		if ((nd->nd_flag & ND_NFSV41) != 0) {
172			opcnt += nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq;
173			if (procnum == NFSPROC_RENEW)
174				/*
175				 * For the special case of Renew, just do a
176				 * Sequence Op.
177				 */
178				opcnt = 1;
179			else if (procnum == NFSPROC_WRITEDS ||
180			    procnum == NFSPROC_COMMITDS)
181				/*
182				 * For the special case of a Writeor Commit to
183				 * a DS, the opcnt == 3, for Sequence, PutFH,
184				 * Write/Commit.
185				 */
186				opcnt = 3;
187		}
188		/*
189		 * What should the tag really be?
190		 */
191		(void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
192			nfsv4_opmap[procnum].taglen);
193		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
194		if ((nd->nd_flag & ND_NFSV41) != 0)
195			*tl++ = txdr_unsigned(NFSV41_MINORVERSION);
196		else
197			*tl++ = txdr_unsigned(NFSV4_MINORVERSION);
198		if (opcntpp != NULL)
199			*opcntpp = tl;
200		*tl = txdr_unsigned(opcnt);
201		if ((nd->nd_flag & ND_NFSV41) != 0 &&
202		    nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq > 0) {
203			if (nfsv4_opflag[nfsv4_opmap[procnum].op].loopbadsess >
204			    0)
205				nd->nd_flag |= ND_LOOPBADSESS;
206			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
207			*tl = txdr_unsigned(NFSV4OP_SEQUENCE);
208			if (sep == NULL) {
209				sep = nfsmnt_mdssession(nmp);
210				nfsv4_setsequence(nmp, nd, sep,
211				    nfs_bigreply[procnum]);
212			} else
213				nfsv4_setsequence(nmp, nd, sep,
214				    nfs_bigreply[procnum]);
215		}
216		if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
217			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
218			*tl = txdr_unsigned(NFSV4OP_PUTFH);
219			(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
220			if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh
221			    == 2 && procnum != NFSPROC_WRITEDS &&
222			    procnum != NFSPROC_COMMITDS) {
223				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
224				*tl = txdr_unsigned(NFSV4OP_GETATTR);
225				/*
226				 * For Lookup Ops, we want all the directory
227				 * attributes, so we can load the name cache.
228				 */
229				if (procnum == NFSPROC_LOOKUP ||
230				    procnum == NFSPROC_LOOKUPP)
231					NFSGETATTR_ATTRBIT(&attrbits);
232				else {
233					NFSWCCATTR_ATTRBIT(&attrbits);
234					nd->nd_flag |= ND_V4WCCATTR;
235				}
236				(void) nfsrv_putattrbit(nd, &attrbits);
237			}
238		}
239		if (procnum != NFSPROC_RENEW ||
240		    (nd->nd_flag & ND_NFSV41) == 0) {
241			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
242			*tl = txdr_unsigned(nfsv4_opmap[procnum].op);
243		}
244	} else {
245		(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
246	}
247	if (procnum < NFSV4_NPROCS)
248		NFSINCRGLOBAL(newnfsstats.rpccnt[procnum]);
249}
250
251#ifndef APPLE
252/*
253 * copies a uio scatter/gather list to an mbuf chain.
254 * NOTE: can ony handle iovcnt == 1
255 */
256APPLESTATIC void
257nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
258{
259	char *uiocp;
260	struct mbuf *mp, *mp2;
261	int xfer, left, mlen;
262	int uiosiz, clflg, rem;
263	char *cp, *tcp;
264
265	KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
266
267	if (siz > ncl_mbuf_mlen)	/* or should it >= MCLBYTES ?? */
268		clflg = 1;
269	else
270		clflg = 0;
271	rem = NFSM_RNDUP(siz) - siz;
272	mp = mp2 = nd->nd_mb;
273	while (siz > 0) {
274		left = uiop->uio_iov->iov_len;
275		uiocp = uiop->uio_iov->iov_base;
276		if (left > siz)
277			left = siz;
278		uiosiz = left;
279		while (left > 0) {
280			mlen = M_TRAILINGSPACE(mp);
281			if (mlen == 0) {
282				if (clflg)
283					NFSMCLGET(mp, M_WAITOK);
284				else
285					NFSMGET(mp);
286				mbuf_setlen(mp, 0);
287				mbuf_setnext(mp2, mp);
288				mp2 = mp;
289				mlen = M_TRAILINGSPACE(mp);
290			}
291			xfer = (left > mlen) ? mlen : left;
292#ifdef notdef
293			/* Not Yet.. */
294			if (uiop->uio_iov->iov_op != NULL)
295				(*(uiop->uio_iov->iov_op))
296				(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
297				    xfer);
298			else
299#endif
300			if (uiop->uio_segflg == UIO_SYSSPACE)
301			    NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
302				xfer);
303			else
304			    copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
305				+ mbuf_len(mp), xfer);
306			mbuf_setlen(mp, mbuf_len(mp) + xfer);
307			left -= xfer;
308			uiocp += xfer;
309			uiop->uio_offset += xfer;
310			uiop->uio_resid -= xfer;
311		}
312		tcp = (char *)uiop->uio_iov->iov_base;
313		tcp += uiosiz;
314		uiop->uio_iov->iov_base = (void *)tcp;
315		uiop->uio_iov->iov_len -= uiosiz;
316		siz -= uiosiz;
317	}
318	if (rem > 0) {
319		if (rem > M_TRAILINGSPACE(mp)) {
320			NFSMGET(mp);
321			mbuf_setlen(mp, 0);
322			mbuf_setnext(mp2, mp);
323		}
324		cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
325		for (left = 0; left < rem; left++)
326			*cp++ = '\0';
327		mbuf_setlen(mp, mbuf_len(mp) + rem);
328		nd->nd_bpos = cp;
329	} else
330		nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
331	nd->nd_mb = mp;
332}
333#endif	/* !APPLE */
334
335/*
336 * Load vnode attributes from the xdr file attributes.
337 * Returns EBADRPC if they can't be parsed, 0 otherwise.
338 */
339APPLESTATIC int
340nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
341{
342	struct nfs_fattr *fp;
343	int error = 0;
344
345	if (nd->nd_flag & ND_NFSV4) {
346		error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
347		    NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
348	} else if (nd->nd_flag & ND_NFSV3) {
349		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
350		nap->na_type = nfsv34tov_type(fp->fa_type);
351		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
352		nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
353			fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
354		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
355		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
356		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
357		nap->na_size = fxdr_hyper(&fp->fa3_size);
358		nap->na_blocksize = NFS_FABLKSIZE;
359		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
360		nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
361		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
362		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
363		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
364		nap->na_flags = 0;
365		nap->na_filerev = 0;
366	} else {
367		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
368		nap->na_type = nfsv2tov_type(fp->fa_type);
369		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
370		if (nap->na_type == VNON || nap->na_type == VREG)
371			nap->na_type = IFTOVT(nap->na_mode);
372		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
373
374		/*
375		 * Really ugly NFSv2 kludge.
376		 */
377		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
378			nap->na_type = VFIFO;
379		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
380		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
381		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
382		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
383		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
384		nap->na_bytes =
385		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
386		    NFS_FABLKSIZE;
387		nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
388		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
389		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
390		nap->na_flags = 0;
391		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
392		    fp->fa2_ctime.nfsv2_sec);
393		nap->na_ctime.tv_nsec = 0;
394		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
395		nap->na_filerev = 0;
396	}
397nfsmout:
398	return (error);
399}
400
401/*
402 * This function finds the directory cookie that corresponds to the
403 * logical byte offset given.
404 */
405APPLESTATIC nfsuint64 *
406nfscl_getcookie(struct nfsnode *np, off_t off, int add)
407{
408	struct nfsdmap *dp, *dp2;
409	int pos;
410
411	pos = off / NFS_DIRBLKSIZ;
412	if (pos == 0) {
413		KASSERT(!add, ("nfs getcookie add at 0"));
414		return (&nfs_nullcookie);
415	}
416	pos--;
417	dp = LIST_FIRST(&np->n_cookies);
418	if (!dp) {
419		if (add) {
420			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
421				M_NFSDIROFF, M_WAITOK);
422			dp->ndm_eocookie = 0;
423			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
424		} else
425			return (NULL);
426	}
427	while (pos >= NFSNUMCOOKIES) {
428		pos -= NFSNUMCOOKIES;
429		if (LIST_NEXT(dp, ndm_list) != NULL) {
430			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
431				pos >= dp->ndm_eocookie)
432				return (NULL);
433			dp = LIST_NEXT(dp, ndm_list);
434		} else if (add) {
435			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
436				M_NFSDIROFF, M_WAITOK);
437			dp2->ndm_eocookie = 0;
438			LIST_INSERT_AFTER(dp, dp2, ndm_list);
439			dp = dp2;
440		} else
441			return (NULL);
442	}
443	if (pos >= dp->ndm_eocookie) {
444		if (add)
445			dp->ndm_eocookie = pos + 1;
446		else
447			return (NULL);
448	}
449	return (&dp->ndm_cookies[pos]);
450}
451
452/*
453 * Gets a file handle out of an nfs reply sent to the client and returns
454 * the file handle and the file's attributes.
455 * For V4, it assumes that Getfh and Getattr Op's results are here.
456 */
457APPLESTATIC int
458nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
459    struct nfsvattr *nap, int *attrflagp)
460{
461	u_int32_t *tl;
462	int error = 0, flag = 1;
463
464	*nfhpp = NULL;
465	*attrflagp = 0;
466	/*
467	 * First get the file handle and vnode.
468	 */
469	if (nd->nd_flag & ND_NFSV3) {
470		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
471		flag = fxdr_unsigned(int, *tl);
472	} else if (nd->nd_flag & ND_NFSV4) {
473		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
474		/* If the GetFH failed, clear flag. */
475		if (*++tl != 0) {
476			nd->nd_flag |= ND_NOMOREDATA;
477			flag = 0;
478		}
479	}
480	if (flag) {
481		error = nfsm_getfh(nd, nfhpp);
482		if (error)
483			return (error);
484	}
485
486	/*
487	 * Now, get the attributes.
488	 */
489	if (flag != 0 && (nd->nd_flag & ND_NFSV4) != 0) {
490		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
491		if (*++tl != 0) {
492			nd->nd_flag |= ND_NOMOREDATA;
493			flag = 0;
494		}
495	} else if (nd->nd_flag & ND_NFSV3) {
496		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
497		if (flag) {
498			flag = fxdr_unsigned(int, *tl);
499		} else if (fxdr_unsigned(int, *tl)) {
500			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
501			if (error)
502				return (error);
503		}
504	}
505	if (flag) {
506		error = nfsm_loadattr(nd, nap);
507		if (!error)
508			*attrflagp = 1;
509	}
510nfsmout:
511	return (error);
512}
513
514/*
515 * Put a state Id in the mbuf list.
516 */
517APPLESTATIC void
518nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
519{
520	nfsv4stateid_t *st;
521
522	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
523	if (flag == NFSSTATEID_PUTALLZERO) {
524		st->seqid = 0;
525		st->other[0] = 0;
526		st->other[1] = 0;
527		st->other[2] = 0;
528	} else if (flag == NFSSTATEID_PUTALLONE) {
529		st->seqid = 0xffffffff;
530		st->other[0] = 0xffffffff;
531		st->other[1] = 0xffffffff;
532		st->other[2] = 0xffffffff;
533	} else if (flag == NFSSTATEID_PUTSEQIDZERO) {
534		st->seqid = 0;
535		st->other[0] = stateidp->other[0];
536		st->other[1] = stateidp->other[1];
537		st->other[2] = stateidp->other[2];
538	} else {
539		st->seqid = stateidp->seqid;
540		st->other[0] = stateidp->other[0];
541		st->other[1] = stateidp->other[1];
542		st->other[2] = stateidp->other[2];
543	}
544}
545
546/*
547 * Initialize the owner/delegation sleep lock.
548 */
549APPLESTATIC void
550nfscl_lockinit(struct nfsv4lock *lckp)
551{
552
553	lckp->nfslock_usecnt = 0;
554	lckp->nfslock_lock = 0;
555}
556
557/*
558 * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
559 * thread for each posix process in the kernel.)
560 */
561APPLESTATIC void
562nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
563{
564	int igotlock;
565
566	do {
567		igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
568	} while (!igotlock);
569}
570
571/*
572 * Release an exclusive lock.
573 */
574APPLESTATIC void
575nfscl_lockunlock(struct nfsv4lock *lckp)
576{
577
578	nfsv4_unlock(lckp, 0);
579}
580
581/*
582 * Called to derefernce a lock on a stateid (delegation or open owner).
583 */
584APPLESTATIC void
585nfscl_lockderef(struct nfsv4lock *lckp)
586{
587
588	NFSLOCKCLSTATE();
589	lckp->nfslock_usecnt--;
590	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
591		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
592		wakeup((caddr_t)lckp);
593	}
594	NFSUNLOCKCLSTATE();
595}
596
597