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