xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision 193d9e768ba63fcfb187cfd17f461f7d41345048)
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  * 3. 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 nfsstatsv1 nfsstatsv1;
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, 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  */
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 			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 < NFSV41_NPROCS)
248 		NFSINCRGLOBAL(nfsstatsv1.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  */
256 APPLESTATIC void
257 nfsm_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  */
339 APPLESTATIC int
340 nfsm_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 	}
397 nfsmout:
398 	return (error);
399 }
400 
401 /*
402  * This function finds the directory cookie that corresponds to the
403  * logical byte offset given.
404  */
405 APPLESTATIC nfsuint64 *
406 nfscl_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  */
457 APPLESTATIC int
458 nfscl_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 	}
475 	if (flag) {
476 		error = nfsm_getfh(nd, nfhpp);
477 		if (error)
478 			return (error);
479 	}
480 
481 	/*
482 	 * Now, get the attributes.
483 	 */
484 	if (nd->nd_flag & ND_NFSV4) {
485 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
486 	} else if (nd->nd_flag & ND_NFSV3) {
487 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
488 		if (flag) {
489 			flag = fxdr_unsigned(int, *tl);
490 		} else if (fxdr_unsigned(int, *tl)) {
491 			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
492 			if (error)
493 				return (error);
494 		}
495 	}
496 	if (flag) {
497 		error = nfsm_loadattr(nd, nap);
498 		if (!error)
499 			*attrflagp = 1;
500 	}
501 nfsmout:
502 	return (error);
503 }
504 
505 /*
506  * Put a state Id in the mbuf list.
507  */
508 APPLESTATIC void
509 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
510 {
511 	nfsv4stateid_t *st;
512 
513 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
514 	if (flag == NFSSTATEID_PUTALLZERO) {
515 		st->seqid = 0;
516 		st->other[0] = 0;
517 		st->other[1] = 0;
518 		st->other[2] = 0;
519 	} else if (flag == NFSSTATEID_PUTALLONE) {
520 		st->seqid = 0xffffffff;
521 		st->other[0] = 0xffffffff;
522 		st->other[1] = 0xffffffff;
523 		st->other[2] = 0xffffffff;
524 	} else if (flag == NFSSTATEID_PUTSEQIDZERO) {
525 		st->seqid = 0;
526 		st->other[0] = stateidp->other[0];
527 		st->other[1] = stateidp->other[1];
528 		st->other[2] = stateidp->other[2];
529 	} else {
530 		st->seqid = stateidp->seqid;
531 		st->other[0] = stateidp->other[0];
532 		st->other[1] = stateidp->other[1];
533 		st->other[2] = stateidp->other[2];
534 	}
535 }
536 
537 /*
538  * Initialize the owner/delegation sleep lock.
539  */
540 APPLESTATIC void
541 nfscl_lockinit(struct nfsv4lock *lckp)
542 {
543 
544 	lckp->nfslock_usecnt = 0;
545 	lckp->nfslock_lock = 0;
546 }
547 
548 /*
549  * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
550  * thread for each posix process in the kernel.)
551  */
552 APPLESTATIC void
553 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
554 {
555 	int igotlock;
556 
557 	do {
558 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
559 	} while (!igotlock);
560 }
561 
562 /*
563  * Release an exclusive lock.
564  */
565 APPLESTATIC void
566 nfscl_lockunlock(struct nfsv4lock *lckp)
567 {
568 
569 	nfsv4_unlock(lckp, 0);
570 }
571 
572 /*
573  * Called to derefernce a lock on a stateid (delegation or open owner).
574  */
575 APPLESTATIC void
576 nfscl_lockderef(struct nfsv4lock *lckp)
577 {
578 
579 	NFSLOCKCLSTATE();
580 	lckp->nfslock_usecnt--;
581 	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
582 		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
583 		wakeup((caddr_t)lckp);
584 	}
585 	NFSUNLOCKCLSTATE();
586 }
587 
588