xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision 7d99ab9fd0cc2c1ce2ecef0ed6d0672c2a50b0cb)
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$");
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 
45 extern struct nfsstats newnfsstats;
46 extern struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS];
47 extern int ncl_mbuf_mlen;
48 extern enum vtype newnv2tov_type[8];
49 extern enum vtype nv34tov_type[8];
50 extern int	nfs_bigreply[NFSV41_NPROCS];
51 NFSCLSTATEMUTEX;
52 #endif	/* !APPLEKEXT */
53 
54 static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
55 static 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, 3, "Open", 4, },
70 	{ NFSV4OP_CREATE, 3, "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  */
120 static 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  */
130 APPLESTATIC void
131 nfscl_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 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
204 			*tl = txdr_unsigned(NFSV4OP_SEQUENCE);
205 			if (sep == NULL)
206 				nfsv4_setsequence(nd, NFSMNT_MDSSESSION(nmp),
207 				    nfs_bigreply[procnum]);
208 			else
209 				nfsv4_setsequence(nd, sep,
210 				    nfs_bigreply[procnum]);
211 		}
212 		if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
213 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
214 			*tl = txdr_unsigned(NFSV4OP_PUTFH);
215 			(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
216 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh
217 			    == 2 && procnum != NFSPROC_WRITEDS &&
218 			    procnum != NFSPROC_COMMITDS) {
219 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
220 				*tl = txdr_unsigned(NFSV4OP_GETATTR);
221 				NFSWCCATTR_ATTRBIT(&attrbits);
222 				(void) nfsrv_putattrbit(nd, &attrbits);
223 				nd->nd_flag |= ND_V4WCCATTR;
224 			}
225 		}
226 		if (procnum != NFSPROC_RENEW ||
227 		    (nd->nd_flag & ND_NFSV41) == 0) {
228 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
229 			*tl = txdr_unsigned(nfsv4_opmap[procnum].op);
230 		}
231 	} else {
232 		(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
233 	}
234 	if (procnum < NFSV4_NPROCS)
235 		NFSINCRGLOBAL(newnfsstats.rpccnt[procnum]);
236 }
237 
238 #ifndef APPLE
239 /*
240  * copies a uio scatter/gather list to an mbuf chain.
241  * NOTE: can ony handle iovcnt == 1
242  */
243 APPLESTATIC void
244 nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
245 {
246 	char *uiocp;
247 	struct mbuf *mp, *mp2;
248 	int xfer, left, mlen;
249 	int uiosiz, clflg, rem;
250 	char *cp, *tcp;
251 
252 	KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
253 
254 	if (siz > ncl_mbuf_mlen)	/* or should it >= MCLBYTES ?? */
255 		clflg = 1;
256 	else
257 		clflg = 0;
258 	rem = NFSM_RNDUP(siz) - siz;
259 	mp = mp2 = nd->nd_mb;
260 	while (siz > 0) {
261 		left = uiop->uio_iov->iov_len;
262 		uiocp = uiop->uio_iov->iov_base;
263 		if (left > siz)
264 			left = siz;
265 		uiosiz = left;
266 		while (left > 0) {
267 			mlen = M_TRAILINGSPACE(mp);
268 			if (mlen == 0) {
269 				if (clflg)
270 					NFSMCLGET(mp, M_WAITOK);
271 				else
272 					NFSMGET(mp);
273 				mbuf_setlen(mp, 0);
274 				mbuf_setnext(mp2, mp);
275 				mp2 = mp;
276 				mlen = M_TRAILINGSPACE(mp);
277 			}
278 			xfer = (left > mlen) ? mlen : left;
279 #ifdef notdef
280 			/* Not Yet.. */
281 			if (uiop->uio_iov->iov_op != NULL)
282 				(*(uiop->uio_iov->iov_op))
283 				(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
284 				    xfer);
285 			else
286 #endif
287 			if (uiop->uio_segflg == UIO_SYSSPACE)
288 			    NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
289 				xfer);
290 			else
291 			    copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
292 				+ mbuf_len(mp), xfer);
293 			mbuf_setlen(mp, mbuf_len(mp) + xfer);
294 			left -= xfer;
295 			uiocp += xfer;
296 			uiop->uio_offset += xfer;
297 			uiop->uio_resid -= xfer;
298 		}
299 		tcp = (char *)uiop->uio_iov->iov_base;
300 		tcp += uiosiz;
301 		uiop->uio_iov->iov_base = (void *)tcp;
302 		uiop->uio_iov->iov_len -= uiosiz;
303 		siz -= uiosiz;
304 	}
305 	if (rem > 0) {
306 		if (rem > M_TRAILINGSPACE(mp)) {
307 			NFSMGET(mp);
308 			mbuf_setlen(mp, 0);
309 			mbuf_setnext(mp2, mp);
310 		}
311 		cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
312 		for (left = 0; left < rem; left++)
313 			*cp++ = '\0';
314 		mbuf_setlen(mp, mbuf_len(mp) + rem);
315 		nd->nd_bpos = cp;
316 	} else
317 		nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
318 	nd->nd_mb = mp;
319 }
320 #endif	/* !APPLE */
321 
322 /*
323  * Load vnode attributes from the xdr file attributes.
324  * Returns EBADRPC if they can't be parsed, 0 otherwise.
325  */
326 APPLESTATIC int
327 nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
328 {
329 	struct nfs_fattr *fp;
330 	int error = 0;
331 
332 	if (nd->nd_flag & ND_NFSV4) {
333 		error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
334 		    NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
335 	} else if (nd->nd_flag & ND_NFSV3) {
336 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
337 		nap->na_type = nfsv34tov_type(fp->fa_type);
338 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
339 		nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
340 			fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
341 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
342 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
343 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
344 		nap->na_size = fxdr_hyper(&fp->fa3_size);
345 		nap->na_blocksize = NFS_FABLKSIZE;
346 		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
347 		nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
348 		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
349 		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
350 		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
351 		nap->na_flags = 0;
352 		nap->na_filerev = 0;
353 	} else {
354 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
355 		nap->na_type = nfsv2tov_type(fp->fa_type);
356 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
357 		if (nap->na_type == VNON || nap->na_type == VREG)
358 			nap->na_type = IFTOVT(nap->na_mode);
359 		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
360 
361 		/*
362 		 * Really ugly NFSv2 kludge.
363 		 */
364 		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
365 			nap->na_type = VFIFO;
366 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
367 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
368 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
369 		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
370 		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
371 		nap->na_bytes =
372 		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
373 		    NFS_FABLKSIZE;
374 		nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
375 		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
376 		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
377 		nap->na_flags = 0;
378 		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
379 		    fp->fa2_ctime.nfsv2_sec);
380 		nap->na_ctime.tv_nsec = 0;
381 		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
382 		nap->na_filerev = 0;
383 	}
384 nfsmout:
385 	return (error);
386 }
387 
388 /*
389  * This function finds the directory cookie that corresponds to the
390  * logical byte offset given.
391  */
392 APPLESTATIC nfsuint64 *
393 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
394 {
395 	struct nfsdmap *dp, *dp2;
396 	int pos;
397 
398 	pos = off / NFS_DIRBLKSIZ;
399 	if (pos == 0) {
400 		KASSERT(!add, ("nfs getcookie add at 0"));
401 		return (&nfs_nullcookie);
402 	}
403 	pos--;
404 	dp = LIST_FIRST(&np->n_cookies);
405 	if (!dp) {
406 		if (add) {
407 			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
408 				M_NFSDIROFF, M_WAITOK);
409 			dp->ndm_eocookie = 0;
410 			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
411 		} else
412 			return (NULL);
413 	}
414 	while (pos >= NFSNUMCOOKIES) {
415 		pos -= NFSNUMCOOKIES;
416 		if (LIST_NEXT(dp, ndm_list) != NULL) {
417 			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
418 				pos >= dp->ndm_eocookie)
419 				return (NULL);
420 			dp = LIST_NEXT(dp, ndm_list);
421 		} else if (add) {
422 			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
423 				M_NFSDIROFF, M_WAITOK);
424 			dp2->ndm_eocookie = 0;
425 			LIST_INSERT_AFTER(dp, dp2, ndm_list);
426 			dp = dp2;
427 		} else
428 			return (NULL);
429 	}
430 	if (pos >= dp->ndm_eocookie) {
431 		if (add)
432 			dp->ndm_eocookie = pos + 1;
433 		else
434 			return (NULL);
435 	}
436 	return (&dp->ndm_cookies[pos]);
437 }
438 
439 /*
440  * Gets a file handle out of an nfs reply sent to the client and returns
441  * the file handle and the file's attributes.
442  * For V4, it assumes that Getfh and Getattr Op's results are here.
443  */
444 APPLESTATIC int
445 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
446     struct nfsvattr *nap, int *attrflagp)
447 {
448 	u_int32_t *tl;
449 	int error = 0, flag = 1;
450 
451 	*nfhpp = NULL;
452 	*attrflagp = 0;
453 	/*
454 	 * First get the file handle and vnode.
455 	 */
456 	if (nd->nd_flag & ND_NFSV3) {
457 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
458 		flag = fxdr_unsigned(int, *tl);
459 	} else if (nd->nd_flag & ND_NFSV4) {
460 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
461 	}
462 	if (flag) {
463 		error = nfsm_getfh(nd, nfhpp);
464 		if (error)
465 			return (error);
466 	}
467 
468 	/*
469 	 * Now, get the attributes.
470 	 */
471 	if (nd->nd_flag & ND_NFSV4) {
472 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
473 	} else if (nd->nd_flag & ND_NFSV3) {
474 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
475 		if (flag) {
476 			flag = fxdr_unsigned(int, *tl);
477 		} else if (fxdr_unsigned(int, *tl)) {
478 			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
479 			if (error)
480 				return (error);
481 		}
482 	}
483 	if (flag) {
484 		error = nfsm_loadattr(nd, nap);
485 		if (!error)
486 			*attrflagp = 1;
487 	}
488 nfsmout:
489 	return (error);
490 }
491 
492 /*
493  * Put a state Id in the mbuf list.
494  */
495 APPLESTATIC void
496 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
497 {
498 	nfsv4stateid_t *st;
499 
500 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
501 	if (flag == NFSSTATEID_PUTALLZERO) {
502 		st->seqid = 0;
503 		st->other[0] = 0;
504 		st->other[1] = 0;
505 		st->other[2] = 0;
506 	} else if (flag == NFSSTATEID_PUTALLONE) {
507 		st->seqid = 0xffffffff;
508 		st->other[0] = 0xffffffff;
509 		st->other[1] = 0xffffffff;
510 		st->other[2] = 0xffffffff;
511 	} else if (flag == NFSSTATEID_PUTSEQIDZERO) {
512 		st->seqid = 0;
513 		st->other[0] = stateidp->other[0];
514 		st->other[1] = stateidp->other[1];
515 		st->other[2] = stateidp->other[2];
516 	} else {
517 		st->seqid = stateidp->seqid;
518 		st->other[0] = stateidp->other[0];
519 		st->other[1] = stateidp->other[1];
520 		st->other[2] = stateidp->other[2];
521 	}
522 }
523 
524 /*
525  * Initialize the owner/delegation sleep lock.
526  */
527 APPLESTATIC void
528 nfscl_lockinit(struct nfsv4lock *lckp)
529 {
530 
531 	lckp->nfslock_usecnt = 0;
532 	lckp->nfslock_lock = 0;
533 }
534 
535 /*
536  * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
537  * thread for each posix process in the kernel.)
538  */
539 APPLESTATIC void
540 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
541 {
542 	int igotlock;
543 
544 	do {
545 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
546 	} while (!igotlock);
547 }
548 
549 /*
550  * Release an exclusive lock.
551  */
552 APPLESTATIC void
553 nfscl_lockunlock(struct nfsv4lock *lckp)
554 {
555 
556 	nfsv4_unlock(lckp, 0);
557 }
558 
559 /*
560  * Called to derefernce a lock on a stateid (delegation or open owner).
561  */
562 APPLESTATIC void
563 nfscl_lockderef(struct nfsv4lock *lckp)
564 {
565 
566 	NFSLOCKCLSTATE();
567 	lckp->nfslock_usecnt--;
568 	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
569 		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
570 		wakeup((caddr_t)lckp);
571 	}
572 	NFSUNLOCKCLSTATE();
573 }
574 
575