xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision a665699823a2099a451e1c9c1505ef1cbf29c914)
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 = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
434 			fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
435 		nap->na_nlink = fxdr_unsigned(uint32_t, fp->fa_nlink);
436 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
437 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
438 		nap->na_size = fxdr_hyper(&fp->fa3_size);
439 		nap->na_blocksize = NFS_FABLKSIZE;
440 		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
441 		nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
442 		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
443 		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
444 		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
445 		nap->na_flags = 0;
446 		nap->na_filerev = 0;
447 	} else {
448 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
449 		nap->na_type = nfsv2tov_type(fp->fa_type);
450 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
451 		if (nap->na_type == VNON || nap->na_type == VREG)
452 			nap->na_type = IFTOVT(nap->na_mode);
453 		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
454 
455 		/*
456 		 * Really ugly NFSv2 kludge.
457 		 */
458 		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
459 			nap->na_type = VFIFO;
460 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
461 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
462 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
463 		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
464 		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
465 		nap->na_bytes =
466 		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
467 		    NFS_FABLKSIZE;
468 		nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
469 		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
470 		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
471 		nap->na_flags = 0;
472 		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
473 		    fp->fa2_ctime.nfsv2_sec);
474 		nap->na_ctime.tv_nsec = 0;
475 		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
476 		nap->na_filerev = 0;
477 	}
478 nfsmout:
479 	return (error);
480 }
481 
482 /*
483  * This function finds the directory cookie that corresponds to the
484  * logical byte offset given.
485  */
486 APPLESTATIC nfsuint64 *
487 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
488 {
489 	struct nfsdmap *dp, *dp2;
490 	int pos;
491 
492 	pos = off / NFS_DIRBLKSIZ;
493 	if (pos == 0) {
494 		KASSERT(!add, ("nfs getcookie add at 0"));
495 		return (&nfs_nullcookie);
496 	}
497 	pos--;
498 	dp = LIST_FIRST(&np->n_cookies);
499 	if (!dp) {
500 		if (add) {
501 			dp = malloc(sizeof (struct nfsdmap),
502 				M_NFSDIROFF, M_WAITOK);
503 			dp->ndm_eocookie = 0;
504 			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
505 		} else
506 			return (NULL);
507 	}
508 	while (pos >= NFSNUMCOOKIES) {
509 		pos -= NFSNUMCOOKIES;
510 		if (LIST_NEXT(dp, ndm_list) != NULL) {
511 			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
512 				pos >= dp->ndm_eocookie)
513 				return (NULL);
514 			dp = LIST_NEXT(dp, ndm_list);
515 		} else if (add) {
516 			dp2 = malloc(sizeof (struct nfsdmap),
517 				M_NFSDIROFF, M_WAITOK);
518 			dp2->ndm_eocookie = 0;
519 			LIST_INSERT_AFTER(dp, dp2, ndm_list);
520 			dp = dp2;
521 		} else
522 			return (NULL);
523 	}
524 	if (pos >= dp->ndm_eocookie) {
525 		if (add)
526 			dp->ndm_eocookie = pos + 1;
527 		else
528 			return (NULL);
529 	}
530 	return (&dp->ndm_cookies[pos]);
531 }
532 
533 /*
534  * Gets a file handle out of an nfs reply sent to the client and returns
535  * the file handle and the file's attributes.
536  * For V4, it assumes that Getfh and Getattr Op's results are here.
537  */
538 APPLESTATIC int
539 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
540     struct nfsvattr *nap, int *attrflagp)
541 {
542 	u_int32_t *tl;
543 	int error = 0, flag = 1;
544 
545 	*nfhpp = NULL;
546 	*attrflagp = 0;
547 	/*
548 	 * First get the file handle and vnode.
549 	 */
550 	if (nd->nd_flag & ND_NFSV3) {
551 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
552 		flag = fxdr_unsigned(int, *tl);
553 	} else if (nd->nd_flag & ND_NFSV4) {
554 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
555 		/* If the GetFH failed, clear flag. */
556 		if (*++tl != 0) {
557 			nd->nd_flag |= ND_NOMOREDATA;
558 			flag = 0;
559 			error = ENXIO;	/* Return ENXIO so *nfhpp isn't used. */
560 		}
561 	}
562 	if (flag) {
563 		error = nfsm_getfh(nd, nfhpp);
564 		if (error)
565 			return (error);
566 	}
567 
568 	/*
569 	 * Now, get the attributes.
570 	 */
571 	if (flag != 0 && (nd->nd_flag & ND_NFSV4) != 0) {
572 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
573 		if (*++tl != 0) {
574 			nd->nd_flag |= ND_NOMOREDATA;
575 			flag = 0;
576 		}
577 	} else if (nd->nd_flag & ND_NFSV3) {
578 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
579 		if (flag) {
580 			flag = fxdr_unsigned(int, *tl);
581 		} else if (fxdr_unsigned(int, *tl)) {
582 			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
583 			if (error)
584 				return (error);
585 		}
586 	}
587 	if (flag) {
588 		error = nfsm_loadattr(nd, nap);
589 		if (!error)
590 			*attrflagp = 1;
591 	}
592 nfsmout:
593 	return (error);
594 }
595 
596 /*
597  * Put a state Id in the mbuf list.
598  */
599 APPLESTATIC void
600 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
601 {
602 	nfsv4stateid_t *st;
603 
604 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
605 	if (flag == NFSSTATEID_PUTALLZERO) {
606 		st->seqid = 0;
607 		st->other[0] = 0;
608 		st->other[1] = 0;
609 		st->other[2] = 0;
610 	} else if (flag == NFSSTATEID_PUTALLONE) {
611 		st->seqid = 0xffffffff;
612 		st->other[0] = 0xffffffff;
613 		st->other[1] = 0xffffffff;
614 		st->other[2] = 0xffffffff;
615 	} else if (flag == NFSSTATEID_PUTSEQIDZERO) {
616 		st->seqid = 0;
617 		st->other[0] = stateidp->other[0];
618 		st->other[1] = stateidp->other[1];
619 		st->other[2] = stateidp->other[2];
620 	} else {
621 		st->seqid = stateidp->seqid;
622 		st->other[0] = stateidp->other[0];
623 		st->other[1] = stateidp->other[1];
624 		st->other[2] = stateidp->other[2];
625 	}
626 }
627 
628 /*
629  * Initialize the owner/delegation sleep lock.
630  */
631 APPLESTATIC void
632 nfscl_lockinit(struct nfsv4lock *lckp)
633 {
634 
635 	lckp->nfslock_usecnt = 0;
636 	lckp->nfslock_lock = 0;
637 }
638 
639 /*
640  * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
641  * thread for each posix process in the kernel.)
642  */
643 APPLESTATIC void
644 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
645 {
646 	int igotlock;
647 
648 	do {
649 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
650 	} while (!igotlock);
651 }
652 
653 /*
654  * Release an exclusive lock.
655  */
656 APPLESTATIC void
657 nfscl_lockunlock(struct nfsv4lock *lckp)
658 {
659 
660 	nfsv4_unlock(lckp, 0);
661 }
662 
663 /*
664  * Called to derefernce a lock on a stateid (delegation or open owner).
665  */
666 APPLESTATIC void
667 nfscl_lockderef(struct nfsv4lock *lckp)
668 {
669 
670 	NFSLOCKCLSTATE();
671 	lckp->nfslock_usecnt--;
672 	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
673 		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
674 		wakeup((caddr_t)lckp);
675 	}
676 	NFSUNLOCKCLSTATE();
677 }
678 
679