xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision d93a896e)
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 	{ NFSV4OP_OPEN, 3, "OpenLayoutGet", 13, },
116 	{ NFSV4OP_OPEN, 8, "CreateLayGet", 12, },
117 };
118 
119 /*
120  * NFS RPCS that have large request message size.
121  */
122 static int nfs_bigrequest[NFSV41_NPROCS] = {
123 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0
126 };
127 
128 /*
129  * Start building a request. Mostly just put the first file handle in
130  * place.
131  */
132 APPLESTATIC void
133 nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
134     u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp, struct nfsclsession *sep)
135 {
136 	struct mbuf *mb;
137 	u_int32_t *tl;
138 	int opcnt;
139 	nfsattrbit_t attrbits;
140 
141 	/*
142 	 * First, fill in some of the fields of nd.
143 	 */
144 	nd->nd_slotseq = NULL;
145 	if (NFSHASNFSV4(nmp)) {
146 		nd->nd_flag = ND_NFSV4 | ND_NFSCL;
147 		if (NFSHASNFSV4N(nmp))
148 			nd->nd_flag |= ND_NFSV41;
149 	} else if (NFSHASNFSV3(nmp))
150 		nd->nd_flag = ND_NFSV3 | ND_NFSCL;
151 	else
152 		nd->nd_flag = ND_NFSV2 | ND_NFSCL;
153 	nd->nd_procnum = procnum;
154 	nd->nd_repstat = 0;
155 
156 	/*
157 	 * Get the first mbuf for the request.
158 	 */
159 	if (nfs_bigrequest[procnum])
160 		NFSMCLGET(mb, M_WAITOK);
161 	else
162 		NFSMGET(mb);
163 	mbuf_setlen(mb, 0);
164 	nd->nd_mreq = nd->nd_mb = mb;
165 	nd->nd_bpos = NFSMTOD(mb, caddr_t);
166 
167 	/*
168 	 * And fill the first file handle into the request.
169 	 */
170 	if (nd->nd_flag & ND_NFSV4) {
171 		opcnt = nfsv4_opmap[procnum].opcnt +
172 		    nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
173 		if ((nd->nd_flag & ND_NFSV41) != 0) {
174 			opcnt += nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq;
175 			if (procnum == NFSPROC_RENEW)
176 				/*
177 				 * For the special case of Renew, just do a
178 				 * Sequence Op.
179 				 */
180 				opcnt = 1;
181 			else if (procnum == NFSPROC_WRITEDS ||
182 			    procnum == NFSPROC_COMMITDS)
183 				/*
184 				 * For the special case of a Writeor Commit to
185 				 * a DS, the opcnt == 3, for Sequence, PutFH,
186 				 * Write/Commit.
187 				 */
188 				opcnt = 3;
189 		}
190 		/*
191 		 * What should the tag really be?
192 		 */
193 		(void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
194 			nfsv4_opmap[procnum].taglen);
195 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
196 		if ((nd->nd_flag & ND_NFSV41) != 0)
197 			*tl++ = txdr_unsigned(NFSV41_MINORVERSION);
198 		else
199 			*tl++ = txdr_unsigned(NFSV4_MINORVERSION);
200 		if (opcntpp != NULL)
201 			*opcntpp = tl;
202 		*tl = txdr_unsigned(opcnt);
203 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
204 		    nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq > 0) {
205 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].loopbadsess >
206 			    0)
207 				nd->nd_flag |= ND_LOOPBADSESS;
208 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
209 			*tl = txdr_unsigned(NFSV4OP_SEQUENCE);
210 			if (sep == NULL) {
211 				sep = nfsmnt_mdssession(nmp);
212 				nfsv4_setsequence(nmp, nd, sep,
213 				    nfs_bigreply[procnum]);
214 			} else
215 				nfsv4_setsequence(nmp, nd, sep,
216 				    nfs_bigreply[procnum]);
217 		}
218 		if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
219 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
220 			*tl = txdr_unsigned(NFSV4OP_PUTFH);
221 			(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
222 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh
223 			    == 2 && procnum != NFSPROC_WRITEDS &&
224 			    procnum != NFSPROC_COMMITDS) {
225 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
226 				*tl = txdr_unsigned(NFSV4OP_GETATTR);
227 				/*
228 				 * For Lookup Ops, we want all the directory
229 				 * attributes, so we can load the name cache.
230 				 */
231 				if (procnum == NFSPROC_LOOKUP ||
232 				    procnum == NFSPROC_LOOKUPP)
233 					NFSGETATTR_ATTRBIT(&attrbits);
234 				else {
235 					NFSWCCATTR_ATTRBIT(&attrbits);
236 					nd->nd_flag |= ND_V4WCCATTR;
237 				}
238 				(void) nfsrv_putattrbit(nd, &attrbits);
239 			}
240 		}
241 		if (procnum != NFSPROC_RENEW ||
242 		    (nd->nd_flag & ND_NFSV41) == 0) {
243 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
244 			*tl = txdr_unsigned(nfsv4_opmap[procnum].op);
245 		}
246 	} else {
247 		(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
248 	}
249 	if (procnum < NFSV41_NPROCS)
250 		NFSINCRGLOBAL(nfsstatsv1.rpccnt[procnum]);
251 }
252 
253 #ifndef APPLE
254 /*
255  * copies a uio scatter/gather list to an mbuf chain.
256  * NOTE: can ony handle iovcnt == 1
257  */
258 APPLESTATIC void
259 nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
260 {
261 	char *uiocp;
262 	struct mbuf *mp, *mp2;
263 	int xfer, left, mlen;
264 	int uiosiz, clflg, rem;
265 	char *cp, *tcp;
266 
267 	KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
268 
269 	if (siz > ncl_mbuf_mlen)	/* or should it >= MCLBYTES ?? */
270 		clflg = 1;
271 	else
272 		clflg = 0;
273 	rem = NFSM_RNDUP(siz) - siz;
274 	mp = mp2 = nd->nd_mb;
275 	while (siz > 0) {
276 		left = uiop->uio_iov->iov_len;
277 		uiocp = uiop->uio_iov->iov_base;
278 		if (left > siz)
279 			left = siz;
280 		uiosiz = left;
281 		while (left > 0) {
282 			mlen = M_TRAILINGSPACE(mp);
283 			if (mlen == 0) {
284 				if (clflg)
285 					NFSMCLGET(mp, M_WAITOK);
286 				else
287 					NFSMGET(mp);
288 				mbuf_setlen(mp, 0);
289 				mbuf_setnext(mp2, mp);
290 				mp2 = mp;
291 				mlen = M_TRAILINGSPACE(mp);
292 			}
293 			xfer = (left > mlen) ? mlen : left;
294 #ifdef notdef
295 			/* Not Yet.. */
296 			if (uiop->uio_iov->iov_op != NULL)
297 				(*(uiop->uio_iov->iov_op))
298 				(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
299 				    xfer);
300 			else
301 #endif
302 			if (uiop->uio_segflg == UIO_SYSSPACE)
303 			    NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
304 				xfer);
305 			else
306 			    copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
307 				+ mbuf_len(mp), xfer);
308 			mbuf_setlen(mp, mbuf_len(mp) + xfer);
309 			left -= xfer;
310 			uiocp += xfer;
311 			uiop->uio_offset += xfer;
312 			uiop->uio_resid -= xfer;
313 		}
314 		tcp = (char *)uiop->uio_iov->iov_base;
315 		tcp += uiosiz;
316 		uiop->uio_iov->iov_base = (void *)tcp;
317 		uiop->uio_iov->iov_len -= uiosiz;
318 		siz -= uiosiz;
319 	}
320 	if (rem > 0) {
321 		if (rem > M_TRAILINGSPACE(mp)) {
322 			NFSMGET(mp);
323 			mbuf_setlen(mp, 0);
324 			mbuf_setnext(mp2, mp);
325 		}
326 		cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
327 		for (left = 0; left < rem; left++)
328 			*cp++ = '\0';
329 		mbuf_setlen(mp, mbuf_len(mp) + rem);
330 		nd->nd_bpos = cp;
331 	} else
332 		nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
333 	nd->nd_mb = mp;
334 }
335 #endif	/* !APPLE */
336 
337 /*
338  * Load vnode attributes from the xdr file attributes.
339  * Returns EBADRPC if they can't be parsed, 0 otherwise.
340  */
341 APPLESTATIC int
342 nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
343 {
344 	struct nfs_fattr *fp;
345 	int error = 0;
346 
347 	if (nd->nd_flag & ND_NFSV4) {
348 		error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
349 		    NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
350 	} else if (nd->nd_flag & ND_NFSV3) {
351 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
352 		nap->na_type = nfsv34tov_type(fp->fa_type);
353 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
354 		nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
355 			fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
356 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
357 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
358 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
359 		nap->na_size = fxdr_hyper(&fp->fa3_size);
360 		nap->na_blocksize = NFS_FABLKSIZE;
361 		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
362 		nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
363 		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
364 		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
365 		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
366 		nap->na_flags = 0;
367 		nap->na_filerev = 0;
368 	} else {
369 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
370 		nap->na_type = nfsv2tov_type(fp->fa_type);
371 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
372 		if (nap->na_type == VNON || nap->na_type == VREG)
373 			nap->na_type = IFTOVT(nap->na_mode);
374 		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
375 
376 		/*
377 		 * Really ugly NFSv2 kludge.
378 		 */
379 		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
380 			nap->na_type = VFIFO;
381 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
382 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
383 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
384 		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
385 		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
386 		nap->na_bytes =
387 		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
388 		    NFS_FABLKSIZE;
389 		nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
390 		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
391 		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
392 		nap->na_flags = 0;
393 		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
394 		    fp->fa2_ctime.nfsv2_sec);
395 		nap->na_ctime.tv_nsec = 0;
396 		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
397 		nap->na_filerev = 0;
398 	}
399 nfsmout:
400 	return (error);
401 }
402 
403 /*
404  * This function finds the directory cookie that corresponds to the
405  * logical byte offset given.
406  */
407 APPLESTATIC nfsuint64 *
408 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
409 {
410 	struct nfsdmap *dp, *dp2;
411 	int pos;
412 
413 	pos = off / NFS_DIRBLKSIZ;
414 	if (pos == 0) {
415 		KASSERT(!add, ("nfs getcookie add at 0"));
416 		return (&nfs_nullcookie);
417 	}
418 	pos--;
419 	dp = LIST_FIRST(&np->n_cookies);
420 	if (!dp) {
421 		if (add) {
422 			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
423 				M_NFSDIROFF, M_WAITOK);
424 			dp->ndm_eocookie = 0;
425 			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
426 		} else
427 			return (NULL);
428 	}
429 	while (pos >= NFSNUMCOOKIES) {
430 		pos -= NFSNUMCOOKIES;
431 		if (LIST_NEXT(dp, ndm_list) != NULL) {
432 			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
433 				pos >= dp->ndm_eocookie)
434 				return (NULL);
435 			dp = LIST_NEXT(dp, ndm_list);
436 		} else if (add) {
437 			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
438 				M_NFSDIROFF, M_WAITOK);
439 			dp2->ndm_eocookie = 0;
440 			LIST_INSERT_AFTER(dp, dp2, ndm_list);
441 			dp = dp2;
442 		} else
443 			return (NULL);
444 	}
445 	if (pos >= dp->ndm_eocookie) {
446 		if (add)
447 			dp->ndm_eocookie = pos + 1;
448 		else
449 			return (NULL);
450 	}
451 	return (&dp->ndm_cookies[pos]);
452 }
453 
454 /*
455  * Gets a file handle out of an nfs reply sent to the client and returns
456  * the file handle and the file's attributes.
457  * For V4, it assumes that Getfh and Getattr Op's results are here.
458  */
459 APPLESTATIC int
460 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
461     struct nfsvattr *nap, int *attrflagp)
462 {
463 	u_int32_t *tl;
464 	int error = 0, flag = 1;
465 
466 	*nfhpp = NULL;
467 	*attrflagp = 0;
468 	/*
469 	 * First get the file handle and vnode.
470 	 */
471 	if (nd->nd_flag & ND_NFSV3) {
472 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
473 		flag = fxdr_unsigned(int, *tl);
474 	} else if (nd->nd_flag & ND_NFSV4) {
475 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
476 		/* If the GetFH failed, clear flag. */
477 		if (*++tl != 0) {
478 			nd->nd_flag |= ND_NOMOREDATA;
479 			flag = 0;
480 			error = ENXIO;	/* Return ENXIO so *nfhpp isn't used. */
481 		}
482 	}
483 	if (flag) {
484 		error = nfsm_getfh(nd, nfhpp);
485 		if (error)
486 			return (error);
487 	}
488 
489 	/*
490 	 * Now, get the attributes.
491 	 */
492 	if (flag != 0 && (nd->nd_flag & ND_NFSV4) != 0) {
493 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
494 		if (*++tl != 0) {
495 			nd->nd_flag |= ND_NOMOREDATA;
496 			flag = 0;
497 		}
498 	} else if (nd->nd_flag & ND_NFSV3) {
499 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
500 		if (flag) {
501 			flag = fxdr_unsigned(int, *tl);
502 		} else if (fxdr_unsigned(int, *tl)) {
503 			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
504 			if (error)
505 				return (error);
506 		}
507 	}
508 	if (flag) {
509 		error = nfsm_loadattr(nd, nap);
510 		if (!error)
511 			*attrflagp = 1;
512 	}
513 nfsmout:
514 	return (error);
515 }
516 
517 /*
518  * Put a state Id in the mbuf list.
519  */
520 APPLESTATIC void
521 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
522 {
523 	nfsv4stateid_t *st;
524 
525 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
526 	if (flag == NFSSTATEID_PUTALLZERO) {
527 		st->seqid = 0;
528 		st->other[0] = 0;
529 		st->other[1] = 0;
530 		st->other[2] = 0;
531 	} else if (flag == NFSSTATEID_PUTALLONE) {
532 		st->seqid = 0xffffffff;
533 		st->other[0] = 0xffffffff;
534 		st->other[1] = 0xffffffff;
535 		st->other[2] = 0xffffffff;
536 	} else if (flag == NFSSTATEID_PUTSEQIDZERO) {
537 		st->seqid = 0;
538 		st->other[0] = stateidp->other[0];
539 		st->other[1] = stateidp->other[1];
540 		st->other[2] = stateidp->other[2];
541 	} else {
542 		st->seqid = stateidp->seqid;
543 		st->other[0] = stateidp->other[0];
544 		st->other[1] = stateidp->other[1];
545 		st->other[2] = stateidp->other[2];
546 	}
547 }
548 
549 /*
550  * Initialize the owner/delegation sleep lock.
551  */
552 APPLESTATIC void
553 nfscl_lockinit(struct nfsv4lock *lckp)
554 {
555 
556 	lckp->nfslock_usecnt = 0;
557 	lckp->nfslock_lock = 0;
558 }
559 
560 /*
561  * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
562  * thread for each posix process in the kernel.)
563  */
564 APPLESTATIC void
565 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
566 {
567 	int igotlock;
568 
569 	do {
570 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
571 	} while (!igotlock);
572 }
573 
574 /*
575  * Release an exclusive lock.
576  */
577 APPLESTATIC void
578 nfscl_lockunlock(struct nfsv4lock *lckp)
579 {
580 
581 	nfsv4_unlock(lckp, 0);
582 }
583 
584 /*
585  * Called to derefernce a lock on a stateid (delegation or open owner).
586  */
587 APPLESTATIC void
588 nfscl_lockderef(struct nfsv4lock *lckp)
589 {
590 
591 	NFSLOCKCLSTATE();
592 	lckp->nfslock_usecnt--;
593 	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
594 		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
595 		wakeup((caddr_t)lckp);
596 	}
597 	NFSUNLOCKCLSTATE();
598 }
599 
600