xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision ab35e1c7)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Rick Macklem at The University of Guelph.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  */
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 /*
40  * These functions support the macros and help fiddle mbuf chains for
41  * the nfs op functions. They do things like create the rpc header and
42  * copy data between mbuf chains and uio lists.
43  */
44 #ifndef APPLEKEXT
45 #include <fs/nfs/nfsport.h>
46 
47 extern struct nfsstatsv1 nfsstatsv1;
48 extern struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS];
49 extern int ncl_mbuf_mlen;
50 extern enum vtype newnv2tov_type[8];
51 extern enum vtype nv34tov_type[8];
52 extern int	nfs_bigreply[NFSV41_NPROCS];
53 NFSCLSTATEMUTEX;
54 #endif	/* !APPLEKEXT */
55 
56 static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
57 static struct {
58 	int	op;
59 	int	opcnt;
60 	const u_char *tag;
61 	int	taglen;
62 } nfsv4_opmap[NFSV41_NPROCS] = {
63 	{ 0, 1, "Null", 4 },
64 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
65 	{ NFSV4OP_SETATTR, 2, "Setattr", 7, },
66 	{ NFSV4OP_LOOKUP, 3, "Lookup", 6, },
67 	{ NFSV4OP_ACCESS, 2, "Access", 6, },
68 	{ NFSV4OP_READLINK, 2, "Readlink", 8, },
69 	{ NFSV4OP_READ, 1, "Read", 4, },
70 	{ NFSV4OP_WRITE, 2, "Write", 5, },
71 	{ NFSV4OP_OPEN, 5, "Open", 4, },
72 	{ NFSV4OP_CREATE, 5, "Create", 6, },
73 	{ NFSV4OP_CREATE, 1, "Create", 6, },
74 	{ NFSV4OP_CREATE, 3, "Create", 6, },
75 	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
76 	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
77 	{ NFSV4OP_SAVEFH, 5, "Rename", 6, },
78 	{ NFSV4OP_SAVEFH, 4, "Link", 4, },
79 	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
80 	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
81 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
82 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
83 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
84 	{ NFSV4OP_COMMIT, 2, "Commit", 6, },
85 	{ NFSV4OP_LOOKUPP, 3, "Lookupp", 7, },
86 	{ NFSV4OP_SETCLIENTID, 1, "SetClientID", 11, },
87 	{ NFSV4OP_SETCLIENTIDCFRM, 1, "SetClientIDConfirm", 18, },
88 	{ NFSV4OP_LOCK, 1, "Lock", 4, },
89 	{ NFSV4OP_LOCKU, 1, "LockU", 5, },
90 	{ NFSV4OP_OPEN, 2, "Open", 4, },
91 	{ NFSV4OP_CLOSE, 1, "Close", 5, },
92 	{ NFSV4OP_OPENCONFIRM, 1, "Openconfirm", 11, },
93 	{ NFSV4OP_LOCKT, 1, "LockT", 5, },
94 	{ NFSV4OP_OPENDOWNGRADE, 1, "Opendowngrade", 13, },
95 	{ NFSV4OP_RENEW, 1, "Renew", 5, },
96 	{ NFSV4OP_PUTROOTFH, 1, "Dirpath", 7, },
97 	{ NFSV4OP_RELEASELCKOWN, 1, "Rellckown", 9, },
98 	{ NFSV4OP_DELEGRETURN, 1, "Delegret", 8, },
99 	{ NFSV4OP_DELEGRETURN, 3, "DelegRemove", 11, },
100 	{ NFSV4OP_DELEGRETURN, 7, "DelegRename1", 12, },
101 	{ NFSV4OP_DELEGRETURN, 9, "DelegRename2", 12, },
102 	{ NFSV4OP_GETATTR, 1, "Getacl", 6, },
103 	{ NFSV4OP_SETATTR, 1, "Setacl", 6, },
104 	{ NFSV4OP_EXCHANGEID, 1, "ExchangeID", 10, },
105 	{ NFSV4OP_CREATESESSION, 1, "CreateSession", 13, },
106 	{ NFSV4OP_DESTROYSESSION, 1, "DestroySession", 14, },
107 	{ NFSV4OP_DESTROYCLIENTID, 1, "DestroyClient", 13, },
108 	{ NFSV4OP_FREESTATEID, 1, "FreeStateID", 11, },
109 	{ NFSV4OP_LAYOUTGET, 1, "LayoutGet", 9, },
110 	{ NFSV4OP_GETDEVINFO, 1, "GetDeviceInfo", 13, },
111 	{ NFSV4OP_LAYOUTCOMMIT, 1, "LayoutCommit", 12, },
112 	{ NFSV4OP_LAYOUTRETURN, 1, "LayoutReturn", 12, },
113 	{ NFSV4OP_RECLAIMCOMPL, 1, "ReclaimComplete", 15, },
114 	{ NFSV4OP_WRITE, 1, "WriteDS", 7, },
115 	{ NFSV4OP_READ, 1, "ReadDS", 6, },
116 	{ NFSV4OP_COMMIT, 1, "CommitDS", 8, },
117 	{ NFSV4OP_OPEN, 3, "OpenLayoutGet", 13, },
118 	{ NFSV4OP_OPEN, 8, "CreateLayGet", 12, },
119 };
120 
121 /*
122  * NFS RPCS that have large request message size.
123  */
124 static int nfs_bigrequest[NFSV41_NPROCS] = {
125 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0
128 };
129 
130 /*
131  * Start building a request. Mostly just put the first file handle in
132  * place.
133  */
134 APPLESTATIC void
135 nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
136     u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp, struct nfsclsession *sep,
137     int vers, int minorvers)
138 {
139 	struct mbuf *mb;
140 	u_int32_t *tl;
141 	int opcnt;
142 	nfsattrbit_t attrbits;
143 
144 	/*
145 	 * First, fill in some of the fields of nd.
146 	 */
147 	nd->nd_slotseq = NULL;
148 	if (vers == NFS_VER4) {
149 		nd->nd_flag = ND_NFSV4 | ND_NFSCL;
150 		if (minorvers == NFSV41_MINORVERSION)
151 			nd->nd_flag |= ND_NFSV41;
152 	} else if (vers == NFS_VER3)
153 		nd->nd_flag = ND_NFSV3 | ND_NFSCL;
154 	else {
155 		if (NFSHASNFSV4(nmp)) {
156 			nd->nd_flag = ND_NFSV4 | ND_NFSCL;
157 			if (NFSHASNFSV4N(nmp))
158 				nd->nd_flag |= ND_NFSV41;
159 		} else if (NFSHASNFSV3(nmp))
160 			nd->nd_flag = ND_NFSV3 | ND_NFSCL;
161 		else
162 			nd->nd_flag = ND_NFSV2 | ND_NFSCL;
163 	}
164 	nd->nd_procnum = procnum;
165 	nd->nd_repstat = 0;
166 
167 	/*
168 	 * Get the first mbuf for the request.
169 	 */
170 	if (nfs_bigrequest[procnum])
171 		NFSMCLGET(mb, M_WAITOK);
172 	else
173 		NFSMGET(mb);
174 	mbuf_setlen(mb, 0);
175 	nd->nd_mreq = nd->nd_mb = mb;
176 	nd->nd_bpos = NFSMTOD(mb, caddr_t);
177 
178 	/*
179 	 * And fill the first file handle into the request.
180 	 */
181 	if (nd->nd_flag & ND_NFSV4) {
182 		opcnt = nfsv4_opmap[procnum].opcnt +
183 		    nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
184 		if ((nd->nd_flag & ND_NFSV41) != 0) {
185 			opcnt += nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq;
186 			if (procnum == NFSPROC_RENEW)
187 				/*
188 				 * For the special case of Renew, just do a
189 				 * Sequence Op.
190 				 */
191 				opcnt = 1;
192 			else if (procnum == NFSPROC_WRITEDS ||
193 			    procnum == NFSPROC_COMMITDS)
194 				/*
195 				 * For the special case of a Writeor Commit to
196 				 * a DS, the opcnt == 3, for Sequence, PutFH,
197 				 * Write/Commit.
198 				 */
199 				opcnt = 3;
200 		}
201 		/*
202 		 * What should the tag really be?
203 		 */
204 		(void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
205 			nfsv4_opmap[procnum].taglen);
206 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
207 		if ((nd->nd_flag & ND_NFSV41) != 0)
208 			*tl++ = txdr_unsigned(NFSV41_MINORVERSION);
209 		else
210 			*tl++ = txdr_unsigned(NFSV4_MINORVERSION);
211 		if (opcntpp != NULL)
212 			*opcntpp = tl;
213 		*tl = txdr_unsigned(opcnt);
214 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
215 		    nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq > 0) {
216 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].loopbadsess >
217 			    0)
218 				nd->nd_flag |= ND_LOOPBADSESS;
219 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
220 			*tl = txdr_unsigned(NFSV4OP_SEQUENCE);
221 			if (sep == NULL) {
222 				sep = nfsmnt_mdssession(nmp);
223 				nfsv4_setsequence(nmp, nd, sep,
224 				    nfs_bigreply[procnum]);
225 			} else
226 				nfsv4_setsequence(nmp, nd, sep,
227 				    nfs_bigreply[procnum]);
228 		}
229 		if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
230 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
231 			*tl = txdr_unsigned(NFSV4OP_PUTFH);
232 			(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
233 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh
234 			    == 2 && procnum != NFSPROC_WRITEDS &&
235 			    procnum != NFSPROC_COMMITDS) {
236 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
237 				*tl = txdr_unsigned(NFSV4OP_GETATTR);
238 				/*
239 				 * For Lookup Ops, we want all the directory
240 				 * attributes, so we can load the name cache.
241 				 */
242 				if (procnum == NFSPROC_LOOKUP ||
243 				    procnum == NFSPROC_LOOKUPP)
244 					NFSGETATTR_ATTRBIT(&attrbits);
245 				else {
246 					NFSWCCATTR_ATTRBIT(&attrbits);
247 					nd->nd_flag |= ND_V4WCCATTR;
248 				}
249 				(void) nfsrv_putattrbit(nd, &attrbits);
250 			}
251 		}
252 		if (procnum != NFSPROC_RENEW ||
253 		    (nd->nd_flag & ND_NFSV41) == 0) {
254 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
255 			*tl = txdr_unsigned(nfsv4_opmap[procnum].op);
256 		}
257 	} else {
258 		(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
259 	}
260 	if (procnum < NFSV41_NPROCS)
261 		NFSINCRGLOBAL(nfsstatsv1.rpccnt[procnum]);
262 }
263 
264 /*
265  * copies a uio scatter/gather list to an mbuf chain.
266  * NOTE: can ony handle iovcnt == 1
267  */
268 APPLESTATIC void
269 nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
270 {
271 	char *uiocp;
272 	struct mbuf *mp, *mp2;
273 	int xfer, left, mlen;
274 	int uiosiz, clflg, rem;
275 	char *cp, *tcp;
276 
277 	KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
278 
279 	if (siz > ncl_mbuf_mlen)	/* or should it >= MCLBYTES ?? */
280 		clflg = 1;
281 	else
282 		clflg = 0;
283 	rem = NFSM_RNDUP(siz) - siz;
284 	mp = mp2 = nd->nd_mb;
285 	while (siz > 0) {
286 		left = uiop->uio_iov->iov_len;
287 		uiocp = uiop->uio_iov->iov_base;
288 		if (left > siz)
289 			left = siz;
290 		uiosiz = left;
291 		while (left > 0) {
292 			mlen = M_TRAILINGSPACE(mp);
293 			if (mlen == 0) {
294 				if (clflg)
295 					NFSMCLGET(mp, M_WAITOK);
296 				else
297 					NFSMGET(mp);
298 				mbuf_setlen(mp, 0);
299 				mbuf_setnext(mp2, mp);
300 				mp2 = mp;
301 				mlen = M_TRAILINGSPACE(mp);
302 			}
303 			xfer = (left > mlen) ? mlen : left;
304 #ifdef notdef
305 			/* Not Yet.. */
306 			if (uiop->uio_iov->iov_op != NULL)
307 				(*(uiop->uio_iov->iov_op))
308 				(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
309 				    xfer);
310 			else
311 #endif
312 			if (uiop->uio_segflg == UIO_SYSSPACE)
313 			    NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
314 				xfer);
315 			else
316 			    copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
317 				+ mbuf_len(mp), xfer);
318 			mbuf_setlen(mp, mbuf_len(mp) + xfer);
319 			left -= xfer;
320 			uiocp += xfer;
321 			uiop->uio_offset += xfer;
322 			uiop->uio_resid -= xfer;
323 		}
324 		tcp = (char *)uiop->uio_iov->iov_base;
325 		tcp += uiosiz;
326 		uiop->uio_iov->iov_base = (void *)tcp;
327 		uiop->uio_iov->iov_len -= uiosiz;
328 		siz -= uiosiz;
329 	}
330 	if (rem > 0) {
331 		if (rem > M_TRAILINGSPACE(mp)) {
332 			NFSMGET(mp);
333 			mbuf_setlen(mp, 0);
334 			mbuf_setnext(mp2, mp);
335 		}
336 		cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
337 		for (left = 0; left < rem; left++)
338 			*cp++ = '\0';
339 		mbuf_setlen(mp, mbuf_len(mp) + rem);
340 		nd->nd_bpos = cp;
341 	} else
342 		nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
343 	nd->nd_mb = mp;
344 }
345 
346 /*
347  * copies a uio scatter/gather list to an mbuf chain.
348  * This version returns the mbuf list and does not use "nd".
349  * NOTE: can ony handle iovcnt == 1
350  */
351 struct mbuf *
352 nfsm_uiombuflist(struct uio *uiop, int siz, struct mbuf **mbp, char **cpp)
353 {
354 	char *uiocp;
355 	struct mbuf *mp, *mp2, *firstmp;
356 	int xfer, left, mlen;
357 	int uiosiz, clflg;
358 	char *tcp;
359 
360 	KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
361 
362 	if (siz > ncl_mbuf_mlen)	/* or should it >= MCLBYTES ?? */
363 		clflg = 1;
364 	else
365 		clflg = 0;
366 	if (clflg != 0)
367 		NFSMCLGET(mp, M_WAITOK);
368 	else
369 		NFSMGET(mp);
370 	mbuf_setlen(mp, 0);
371 	firstmp = mp2 = mp;
372 	while (siz > 0) {
373 		left = uiop->uio_iov->iov_len;
374 		uiocp = uiop->uio_iov->iov_base;
375 		if (left > siz)
376 			left = siz;
377 		uiosiz = left;
378 		while (left > 0) {
379 			mlen = M_TRAILINGSPACE(mp);
380 			if (mlen == 0) {
381 				if (clflg)
382 					NFSMCLGET(mp, M_WAITOK);
383 				else
384 					NFSMGET(mp);
385 				mbuf_setlen(mp, 0);
386 				mbuf_setnext(mp2, mp);
387 				mp2 = mp;
388 				mlen = M_TRAILINGSPACE(mp);
389 			}
390 			xfer = (left > mlen) ? mlen : left;
391 			if (uiop->uio_segflg == UIO_SYSSPACE)
392 				NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) +
393 				    mbuf_len(mp), xfer);
394 			else
395 				copyin(uiocp, NFSMTOD(mp, caddr_t) +
396 				    mbuf_len(mp), xfer);
397 			mbuf_setlen(mp, mbuf_len(mp) + xfer);
398 			left -= xfer;
399 			uiocp += xfer;
400 			uiop->uio_offset += xfer;
401 			uiop->uio_resid -= xfer;
402 		}
403 		tcp = (char *)uiop->uio_iov->iov_base;
404 		tcp += uiosiz;
405 		uiop->uio_iov->iov_base = (void *)tcp;
406 		uiop->uio_iov->iov_len -= uiosiz;
407 		siz -= uiosiz;
408 	}
409 	if (cpp != NULL)
410 		*cpp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
411 	if (mbp != NULL)
412 		*mbp = mp;
413 	return (firstmp);
414 }
415 
416 /*
417  * Load vnode attributes from the xdr file attributes.
418  * Returns EBADRPC if they can't be parsed, 0 otherwise.
419  */
420 APPLESTATIC int
421 nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
422 {
423 	struct nfs_fattr *fp;
424 	int error = 0;
425 
426 	if (nd->nd_flag & ND_NFSV4) {
427 		error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
428 		    NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
429 	} else if (nd->nd_flag & ND_NFSV3) {
430 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
431 		nap->na_type = nfsv34tov_type(fp->fa_type);
432 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
433 		nap->na_rdev = NFSMAKEDEV(
434 		    fxdr_unsigned(int, fp->fa3_rdev.specdata1),
435 		    fxdr_unsigned(int, fp->fa3_rdev.specdata2));
436 		nap->na_nlink = fxdr_unsigned(uint32_t, fp->fa_nlink);
437 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
438 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
439 		nap->na_size = fxdr_hyper(&fp->fa3_size);
440 		nap->na_blocksize = NFS_FABLKSIZE;
441 		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
442 		nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
443 		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
444 		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
445 		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
446 		nap->na_flags = 0;
447 		nap->na_filerev = 0;
448 	} else {
449 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
450 		nap->na_type = nfsv2tov_type(fp->fa_type);
451 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
452 		if (nap->na_type == VNON || nap->na_type == VREG)
453 			nap->na_type = IFTOVT(nap->na_mode);
454 		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
455 
456 		/*
457 		 * Really ugly NFSv2 kludge.
458 		 */
459 		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
460 			nap->na_type = VFIFO;
461 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
462 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
463 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
464 		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
465 		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
466 		nap->na_bytes =
467 		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
468 		    NFS_FABLKSIZE;
469 		nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
470 		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
471 		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
472 		nap->na_flags = 0;
473 		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
474 		    fp->fa2_ctime.nfsv2_sec);
475 		nap->na_ctime.tv_nsec = 0;
476 		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
477 		nap->na_filerev = 0;
478 	}
479 nfsmout:
480 	return (error);
481 }
482 
483 /*
484  * This function finds the directory cookie that corresponds to the
485  * logical byte offset given.
486  */
487 APPLESTATIC nfsuint64 *
488 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
489 {
490 	struct nfsdmap *dp, *dp2;
491 	int pos;
492 
493 	pos = off / NFS_DIRBLKSIZ;
494 	if (pos == 0) {
495 		KASSERT(!add, ("nfs getcookie add at 0"));
496 		return (&nfs_nullcookie);
497 	}
498 	pos--;
499 	dp = LIST_FIRST(&np->n_cookies);
500 	if (!dp) {
501 		if (add) {
502 			dp = malloc(sizeof (struct nfsdmap),
503 				M_NFSDIROFF, M_WAITOK);
504 			dp->ndm_eocookie = 0;
505 			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
506 		} else
507 			return (NULL);
508 	}
509 	while (pos >= NFSNUMCOOKIES) {
510 		pos -= NFSNUMCOOKIES;
511 		if (LIST_NEXT(dp, ndm_list) != NULL) {
512 			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
513 				pos >= dp->ndm_eocookie)
514 				return (NULL);
515 			dp = LIST_NEXT(dp, ndm_list);
516 		} else if (add) {
517 			dp2 = malloc(sizeof (struct nfsdmap),
518 				M_NFSDIROFF, M_WAITOK);
519 			dp2->ndm_eocookie = 0;
520 			LIST_INSERT_AFTER(dp, dp2, ndm_list);
521 			dp = dp2;
522 		} else
523 			return (NULL);
524 	}
525 	if (pos >= dp->ndm_eocookie) {
526 		if (add)
527 			dp->ndm_eocookie = pos + 1;
528 		else
529 			return (NULL);
530 	}
531 	return (&dp->ndm_cookies[pos]);
532 }
533 
534 /*
535  * Gets a file handle out of an nfs reply sent to the client and returns
536  * the file handle and the file's attributes.
537  * For V4, it assumes that Getfh and Getattr Op's results are here.
538  */
539 APPLESTATIC int
540 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
541     struct nfsvattr *nap, int *attrflagp)
542 {
543 	u_int32_t *tl;
544 	int error = 0, flag = 1;
545 
546 	*nfhpp = NULL;
547 	*attrflagp = 0;
548 	/*
549 	 * First get the file handle and vnode.
550 	 */
551 	if (nd->nd_flag & ND_NFSV3) {
552 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
553 		flag = fxdr_unsigned(int, *tl);
554 	} else if (nd->nd_flag & ND_NFSV4) {
555 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
556 		/* If the GetFH failed, clear flag. */
557 		if (*++tl != 0) {
558 			nd->nd_flag |= ND_NOMOREDATA;
559 			flag = 0;
560 			error = ENXIO;	/* Return ENXIO so *nfhpp isn't used. */
561 		}
562 	}
563 	if (flag) {
564 		error = nfsm_getfh(nd, nfhpp);
565 		if (error)
566 			return (error);
567 	}
568 
569 	/*
570 	 * Now, get the attributes.
571 	 */
572 	if (flag != 0 && (nd->nd_flag & ND_NFSV4) != 0) {
573 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
574 		if (*++tl != 0) {
575 			nd->nd_flag |= ND_NOMOREDATA;
576 			flag = 0;
577 		}
578 	} else if (nd->nd_flag & ND_NFSV3) {
579 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
580 		if (flag) {
581 			flag = fxdr_unsigned(int, *tl);
582 		} else if (fxdr_unsigned(int, *tl)) {
583 			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
584 			if (error)
585 				return (error);
586 		}
587 	}
588 	if (flag) {
589 		error = nfsm_loadattr(nd, nap);
590 		if (!error)
591 			*attrflagp = 1;
592 	}
593 nfsmout:
594 	return (error);
595 }
596 
597 /*
598  * Put a state Id in the mbuf list.
599  */
600 APPLESTATIC void
601 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
602 {
603 	nfsv4stateid_t *st;
604 
605 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
606 	if (flag == NFSSTATEID_PUTALLZERO) {
607 		st->seqid = 0;
608 		st->other[0] = 0;
609 		st->other[1] = 0;
610 		st->other[2] = 0;
611 	} else if (flag == NFSSTATEID_PUTALLONE) {
612 		st->seqid = 0xffffffff;
613 		st->other[0] = 0xffffffff;
614 		st->other[1] = 0xffffffff;
615 		st->other[2] = 0xffffffff;
616 	} else if (flag == NFSSTATEID_PUTSEQIDZERO) {
617 		st->seqid = 0;
618 		st->other[0] = stateidp->other[0];
619 		st->other[1] = stateidp->other[1];
620 		st->other[2] = stateidp->other[2];
621 	} else {
622 		st->seqid = stateidp->seqid;
623 		st->other[0] = stateidp->other[0];
624 		st->other[1] = stateidp->other[1];
625 		st->other[2] = stateidp->other[2];
626 	}
627 }
628 
629 /*
630  * Initialize the owner/delegation sleep lock.
631  */
632 APPLESTATIC void
633 nfscl_lockinit(struct nfsv4lock *lckp)
634 {
635 
636 	lckp->nfslock_usecnt = 0;
637 	lckp->nfslock_lock = 0;
638 }
639 
640 /*
641  * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
642  * thread for each posix process in the kernel.)
643  */
644 APPLESTATIC void
645 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
646 {
647 	int igotlock;
648 
649 	do {
650 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
651 	} while (!igotlock);
652 }
653 
654 /*
655  * Release an exclusive lock.
656  */
657 APPLESTATIC void
658 nfscl_lockunlock(struct nfsv4lock *lckp)
659 {
660 
661 	nfsv4_unlock(lckp, 0);
662 }
663 
664 /*
665  * Called to derefernce a lock on a stateid (delegation or open owner).
666  */
667 APPLESTATIC void
668 nfscl_lockderef(struct nfsv4lock *lckp)
669 {
670 
671 	NFSLOCKCLSTATE();
672 	lckp->nfslock_usecnt--;
673 	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
674 		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
675 		wakeup((caddr_t)lckp);
676 	}
677 	NFSUNLOCKCLSTATE();
678 }
679 
680