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