xref: /freebsd/sys/fs/nfs/nfs_commonsubs.c (revision 6ae1554a5d9b318f8ad53ccc39fa5a961403da73)
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 "opt_inet6.h"
44 
45 #include <fs/nfs/nfsport.h>
46 
47 /*
48  * Data items converted to xdr at startup, since they are constant
49  * This is kinda hokey, but may save a little time doing byte swaps
50  */
51 u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
52 
53 /* And other global data */
54 nfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
55 		      NFFIFO, NFNON };
56 enum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
57 enum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
58 struct timeval nfsboottime;	/* Copy boottime once, so it never changes */
59 int nfscl_ticks;
60 int nfsrv_useacl = 1;
61 struct nfssockreq nfsrv_nfsuserdsock;
62 int nfsrv_nfsuserd = 0;
63 struct nfsreqhead nfsd_reqq;
64 uid_t nfsrv_defaultuid;
65 gid_t nfsrv_defaultgid;
66 int nfsrv_lease = NFSRV_LEASE;
67 int ncl_mbuf_mlen = MLEN;
68 int nfsd_enable_stringtouid = 0;
69 NFSNAMEIDMUTEX;
70 NFSSOCKMUTEX;
71 
72 /*
73  * This array of structures indicates, for V4:
74  * retfh - which of 3 types of calling args are used
75  *	0 - doesn't change cfh or use a sfh
76  *	1 - replaces cfh with a new one (unless it returns an error status)
77  *	2 - uses cfh and sfh
78  * needscfh - if the op wants a cfh and premtime
79  *	0 - doesn't use a cfh
80  *	1 - uses a cfh, but doesn't want pre-op attributes
81  *	2 - uses a cfh and wants pre-op attributes
82  * savereply - indicates a non-idempotent Op
83  *	0 - not non-idempotent
84  *	1 - non-idempotent
85  * Ops that are ordered via seqid# are handled separately from these
86  * non-idempotent Ops.
87  * Define it here, since it is used by both the client and server.
88  */
89 struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS] = {
90 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
91 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
92 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
93 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Access */
94 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Close */
95 	{ 0, 2, 0, 1, LK_EXCLUSIVE, 1 },		/* Commit */
96 	{ 1, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Create */
97 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegpurge */
98 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegreturn */
99 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Getattr */
100 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* GetFH */
101 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Link */
102 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Lock */
103 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockT */
104 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockU */
105 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookup */
106 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookupp */
107 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* NVerify */
108 	{ 1, 1, 0, 1, LK_EXCLUSIVE, 1 },		/* Open */
109 	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenAttr */
110 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenConfirm */
111 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenDowngrade */
112 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutFH */
113 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutPubFH */
114 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutRootFH */
115 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Read */
116 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Readdir */
117 	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* ReadLink */
118 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Remove */
119 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Rename */
120 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Renew */
121 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* RestoreFH */
122 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SaveFH */
123 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SecInfo */
124 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Setattr */
125 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientID */
126 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientIDConfirm */
127 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Verify */
128 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Write */
129 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* ReleaseLockOwner */
130 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Backchannel Ctrl */
131 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Bind Conn to Sess */
132 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Exchange ID */
133 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Create Session */
134 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy Session */
135 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Free StateID */
136 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Dir Deleg */
137 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device Info */
138 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device List */
139 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Commit */
140 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Get */
141 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Return */
142 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Secinfo No name */
143 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Sequence */
144 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Set SSV */
145 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Test StateID */
146 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Want Delegation */
147 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy ClientID */
148 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Reclaim Complete */
149 };
150 #endif	/* !APPLEKEXT */
151 
152 static int ncl_mbuf_mhlen = MHLEN;
153 static int nfsrv_usercnt = 0;
154 static int nfsrv_dnsnamelen;
155 static u_char *nfsrv_dnsname = NULL;
156 static int nfsrv_usermax = 999999999;
157 static struct nfsuserhashhead nfsuserhash[NFSUSERHASHSIZE];
158 static struct nfsuserhashhead nfsusernamehash[NFSUSERHASHSIZE];
159 static struct nfsuserhashhead nfsgrouphash[NFSGROUPHASHSIZE];
160 static struct nfsuserhashhead nfsgroupnamehash[NFSGROUPHASHSIZE];
161 static struct nfsuserlruhead nfsuserlruhead;
162 
163 /*
164  * This static array indicates whether or not the RPC generates a large
165  * reply. This is used by nfs_reply() to decide whether or not an mbuf
166  * cluster should be allocated. (If a cluster is required by an RPC
167  * marked 0 in this array, the code will still work, just not quite as
168  * efficiently.)
169  */
170 int nfs_bigreply[NFSV41_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
171     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
172     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
173 
174 /* local functions */
175 static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
176 static void nfsv4_wanted(struct nfsv4lock *lp);
177 static int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
178 static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
179     NFSPROC_T *p);
180 static void nfsrv_removeuser(struct nfsusrgrp *usrp);
181 static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
182     int *, int *);
183 static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
184 
185 
186 #ifndef APPLE
187 /*
188  * copies mbuf chain to the uio scatter/gather list
189  */
190 int
191 nfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
192 {
193 	char *mbufcp, *uiocp;
194 	int xfer, left, len;
195 	mbuf_t mp;
196 	long uiosiz, rem;
197 	int error = 0;
198 
199 	mp = nd->nd_md;
200 	mbufcp = nd->nd_dpos;
201 	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - mbufcp;
202 	rem = NFSM_RNDUP(siz) - siz;
203 	while (siz > 0) {
204 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) {
205 			error = EBADRPC;
206 			goto out;
207 		}
208 		left = uiop->uio_iov->iov_len;
209 		uiocp = uiop->uio_iov->iov_base;
210 		if (left > siz)
211 			left = siz;
212 		uiosiz = left;
213 		while (left > 0) {
214 			while (len == 0) {
215 				mp = mbuf_next(mp);
216 				if (mp == NULL) {
217 					error = EBADRPC;
218 					goto out;
219 				}
220 				mbufcp = NFSMTOD(mp, caddr_t);
221 				len = mbuf_len(mp);
222 				KASSERT(len >= 0,
223 				    ("len %d, corrupted mbuf?", len));
224 			}
225 			xfer = (left > len) ? len : left;
226 #ifdef notdef
227 			/* Not Yet.. */
228 			if (uiop->uio_iov->iov_op != NULL)
229 				(*(uiop->uio_iov->iov_op))
230 				(mbufcp, uiocp, xfer);
231 			else
232 #endif
233 			if (uiop->uio_segflg == UIO_SYSSPACE)
234 				NFSBCOPY(mbufcp, uiocp, xfer);
235 			else
236 				copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
237 			left -= xfer;
238 			len -= xfer;
239 			mbufcp += xfer;
240 			uiocp += xfer;
241 			uiop->uio_offset += xfer;
242 			uiop->uio_resid -= xfer;
243 		}
244 		if (uiop->uio_iov->iov_len <= siz) {
245 			uiop->uio_iovcnt--;
246 			uiop->uio_iov++;
247 		} else {
248 			uiop->uio_iov->iov_base = (void *)
249 				((char *)uiop->uio_iov->iov_base + uiosiz);
250 			uiop->uio_iov->iov_len -= uiosiz;
251 		}
252 		siz -= uiosiz;
253 	}
254 	nd->nd_dpos = mbufcp;
255 	nd->nd_md = mp;
256 	if (rem > 0) {
257 		if (len < rem)
258 			error = nfsm_advance(nd, rem, len);
259 		else
260 			nd->nd_dpos += rem;
261 	}
262 
263 out:
264 	NFSEXITCODE2(error, nd);
265 	return (error);
266 }
267 #endif	/* !APPLE */
268 
269 /*
270  * Help break down an mbuf chain by setting the first siz bytes contiguous
271  * pointed to by returned val.
272  * This is used by the macro NFSM_DISSECT for tough
273  * cases.
274  */
275 APPLESTATIC void *
276 nfsm_dissct(struct nfsrv_descript *nd, int siz, int how)
277 {
278 	mbuf_t mp2;
279 	int siz2, xfer;
280 	caddr_t p;
281 	int left;
282 	caddr_t retp;
283 
284 	retp = NULL;
285 	left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
286 	while (left == 0) {
287 		nd->nd_md = mbuf_next(nd->nd_md);
288 		if (nd->nd_md == NULL)
289 			return (retp);
290 		left = mbuf_len(nd->nd_md);
291 		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
292 	}
293 	if (left >= siz) {
294 		retp = nd->nd_dpos;
295 		nd->nd_dpos += siz;
296 	} else if (mbuf_next(nd->nd_md) == NULL) {
297 		return (retp);
298 	} else if (siz > ncl_mbuf_mhlen) {
299 		panic("nfs S too big");
300 	} else {
301 		MGET(mp2, MT_DATA, how);
302 		if (mp2 == NULL)
303 			return (NULL);
304 		mbuf_setnext(mp2, mbuf_next(nd->nd_md));
305 		mbuf_setnext(nd->nd_md, mp2);
306 		mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
307 		nd->nd_md = mp2;
308 		retp = p = NFSMTOD(mp2, caddr_t);
309 		NFSBCOPY(nd->nd_dpos, p, left);	/* Copy what was left */
310 		siz2 = siz - left;
311 		p += left;
312 		mp2 = mbuf_next(mp2);
313 		/* Loop around copying up the siz2 bytes */
314 		while (siz2 > 0) {
315 			if (mp2 == NULL)
316 				return (NULL);
317 			xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
318 			if (xfer > 0) {
319 				NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
320 				NFSM_DATAP(mp2, xfer);
321 				mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
322 				p += xfer;
323 				siz2 -= xfer;
324 			}
325 			if (siz2 > 0)
326 				mp2 = mbuf_next(mp2);
327 		}
328 		mbuf_setlen(nd->nd_md, siz);
329 		nd->nd_md = mp2;
330 		nd->nd_dpos = NFSMTOD(mp2, caddr_t);
331 	}
332 	return (retp);
333 }
334 
335 /*
336  * Advance the position in the mbuf chain.
337  * If offs == 0, this is a no-op, but it is simpler to just return from
338  * here than check for offs > 0 for all calls to nfsm_advance.
339  * If left == -1, it should be calculated here.
340  */
341 APPLESTATIC int
342 nfsm_advance(struct nfsrv_descript *nd, int offs, int left)
343 {
344 	int error = 0;
345 
346 	if (offs == 0)
347 		goto out;
348 	/*
349 	 * A negative offs should be considered a serious problem.
350 	 */
351 	if (offs < 0)
352 		panic("nfsrv_advance");
353 
354 	/*
355 	 * If left == -1, calculate it here.
356 	 */
357 	if (left == -1)
358 		left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
359 		    nd->nd_dpos;
360 
361 	/*
362 	 * Loop around, advancing over the mbuf data.
363 	 */
364 	while (offs > left) {
365 		offs -= left;
366 		nd->nd_md = mbuf_next(nd->nd_md);
367 		if (nd->nd_md == NULL) {
368 			error = EBADRPC;
369 			goto out;
370 		}
371 		left = mbuf_len(nd->nd_md);
372 		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
373 	}
374 	nd->nd_dpos += offs;
375 
376 out:
377 	NFSEXITCODE(error);
378 	return (error);
379 }
380 
381 /*
382  * Copy a string into mbuf(s).
383  * Return the number of bytes output, including XDR overheads.
384  */
385 APPLESTATIC int
386 nfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
387 {
388 	mbuf_t m2;
389 	int xfer, left;
390 	mbuf_t m1;
391 	int rem, bytesize;
392 	u_int32_t *tl;
393 	char *cp2;
394 
395 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
396 	*tl = txdr_unsigned(siz);
397 	rem = NFSM_RNDUP(siz) - siz;
398 	bytesize = NFSX_UNSIGNED + siz + rem;
399 	m2 = nd->nd_mb;
400 	cp2 = nd->nd_bpos;
401 	left = M_TRAILINGSPACE(m2);
402 
403 	/*
404 	 * Loop around copying the string to mbuf(s).
405 	 */
406 	while (siz > 0) {
407 		if (left == 0) {
408 			if (siz > ncl_mbuf_mlen)
409 				NFSMCLGET(m1, M_WAITOK);
410 			else
411 				NFSMGET(m1);
412 			mbuf_setlen(m1, 0);
413 			mbuf_setnext(m2, m1);
414 			m2 = m1;
415 			cp2 = NFSMTOD(m2, caddr_t);
416 			left = M_TRAILINGSPACE(m2);
417 		}
418 		if (left >= siz)
419 			xfer = siz;
420 		else
421 			xfer = left;
422 		NFSBCOPY(cp, cp2, xfer);
423 		cp += xfer;
424 		mbuf_setlen(m2, mbuf_len(m2) + xfer);
425 		siz -= xfer;
426 		left -= xfer;
427 		if (siz == 0 && rem) {
428 			if (left < rem)
429 				panic("nfsm_strtom");
430 			NFSBZERO(cp2 + xfer, rem);
431 			mbuf_setlen(m2, mbuf_len(m2) + rem);
432 		}
433 	}
434 	nd->nd_mb = m2;
435 	nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
436 	return (bytesize);
437 }
438 
439 /*
440  * Called once to initialize data structures...
441  */
442 APPLESTATIC void
443 newnfs_init(void)
444 {
445 	static int nfs_inited = 0;
446 
447 	if (nfs_inited)
448 		return;
449 	nfs_inited = 1;
450 
451 	newnfs_true = txdr_unsigned(TRUE);
452 	newnfs_false = txdr_unsigned(FALSE);
453 	newnfs_xdrneg1 = txdr_unsigned(-1);
454 	nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
455 	if (nfscl_ticks < 1)
456 		nfscl_ticks = 1;
457 	NFSSETBOOTTIME(nfsboottime);
458 
459 	/*
460 	 * Initialize reply list and start timer
461 	 */
462 	TAILQ_INIT(&nfsd_reqq);
463 	NFS_TIMERINIT;
464 }
465 
466 /*
467  * Put a file handle in an mbuf list.
468  * If the size argument == 0, just use the default size.
469  * set_true == 1 if there should be an newnfs_true prepended on the file handle.
470  * Return the number of bytes output, including XDR overhead.
471  */
472 APPLESTATIC int
473 nfsm_fhtom(struct nfsrv_descript *nd, u_int8_t *fhp, int size, int set_true)
474 {
475 	u_int32_t *tl;
476 	u_int8_t *cp;
477 	int fullsiz, rem, bytesize = 0;
478 
479 	if (size == 0)
480 		size = NFSX_MYFH;
481 	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
482 	case ND_NFSV2:
483 		if (size > NFSX_V2FH)
484 			panic("fh size > NFSX_V2FH for NFSv2");
485 		NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
486 		NFSBCOPY(fhp, cp, size);
487 		if (size < NFSX_V2FH)
488 			NFSBZERO(cp + size, NFSX_V2FH - size);
489 		bytesize = NFSX_V2FH;
490 		break;
491 	case ND_NFSV3:
492 	case ND_NFSV4:
493 		fullsiz = NFSM_RNDUP(size);
494 		rem = fullsiz - size;
495 		if (set_true) {
496 		    bytesize = 2 * NFSX_UNSIGNED + fullsiz;
497 		    NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
498 		    *tl = newnfs_true;
499 		} else {
500 		    bytesize = NFSX_UNSIGNED + fullsiz;
501 		}
502 		(void) nfsm_strtom(nd, fhp, size);
503 		break;
504 	};
505 	return (bytesize);
506 }
507 
508 /*
509  * This function compares two net addresses by family and returns TRUE
510  * if they are the same host.
511  * If there is any doubt, return FALSE.
512  * The AF_INET family is handled as a special case so that address mbufs
513  * don't need to be saved to store "struct in_addr", which is only 4 bytes.
514  */
515 APPLESTATIC int
516 nfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
517 {
518 	struct sockaddr_in *inetaddr;
519 
520 	switch (family) {
521 	case AF_INET:
522 		inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
523 		if (inetaddr->sin_family == AF_INET &&
524 		    inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
525 			return (1);
526 		break;
527 #ifdef INET6
528 	case AF_INET6:
529 		{
530 		struct sockaddr_in6 *inetaddr6;
531 
532 		inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
533 		/* XXX - should test sin6_scope_id ? */
534 		if (inetaddr6->sin6_family == AF_INET6 &&
535 		    IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
536 			  &haddr->had_inet6))
537 			return (1);
538 		}
539 		break;
540 #endif
541 	};
542 	return (0);
543 }
544 
545 /*
546  * Similar to the above, but takes to NFSSOCKADDR_T args.
547  */
548 APPLESTATIC int
549 nfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
550 {
551 	struct sockaddr_in *addr1, *addr2;
552 	struct sockaddr *inaddr;
553 
554 	inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
555 	switch (inaddr->sa_family) {
556 	case AF_INET:
557 		addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
558 		addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
559 		if (addr2->sin_family == AF_INET &&
560 		    addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
561 			return (1);
562 		break;
563 #ifdef INET6
564 	case AF_INET6:
565 		{
566 		struct sockaddr_in6 *inet6addr1, *inet6addr2;
567 
568 		inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
569 		inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
570 		/* XXX - should test sin6_scope_id ? */
571 		if (inet6addr2->sin6_family == AF_INET6 &&
572 		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
573 			  &inet6addr2->sin6_addr))
574 			return (1);
575 		}
576 		break;
577 #endif
578 	};
579 	return (0);
580 }
581 
582 
583 /*
584  * Trim the stuff already dissected off the mbuf list.
585  */
586 APPLESTATIC void
587 newnfs_trimleading(nd)
588 	struct nfsrv_descript *nd;
589 {
590 	mbuf_t m, n;
591 	int offs;
592 
593 	/*
594 	 * First, free up leading mbufs.
595 	 */
596 	if (nd->nd_mrep != nd->nd_md) {
597 		m = nd->nd_mrep;
598 		while (mbuf_next(m) != nd->nd_md) {
599 			if (mbuf_next(m) == NULL)
600 				panic("nfsm trim leading");
601 			m = mbuf_next(m);
602 		}
603 		mbuf_setnext(m, NULL);
604 		mbuf_freem(nd->nd_mrep);
605 	}
606 	m = nd->nd_md;
607 
608 	/*
609 	 * Now, adjust this mbuf, based on nd_dpos.
610 	 */
611 	offs = nd->nd_dpos - NFSMTOD(m, caddr_t);
612 	if (offs == mbuf_len(m)) {
613 		n = m;
614 		m = mbuf_next(m);
615 		if (m == NULL)
616 			panic("nfsm trim leading2");
617 		mbuf_setnext(n, NULL);
618 		mbuf_freem(n);
619 	} else if (offs > 0) {
620 		mbuf_setlen(m, mbuf_len(m) - offs);
621 		NFSM_DATAP(m, offs);
622 	} else if (offs < 0)
623 		panic("nfsm trimleading offs");
624 	nd->nd_mrep = m;
625 	nd->nd_md = m;
626 	nd->nd_dpos = NFSMTOD(m, caddr_t);
627 }
628 
629 /*
630  * Trim trailing data off the mbuf list being built.
631  */
632 APPLESTATIC void
633 newnfs_trimtrailing(nd, mb, bpos)
634 	struct nfsrv_descript *nd;
635 	mbuf_t mb;
636 	caddr_t bpos;
637 {
638 
639 	if (mbuf_next(mb)) {
640 		mbuf_freem(mbuf_next(mb));
641 		mbuf_setnext(mb, NULL);
642 	}
643 	mbuf_setlen(mb, bpos - NFSMTOD(mb, caddr_t));
644 	nd->nd_mb = mb;
645 	nd->nd_bpos = bpos;
646 }
647 
648 /*
649  * Dissect a file handle on the client.
650  */
651 APPLESTATIC int
652 nfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
653 {
654 	u_int32_t *tl;
655 	struct nfsfh *nfhp;
656 	int error, len;
657 
658 	*nfhpp = NULL;
659 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
660 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
661 		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
662 			len > NFSX_FHMAX) {
663 			error = EBADRPC;
664 			goto nfsmout;
665 		}
666 	} else
667 		len = NFSX_V2FH;
668 	MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + len,
669 	    M_NFSFH, M_WAITOK);
670 	error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
671 	if (error) {
672 		FREE((caddr_t)nfhp, M_NFSFH);
673 		goto nfsmout;
674 	}
675 	nfhp->nfh_len = len;
676 	*nfhpp = nfhp;
677 nfsmout:
678 	NFSEXITCODE2(error, nd);
679 	return (error);
680 }
681 
682 /*
683  * Break down the nfsv4 acl.
684  * If the aclp == NULL or won't fit in an acl, just discard the acl info.
685  */
686 APPLESTATIC int
687 nfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, int *aclerrp,
688     int *aclsizep, __unused NFSPROC_T *p)
689 {
690 	u_int32_t *tl;
691 	int i, aclsize;
692 	int acecnt, error = 0, aceerr = 0, acesize;
693 
694 	*aclerrp = 0;
695 	if (aclp)
696 		aclp->acl_cnt = 0;
697 	/*
698 	 * Parse out the ace entries and expect them to conform to
699 	 * what can be supported by R/W/X bits.
700 	 */
701 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
702 	aclsize = NFSX_UNSIGNED;
703 	acecnt = fxdr_unsigned(int, *tl);
704 	if (acecnt > ACL_MAX_ENTRIES)
705 		aceerr = NFSERR_ATTRNOTSUPP;
706 	if (nfsrv_useacl == 0)
707 		aceerr = NFSERR_ATTRNOTSUPP;
708 	for (i = 0; i < acecnt; i++) {
709 		if (aclp && !aceerr)
710 			error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
711 			    &aceerr, &acesize, p);
712 		else
713 			error = nfsrv_skipace(nd, &acesize);
714 		if (error)
715 			goto nfsmout;
716 		aclsize += acesize;
717 	}
718 	if (aclp && !aceerr)
719 		aclp->acl_cnt = acecnt;
720 	if (aceerr)
721 		*aclerrp = aceerr;
722 	if (aclsizep)
723 		*aclsizep = aclsize;
724 nfsmout:
725 	NFSEXITCODE2(error, nd);
726 	return (error);
727 }
728 
729 /*
730  * Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
731  */
732 static int
733 nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
734 {
735 	u_int32_t *tl;
736 	int error, len = 0;
737 
738 	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
739 	len = fxdr_unsigned(int, *(tl + 3));
740 	error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
741 nfsmout:
742 	*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
743 	NFSEXITCODE2(error, nd);
744 	return (error);
745 }
746 
747 /*
748  * Get attribute bits from an mbuf list.
749  * Returns EBADRPC for a parsing error, 0 otherwise.
750  * If the clearinvalid flag is set, clear the bits not supported.
751  */
752 APPLESTATIC int
753 nfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
754     int *retnotsupp)
755 {
756 	u_int32_t *tl;
757 	int cnt, i, outcnt;
758 	int error = 0;
759 
760 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
761 	cnt = fxdr_unsigned(int, *tl);
762 	if (cnt < 0) {
763 		error = NFSERR_BADXDR;
764 		goto nfsmout;
765 	}
766 	if (cnt > NFSATTRBIT_MAXWORDS)
767 		outcnt = NFSATTRBIT_MAXWORDS;
768 	else
769 		outcnt = cnt;
770 	NFSZERO_ATTRBIT(attrbitp);
771 	if (outcnt > 0) {
772 		NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
773 		for (i = 0; i < outcnt; i++)
774 			attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
775 	}
776 	for (i = 0; i < (cnt - outcnt); i++) {
777 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
778 		if (retnotsupp != NULL && *tl != 0)
779 			*retnotsupp = NFSERR_ATTRNOTSUPP;
780 	}
781 	if (cntp)
782 		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
783 nfsmout:
784 	NFSEXITCODE2(error, nd);
785 	return (error);
786 }
787 
788 /*
789  * Get the attributes for V4.
790  * If the compare flag is true, test for any attribute changes,
791  * otherwise return the attribute values.
792  * These attributes cover fields in "struct vattr", "struct statfs",
793  * "struct nfsfsinfo", the file handle and the lease duration.
794  * The value of retcmpp is set to 1 if all attributes are the same,
795  * and 0 otherwise.
796  * Returns EBADRPC if it can't be parsed, 0 otherwise.
797  */
798 APPLESTATIC int
799 nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
800     struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
801     struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
802     struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
803     u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
804 {
805 	u_int32_t *tl;
806 	int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
807 	int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
808 	u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
809 	nfsattrbit_t attrbits, retattrbits, checkattrbits;
810 	struct nfsfh *tnfhp;
811 	struct nfsreferral *refp;
812 	u_quad_t tquad;
813 	nfsquad_t tnfsquad;
814 	struct timespec temptime;
815 	uid_t uid;
816 	gid_t gid;
817 	long fid;
818 	u_int32_t freenum = 0, tuint;
819 	u_int64_t uquad = 0, thyp, thyp2;
820 #ifdef QUOTA
821 	struct dqblk dqb;
822 	uid_t savuid;
823 #endif
824 	if (compare) {
825 		retnotsup = 0;
826 		error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
827 	} else {
828 		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
829 	}
830 	if (error)
831 		goto nfsmout;
832 
833 	if (compare) {
834 		*retcmpp = retnotsup;
835 	} else {
836 		/*
837 		 * Just set default values to some of the important ones.
838 		 */
839 		if (nap != NULL) {
840 			nap->na_type = VREG;
841 			nap->na_mode = 0;
842 			nap->na_rdev = (NFSDEV_T)0;
843 			nap->na_mtime.tv_sec = 0;
844 			nap->na_mtime.tv_nsec = 0;
845 			nap->na_gen = 0;
846 			nap->na_flags = 0;
847 			nap->na_blocksize = NFS_FABLKSIZE;
848 		}
849 		if (sbp != NULL) {
850 			sbp->f_bsize = NFS_FABLKSIZE;
851 			sbp->f_blocks = 0;
852 			sbp->f_bfree = 0;
853 			sbp->f_bavail = 0;
854 			sbp->f_files = 0;
855 			sbp->f_ffree = 0;
856 		}
857 		if (fsp != NULL) {
858 			fsp->fs_rtmax = 8192;
859 			fsp->fs_rtpref = 8192;
860 			fsp->fs_maxname = NFS_MAXNAMLEN;
861 			fsp->fs_wtmax = 8192;
862 			fsp->fs_wtpref = 8192;
863 			fsp->fs_wtmult = NFS_FABLKSIZE;
864 			fsp->fs_dtpref = 8192;
865 			fsp->fs_maxfilesize = 0xffffffffffffffffull;
866 			fsp->fs_timedelta.tv_sec = 0;
867 			fsp->fs_timedelta.tv_nsec = 1;
868 			fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
869 				NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
870 		}
871 		if (pc != NULL) {
872 			pc->pc_linkmax = LINK_MAX;
873 			pc->pc_namemax = NAME_MAX;
874 			pc->pc_notrunc = 0;
875 			pc->pc_chownrestricted = 0;
876 			pc->pc_caseinsensitive = 0;
877 			pc->pc_casepreserving = 1;
878 		}
879 	}
880 
881 	/*
882 	 * Loop around getting the attributes.
883 	 */
884 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
885 	attrsize = fxdr_unsigned(int, *tl);
886 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
887 	    if (attrsum > attrsize) {
888 		error = NFSERR_BADXDR;
889 		goto nfsmout;
890 	    }
891 	    if (NFSISSET_ATTRBIT(&attrbits, bitpos))
892 		switch (bitpos) {
893 		case NFSATTRBIT_SUPPORTEDATTRS:
894 			retnotsup = 0;
895 			if (compare || nap == NULL)
896 			    error = nfsrv_getattrbits(nd, &retattrbits,
897 				&cnt, &retnotsup);
898 			else
899 			    error = nfsrv_getattrbits(nd, &nap->na_suppattr,
900 				&cnt, &retnotsup);
901 			if (error)
902 			    goto nfsmout;
903 			if (compare && !(*retcmpp)) {
904 			   NFSSETSUPP_ATTRBIT(&checkattrbits);
905 
906 			   /* Some filesystem do not support NFSv4ACL   */
907 			   if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
908 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACL);
909 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACLSUPPORT);
910 		   	   }
911 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
912 			       || retnotsup)
913 				*retcmpp = NFSERR_NOTSAME;
914 			}
915 			attrsum += cnt;
916 			break;
917 		case NFSATTRBIT_TYPE:
918 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
919 			if (compare) {
920 				if (!(*retcmpp)) {
921 				    if (nap->na_type != nfsv34tov_type(*tl))
922 					*retcmpp = NFSERR_NOTSAME;
923 				}
924 			} else if (nap != NULL) {
925 				nap->na_type = nfsv34tov_type(*tl);
926 			}
927 			attrsum += NFSX_UNSIGNED;
928 			break;
929 		case NFSATTRBIT_FHEXPIRETYPE:
930 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
931 			if (compare && !(*retcmpp)) {
932 				if (fxdr_unsigned(int, *tl) !=
933 					NFSV4FHTYPE_PERSISTENT)
934 					*retcmpp = NFSERR_NOTSAME;
935 			}
936 			attrsum += NFSX_UNSIGNED;
937 			break;
938 		case NFSATTRBIT_CHANGE:
939 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
940 			if (compare) {
941 				if (!(*retcmpp)) {
942 				    if (nap->na_filerev != fxdr_hyper(tl))
943 					*retcmpp = NFSERR_NOTSAME;
944 				}
945 			} else if (nap != NULL) {
946 				nap->na_filerev = fxdr_hyper(tl);
947 			}
948 			attrsum += NFSX_HYPER;
949 			break;
950 		case NFSATTRBIT_SIZE:
951 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
952 			if (compare) {
953 				if (!(*retcmpp)) {
954 				    if (nap->na_size != fxdr_hyper(tl))
955 					*retcmpp = NFSERR_NOTSAME;
956 				}
957 			} else if (nap != NULL) {
958 				nap->na_size = fxdr_hyper(tl);
959 			}
960 			attrsum += NFSX_HYPER;
961 			break;
962 		case NFSATTRBIT_LINKSUPPORT:
963 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
964 			if (compare) {
965 				if (!(*retcmpp)) {
966 				    if (fsp->fs_properties & NFSV3_FSFLINK) {
967 					if (*tl == newnfs_false)
968 						*retcmpp = NFSERR_NOTSAME;
969 				    } else {
970 					if (*tl == newnfs_true)
971 						*retcmpp = NFSERR_NOTSAME;
972 				    }
973 				}
974 			} else if (fsp != NULL) {
975 				if (*tl == newnfs_true)
976 					fsp->fs_properties |= NFSV3_FSFLINK;
977 				else
978 					fsp->fs_properties &= ~NFSV3_FSFLINK;
979 			}
980 			attrsum += NFSX_UNSIGNED;
981 			break;
982 		case NFSATTRBIT_SYMLINKSUPPORT:
983 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
984 			if (compare) {
985 				if (!(*retcmpp)) {
986 				    if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
987 					if (*tl == newnfs_false)
988 						*retcmpp = NFSERR_NOTSAME;
989 				    } else {
990 					if (*tl == newnfs_true)
991 						*retcmpp = NFSERR_NOTSAME;
992 				    }
993 				}
994 			} else if (fsp != NULL) {
995 				if (*tl == newnfs_true)
996 					fsp->fs_properties |= NFSV3_FSFSYMLINK;
997 				else
998 					fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
999 			}
1000 			attrsum += NFSX_UNSIGNED;
1001 			break;
1002 		case NFSATTRBIT_NAMEDATTR:
1003 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1004 			if (compare && !(*retcmpp)) {
1005 				if (*tl != newnfs_false)
1006 					*retcmpp = NFSERR_NOTSAME;
1007 			}
1008 			attrsum += NFSX_UNSIGNED;
1009 			break;
1010 		case NFSATTRBIT_FSID:
1011 			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
1012 			thyp = fxdr_hyper(tl);
1013 			tl += 2;
1014 			thyp2 = fxdr_hyper(tl);
1015 			if (compare) {
1016 			    if (*retcmpp == 0) {
1017 				if (thyp != (u_int64_t)
1018 				    vfs_statfs(vnode_mount(vp))->f_fsid.val[0] ||
1019 				    thyp2 != (u_int64_t)
1020 				    vfs_statfs(vnode_mount(vp))->f_fsid.val[1])
1021 					*retcmpp = NFSERR_NOTSAME;
1022 			    }
1023 			} else if (nap != NULL) {
1024 				nap->na_filesid[0] = thyp;
1025 				nap->na_filesid[1] = thyp2;
1026 			}
1027 			attrsum += (4 * NFSX_UNSIGNED);
1028 			break;
1029 		case NFSATTRBIT_UNIQUEHANDLES:
1030 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1031 			if (compare && !(*retcmpp)) {
1032 				if (*tl != newnfs_true)
1033 					*retcmpp = NFSERR_NOTSAME;
1034 			}
1035 			attrsum += NFSX_UNSIGNED;
1036 			break;
1037 		case NFSATTRBIT_LEASETIME:
1038 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1039 			if (compare) {
1040 				if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
1041 				    !(*retcmpp))
1042 					*retcmpp = NFSERR_NOTSAME;
1043 			} else if (leasep != NULL) {
1044 				*leasep = fxdr_unsigned(u_int32_t, *tl);
1045 			}
1046 			attrsum += NFSX_UNSIGNED;
1047 			break;
1048 		case NFSATTRBIT_RDATTRERROR:
1049 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1050 			if (compare) {
1051 				 if (!(*retcmpp))
1052 					*retcmpp = NFSERR_INVAL;
1053 			} else if (rderrp != NULL) {
1054 				*rderrp = fxdr_unsigned(u_int32_t, *tl);
1055 			}
1056 			attrsum += NFSX_UNSIGNED;
1057 			break;
1058 		case NFSATTRBIT_ACL:
1059 			if (compare) {
1060 			  if (!(*retcmpp)) {
1061 			    if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
1062 				NFSACL_T *naclp;
1063 
1064 				naclp = acl_alloc(M_WAITOK);
1065 				error = nfsrv_dissectacl(nd, naclp, &aceerr,
1066 				    &cnt, p);
1067 				if (error) {
1068 				    acl_free(naclp);
1069 				    goto nfsmout;
1070 				}
1071 				if (aceerr || aclp == NULL ||
1072 				    nfsrv_compareacl(aclp, naclp))
1073 				    *retcmpp = NFSERR_NOTSAME;
1074 				acl_free(naclp);
1075 			    } else {
1076 				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1077 				    &cnt, p);
1078 				*retcmpp = NFSERR_ATTRNOTSUPP;
1079 			    }
1080 			  }
1081 			} else {
1082 				if (vp != NULL && aclp != NULL)
1083 				    error = nfsrv_dissectacl(nd, aclp, &aceerr,
1084 					&cnt, p);
1085 				else
1086 				    error = nfsrv_dissectacl(nd, NULL, &aceerr,
1087 					&cnt, p);
1088 				if (error)
1089 				    goto nfsmout;
1090 			}
1091 
1092 			attrsum += cnt;
1093 			break;
1094 		case NFSATTRBIT_ACLSUPPORT:
1095 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1096 			if (compare && !(*retcmpp)) {
1097 				if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
1098 					if (fxdr_unsigned(u_int32_t, *tl) !=
1099 					    NFSV4ACE_SUPTYPES)
1100 						*retcmpp = NFSERR_NOTSAME;
1101 				} else {
1102 					*retcmpp = NFSERR_ATTRNOTSUPP;
1103 				}
1104 			}
1105 			attrsum += NFSX_UNSIGNED;
1106 			break;
1107 		case NFSATTRBIT_ARCHIVE:
1108 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1109 			if (compare && !(*retcmpp))
1110 				*retcmpp = NFSERR_ATTRNOTSUPP;
1111 			attrsum += NFSX_UNSIGNED;
1112 			break;
1113 		case NFSATTRBIT_CANSETTIME:
1114 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1115 			if (compare) {
1116 				if (!(*retcmpp)) {
1117 				    if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
1118 					if (*tl == newnfs_false)
1119 						*retcmpp = NFSERR_NOTSAME;
1120 				    } else {
1121 					if (*tl == newnfs_true)
1122 						*retcmpp = NFSERR_NOTSAME;
1123 				    }
1124 				}
1125 			} else if (fsp != NULL) {
1126 				if (*tl == newnfs_true)
1127 					fsp->fs_properties |= NFSV3_FSFCANSETTIME;
1128 				else
1129 					fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
1130 			}
1131 			attrsum += NFSX_UNSIGNED;
1132 			break;
1133 		case NFSATTRBIT_CASEINSENSITIVE:
1134 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1135 			if (compare) {
1136 				if (!(*retcmpp)) {
1137 				    if (*tl != newnfs_false)
1138 					*retcmpp = NFSERR_NOTSAME;
1139 				}
1140 			} else if (pc != NULL) {
1141 				pc->pc_caseinsensitive =
1142 				    fxdr_unsigned(u_int32_t, *tl);
1143 			}
1144 			attrsum += NFSX_UNSIGNED;
1145 			break;
1146 		case NFSATTRBIT_CASEPRESERVING:
1147 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1148 			if (compare) {
1149 				if (!(*retcmpp)) {
1150 				    if (*tl != newnfs_true)
1151 					*retcmpp = NFSERR_NOTSAME;
1152 				}
1153 			} else if (pc != NULL) {
1154 				pc->pc_casepreserving =
1155 				    fxdr_unsigned(u_int32_t, *tl);
1156 			}
1157 			attrsum += NFSX_UNSIGNED;
1158 			break;
1159 		case NFSATTRBIT_CHOWNRESTRICTED:
1160 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1161 			if (compare) {
1162 				if (!(*retcmpp)) {
1163 				    if (*tl != newnfs_true)
1164 					*retcmpp = NFSERR_NOTSAME;
1165 				}
1166 			} else if (pc != NULL) {
1167 				pc->pc_chownrestricted =
1168 				    fxdr_unsigned(u_int32_t, *tl);
1169 			}
1170 			attrsum += NFSX_UNSIGNED;
1171 			break;
1172 		case NFSATTRBIT_FILEHANDLE:
1173 			error = nfsm_getfh(nd, &tnfhp);
1174 			if (error)
1175 				goto nfsmout;
1176 			tfhsize = tnfhp->nfh_len;
1177 			if (compare) {
1178 				if (!(*retcmpp) &&
1179 				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
1180 				     fhp, fhsize))
1181 					*retcmpp = NFSERR_NOTSAME;
1182 				FREE((caddr_t)tnfhp, M_NFSFH);
1183 			} else if (nfhpp != NULL) {
1184 				*nfhpp = tnfhp;
1185 			} else {
1186 				FREE((caddr_t)tnfhp, M_NFSFH);
1187 			}
1188 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
1189 			break;
1190 		case NFSATTRBIT_FILEID:
1191 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1192 			thyp = fxdr_hyper(tl);
1193 			if (compare) {
1194 				if (!(*retcmpp)) {
1195 				    if ((u_int64_t)nap->na_fileid != thyp)
1196 					*retcmpp = NFSERR_NOTSAME;
1197 				}
1198 			} else if (nap != NULL) {
1199 				if (*tl++)
1200 					printf("NFSv4 fileid > 32bits\n");
1201 				nap->na_fileid = thyp;
1202 			}
1203 			attrsum += NFSX_HYPER;
1204 			break;
1205 		case NFSATTRBIT_FILESAVAIL:
1206 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1207 			if (compare) {
1208 				if (!(*retcmpp) &&
1209 				    sfp->sf_afiles != fxdr_hyper(tl))
1210 					*retcmpp = NFSERR_NOTSAME;
1211 			} else if (sfp != NULL) {
1212 				sfp->sf_afiles = fxdr_hyper(tl);
1213 			}
1214 			attrsum += NFSX_HYPER;
1215 			break;
1216 		case NFSATTRBIT_FILESFREE:
1217 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1218 			if (compare) {
1219 				if (!(*retcmpp) &&
1220 				    sfp->sf_ffiles != fxdr_hyper(tl))
1221 					*retcmpp = NFSERR_NOTSAME;
1222 			} else if (sfp != NULL) {
1223 				sfp->sf_ffiles = fxdr_hyper(tl);
1224 			}
1225 			attrsum += NFSX_HYPER;
1226 			break;
1227 		case NFSATTRBIT_FILESTOTAL:
1228 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1229 			if (compare) {
1230 				if (!(*retcmpp) &&
1231 				    sfp->sf_tfiles != fxdr_hyper(tl))
1232 					*retcmpp = NFSERR_NOTSAME;
1233 			} else if (sfp != NULL) {
1234 				sfp->sf_tfiles = fxdr_hyper(tl);
1235 			}
1236 			attrsum += NFSX_HYPER;
1237 			break;
1238 		case NFSATTRBIT_FSLOCATIONS:
1239 			error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
1240 			if (error)
1241 				goto nfsmout;
1242 			attrsum += l;
1243 			if (compare && !(*retcmpp)) {
1244 				refp = nfsv4root_getreferral(vp, NULL, 0);
1245 				if (refp != NULL) {
1246 					if (cp == NULL || cp2 == NULL ||
1247 					    strcmp(cp, "/") ||
1248 					    strcmp(cp2, refp->nfr_srvlist))
1249 						*retcmpp = NFSERR_NOTSAME;
1250 				} else if (m == 0) {
1251 					*retcmpp = NFSERR_NOTSAME;
1252 				}
1253 			}
1254 			if (cp != NULL)
1255 				free(cp, M_NFSSTRING);
1256 			if (cp2 != NULL)
1257 				free(cp2, M_NFSSTRING);
1258 			break;
1259 		case NFSATTRBIT_HIDDEN:
1260 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1261 			if (compare && !(*retcmpp))
1262 				*retcmpp = NFSERR_ATTRNOTSUPP;
1263 			attrsum += NFSX_UNSIGNED;
1264 			break;
1265 		case NFSATTRBIT_HOMOGENEOUS:
1266 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1267 			if (compare) {
1268 				if (!(*retcmpp)) {
1269 				    if (fsp->fs_properties &
1270 					NFSV3_FSFHOMOGENEOUS) {
1271 					if (*tl == newnfs_false)
1272 						*retcmpp = NFSERR_NOTSAME;
1273 				    } else {
1274 					if (*tl == newnfs_true)
1275 						*retcmpp = NFSERR_NOTSAME;
1276 				    }
1277 				}
1278 			} else if (fsp != NULL) {
1279 				if (*tl == newnfs_true)
1280 				    fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
1281 				else
1282 				    fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
1283 			}
1284 			attrsum += NFSX_UNSIGNED;
1285 			break;
1286 		case NFSATTRBIT_MAXFILESIZE:
1287 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1288 			tnfsquad.qval = fxdr_hyper(tl);
1289 			if (compare) {
1290 				if (!(*retcmpp)) {
1291 					tquad = NFSRV_MAXFILESIZE;
1292 					if (tquad != tnfsquad.qval)
1293 						*retcmpp = NFSERR_NOTSAME;
1294 				}
1295 			} else if (fsp != NULL) {
1296 				fsp->fs_maxfilesize = tnfsquad.qval;
1297 			}
1298 			attrsum += NFSX_HYPER;
1299 			break;
1300 		case NFSATTRBIT_MAXLINK:
1301 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1302 			if (compare) {
1303 				if (!(*retcmpp)) {
1304 				    if (fxdr_unsigned(int, *tl) != LINK_MAX)
1305 					*retcmpp = NFSERR_NOTSAME;
1306 				}
1307 			} else if (pc != NULL) {
1308 				pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
1309 			}
1310 			attrsum += NFSX_UNSIGNED;
1311 			break;
1312 		case NFSATTRBIT_MAXNAME:
1313 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1314 			if (compare) {
1315 				if (!(*retcmpp)) {
1316 				    if (fsp->fs_maxname !=
1317 					fxdr_unsigned(u_int32_t, *tl))
1318 						*retcmpp = NFSERR_NOTSAME;
1319 				}
1320 			} else {
1321 				tuint = fxdr_unsigned(u_int32_t, *tl);
1322 				/*
1323 				 * Some Linux NFSv4 servers report this
1324 				 * as 0 or 4billion, so I'll set it to
1325 				 * NFS_MAXNAMLEN. If a server actually creates
1326 				 * a name longer than NFS_MAXNAMLEN, it will
1327 				 * get an error back.
1328 				 */
1329 				if (tuint == 0 || tuint > NFS_MAXNAMLEN)
1330 					tuint = NFS_MAXNAMLEN;
1331 				if (fsp != NULL)
1332 					fsp->fs_maxname = tuint;
1333 				if (pc != NULL)
1334 					pc->pc_namemax = tuint;
1335 			}
1336 			attrsum += NFSX_UNSIGNED;
1337 			break;
1338 		case NFSATTRBIT_MAXREAD:
1339 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1340 			if (compare) {
1341 				if (!(*retcmpp)) {
1342 				    if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
1343 					*(tl + 1)) || *tl != 0)
1344 					*retcmpp = NFSERR_NOTSAME;
1345 				}
1346 			} else if (fsp != NULL) {
1347 				fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
1348 				fsp->fs_rtpref = fsp->fs_rtmax;
1349 				fsp->fs_dtpref = fsp->fs_rtpref;
1350 			}
1351 			attrsum += NFSX_HYPER;
1352 			break;
1353 		case NFSATTRBIT_MAXWRITE:
1354 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1355 			if (compare) {
1356 				if (!(*retcmpp)) {
1357 				    if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
1358 					*(tl + 1)) || *tl != 0)
1359 					*retcmpp = NFSERR_NOTSAME;
1360 				}
1361 			} else if (fsp != NULL) {
1362 				fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
1363 				fsp->fs_wtpref = fsp->fs_wtmax;
1364 			}
1365 			attrsum += NFSX_HYPER;
1366 			break;
1367 		case NFSATTRBIT_MIMETYPE:
1368 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1369 			i = fxdr_unsigned(int, *tl);
1370 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
1371 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
1372 			if (error)
1373 				goto nfsmout;
1374 			if (compare && !(*retcmpp))
1375 				*retcmpp = NFSERR_ATTRNOTSUPP;
1376 			break;
1377 		case NFSATTRBIT_MODE:
1378 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1379 			if (compare) {
1380 				if (!(*retcmpp)) {
1381 				    if (nap->na_mode != nfstov_mode(*tl))
1382 					*retcmpp = NFSERR_NOTSAME;
1383 				}
1384 			} else if (nap != NULL) {
1385 				nap->na_mode = nfstov_mode(*tl);
1386 			}
1387 			attrsum += NFSX_UNSIGNED;
1388 			break;
1389 		case NFSATTRBIT_NOTRUNC:
1390 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1391 			if (compare) {
1392 				if (!(*retcmpp)) {
1393 				    if (*tl != newnfs_true)
1394 					*retcmpp = NFSERR_NOTSAME;
1395 				}
1396 			} else if (pc != NULL) {
1397 				pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
1398 			}
1399 			attrsum += NFSX_UNSIGNED;
1400 			break;
1401 		case NFSATTRBIT_NUMLINKS:
1402 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1403 			tuint = fxdr_unsigned(u_int32_t, *tl);
1404 			if (compare) {
1405 			    if (!(*retcmpp)) {
1406 				if ((u_int32_t)nap->na_nlink != tuint)
1407 					*retcmpp = NFSERR_NOTSAME;
1408 			    }
1409 			} else if (nap != NULL) {
1410 				nap->na_nlink = tuint;
1411 			}
1412 			attrsum += NFSX_UNSIGNED;
1413 			break;
1414 		case NFSATTRBIT_OWNER:
1415 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1416 			j = fxdr_unsigned(int, *tl);
1417 			if (j < 0) {
1418 				error = NFSERR_BADXDR;
1419 				goto nfsmout;
1420 			}
1421 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1422 			if (j > NFSV4_SMALLSTR)
1423 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1424 			else
1425 				cp = namestr;
1426 			error = nfsrv_mtostr(nd, cp, j);
1427 			if (error) {
1428 				if (j > NFSV4_SMALLSTR)
1429 					free(cp, M_NFSSTRING);
1430 				goto nfsmout;
1431 			}
1432 			if (compare) {
1433 			    if (!(*retcmpp)) {
1434 				if (nfsv4_strtouid(nd, cp, j, &uid, p) ||
1435 				    nap->na_uid != uid)
1436 				    *retcmpp = NFSERR_NOTSAME;
1437 			    }
1438 			} else if (nap != NULL) {
1439 				if (nfsv4_strtouid(nd, cp, j, &uid, p))
1440 					nap->na_uid = nfsrv_defaultuid;
1441 				else
1442 					nap->na_uid = uid;
1443 			}
1444 			if (j > NFSV4_SMALLSTR)
1445 				free(cp, M_NFSSTRING);
1446 			break;
1447 		case NFSATTRBIT_OWNERGROUP:
1448 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1449 			j = fxdr_unsigned(int, *tl);
1450 			if (j < 0) {
1451 				error =  NFSERR_BADXDR;
1452 				goto nfsmout;
1453 			}
1454 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1455 			if (j > NFSV4_SMALLSTR)
1456 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1457 			else
1458 				cp = namestr;
1459 			error = nfsrv_mtostr(nd, cp, j);
1460 			if (error) {
1461 				if (j > NFSV4_SMALLSTR)
1462 					free(cp, M_NFSSTRING);
1463 				goto nfsmout;
1464 			}
1465 			if (compare) {
1466 			    if (!(*retcmpp)) {
1467 				if (nfsv4_strtogid(nd, cp, j, &gid, p) ||
1468 				    nap->na_gid != gid)
1469 				    *retcmpp = NFSERR_NOTSAME;
1470 			    }
1471 			} else if (nap != NULL) {
1472 				if (nfsv4_strtogid(nd, cp, j, &gid, p))
1473 					nap->na_gid = nfsrv_defaultgid;
1474 				else
1475 					nap->na_gid = gid;
1476 			}
1477 			if (j > NFSV4_SMALLSTR)
1478 				free(cp, M_NFSSTRING);
1479 			break;
1480 		case NFSATTRBIT_QUOTAHARD:
1481 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1482 			if (sbp != NULL) {
1483 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1484 				freenum = sbp->f_bfree;
1485 			    else
1486 				freenum = sbp->f_bavail;
1487 #ifdef QUOTA
1488 			    /*
1489 			     * ufs_quotactl() insists that the uid argument
1490 			     * equal p_ruid for non-root quota access, so
1491 			     * we'll just make sure that's the case.
1492 			     */
1493 			    savuid = p->p_cred->p_ruid;
1494 			    p->p_cred->p_ruid = cred->cr_uid;
1495 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1496 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1497 				freenum = min(dqb.dqb_bhardlimit, freenum);
1498 			    p->p_cred->p_ruid = savuid;
1499 #endif	/* QUOTA */
1500 			    uquad = (u_int64_t)freenum;
1501 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1502 			}
1503 			if (compare && !(*retcmpp)) {
1504 				if (uquad != fxdr_hyper(tl))
1505 					*retcmpp = NFSERR_NOTSAME;
1506 			}
1507 			attrsum += NFSX_HYPER;
1508 			break;
1509 		case NFSATTRBIT_QUOTASOFT:
1510 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1511 			if (sbp != NULL) {
1512 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1513 				freenum = sbp->f_bfree;
1514 			    else
1515 				freenum = sbp->f_bavail;
1516 #ifdef QUOTA
1517 			    /*
1518 			     * ufs_quotactl() insists that the uid argument
1519 			     * equal p_ruid for non-root quota access, so
1520 			     * we'll just make sure that's the case.
1521 			     */
1522 			    savuid = p->p_cred->p_ruid;
1523 			    p->p_cred->p_ruid = cred->cr_uid;
1524 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1525 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1526 				freenum = min(dqb.dqb_bsoftlimit, freenum);
1527 			    p->p_cred->p_ruid = savuid;
1528 #endif	/* QUOTA */
1529 			    uquad = (u_int64_t)freenum;
1530 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1531 			}
1532 			if (compare && !(*retcmpp)) {
1533 				if (uquad != fxdr_hyper(tl))
1534 					*retcmpp = NFSERR_NOTSAME;
1535 			}
1536 			attrsum += NFSX_HYPER;
1537 			break;
1538 		case NFSATTRBIT_QUOTAUSED:
1539 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1540 			if (sbp != NULL) {
1541 			    freenum = 0;
1542 #ifdef QUOTA
1543 			    /*
1544 			     * ufs_quotactl() insists that the uid argument
1545 			     * equal p_ruid for non-root quota access, so
1546 			     * we'll just make sure that's the case.
1547 			     */
1548 			    savuid = p->p_cred->p_ruid;
1549 			    p->p_cred->p_ruid = cred->cr_uid;
1550 			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1551 				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1552 				freenum = dqb.dqb_curblocks;
1553 			    p->p_cred->p_ruid = savuid;
1554 #endif	/* QUOTA */
1555 			    uquad = (u_int64_t)freenum;
1556 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1557 			}
1558 			if (compare && !(*retcmpp)) {
1559 				if (uquad != fxdr_hyper(tl))
1560 					*retcmpp = NFSERR_NOTSAME;
1561 			}
1562 			attrsum += NFSX_HYPER;
1563 			break;
1564 		case NFSATTRBIT_RAWDEV:
1565 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
1566 			j = fxdr_unsigned(int, *tl++);
1567 			k = fxdr_unsigned(int, *tl);
1568 			if (compare) {
1569 			    if (!(*retcmpp)) {
1570 				if (nap->na_rdev != NFSMAKEDEV(j, k))
1571 					*retcmpp = NFSERR_NOTSAME;
1572 			    }
1573 			} else if (nap != NULL) {
1574 				nap->na_rdev = NFSMAKEDEV(j, k);
1575 			}
1576 			attrsum += NFSX_V4SPECDATA;
1577 			break;
1578 		case NFSATTRBIT_SPACEAVAIL:
1579 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1580 			if (compare) {
1581 				if (!(*retcmpp) &&
1582 				    sfp->sf_abytes != fxdr_hyper(tl))
1583 					*retcmpp = NFSERR_NOTSAME;
1584 			} else if (sfp != NULL) {
1585 				sfp->sf_abytes = fxdr_hyper(tl);
1586 			}
1587 			attrsum += NFSX_HYPER;
1588 			break;
1589 		case NFSATTRBIT_SPACEFREE:
1590 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1591 			if (compare) {
1592 				if (!(*retcmpp) &&
1593 				    sfp->sf_fbytes != fxdr_hyper(tl))
1594 					*retcmpp = NFSERR_NOTSAME;
1595 			} else if (sfp != NULL) {
1596 				sfp->sf_fbytes = fxdr_hyper(tl);
1597 			}
1598 			attrsum += NFSX_HYPER;
1599 			break;
1600 		case NFSATTRBIT_SPACETOTAL:
1601 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1602 			if (compare) {
1603 				if (!(*retcmpp) &&
1604 				    sfp->sf_tbytes != fxdr_hyper(tl))
1605 					*retcmpp = NFSERR_NOTSAME;
1606 			} else if (sfp != NULL) {
1607 				sfp->sf_tbytes = fxdr_hyper(tl);
1608 			}
1609 			attrsum += NFSX_HYPER;
1610 			break;
1611 		case NFSATTRBIT_SPACEUSED:
1612 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1613 			thyp = fxdr_hyper(tl);
1614 			if (compare) {
1615 			    if (!(*retcmpp)) {
1616 				if ((u_int64_t)nap->na_bytes != thyp)
1617 					*retcmpp = NFSERR_NOTSAME;
1618 			    }
1619 			} else if (nap != NULL) {
1620 				nap->na_bytes = thyp;
1621 			}
1622 			attrsum += NFSX_HYPER;
1623 			break;
1624 		case NFSATTRBIT_SYSTEM:
1625 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1626 			if (compare && !(*retcmpp))
1627 				*retcmpp = NFSERR_ATTRNOTSUPP;
1628 			attrsum += NFSX_UNSIGNED;
1629 			break;
1630 		case NFSATTRBIT_TIMEACCESS:
1631 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1632 			fxdr_nfsv4time(tl, &temptime);
1633 			if (compare) {
1634 			    if (!(*retcmpp)) {
1635 				if (!NFS_CMPTIME(temptime, nap->na_atime))
1636 					*retcmpp = NFSERR_NOTSAME;
1637 			    }
1638 			} else if (nap != NULL) {
1639 				nap->na_atime = temptime;
1640 			}
1641 			attrsum += NFSX_V4TIME;
1642 			break;
1643 		case NFSATTRBIT_TIMEACCESSSET:
1644 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1645 			attrsum += NFSX_UNSIGNED;
1646 			i = fxdr_unsigned(int, *tl);
1647 			if (i == NFSV4SATTRTIME_TOCLIENT) {
1648 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1649 				attrsum += NFSX_V4TIME;
1650 			}
1651 			if (compare && !(*retcmpp))
1652 				*retcmpp = NFSERR_INVAL;
1653 			break;
1654 		case NFSATTRBIT_TIMEBACKUP:
1655 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1656 			if (compare && !(*retcmpp))
1657 				*retcmpp = NFSERR_ATTRNOTSUPP;
1658 			attrsum += NFSX_V4TIME;
1659 			break;
1660 		case NFSATTRBIT_TIMECREATE:
1661 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1662 			if (compare && !(*retcmpp))
1663 				*retcmpp = NFSERR_ATTRNOTSUPP;
1664 			attrsum += NFSX_V4TIME;
1665 			break;
1666 		case NFSATTRBIT_TIMEDELTA:
1667 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1668 			if (fsp != NULL) {
1669 			    if (compare) {
1670 				if (!(*retcmpp)) {
1671 				    if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
1672 					fxdr_unsigned(u_int32_t, *(tl + 1)) ||
1673 				        (u_int32_t)fsp->fs_timedelta.tv_nsec !=
1674 					(fxdr_unsigned(u_int32_t, *(tl + 2)) %
1675 					 1000000000) ||
1676 					*tl != 0)
1677 					    *retcmpp = NFSERR_NOTSAME;
1678 				}
1679 			    } else {
1680 				fxdr_nfsv4time(tl, &fsp->fs_timedelta);
1681 			    }
1682 			}
1683 			attrsum += NFSX_V4TIME;
1684 			break;
1685 		case NFSATTRBIT_TIMEMETADATA:
1686 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1687 			fxdr_nfsv4time(tl, &temptime);
1688 			if (compare) {
1689 			    if (!(*retcmpp)) {
1690 				if (!NFS_CMPTIME(temptime, nap->na_ctime))
1691 					*retcmpp = NFSERR_NOTSAME;
1692 			    }
1693 			} else if (nap != NULL) {
1694 				nap->na_ctime = temptime;
1695 			}
1696 			attrsum += NFSX_V4TIME;
1697 			break;
1698 		case NFSATTRBIT_TIMEMODIFY:
1699 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1700 			fxdr_nfsv4time(tl, &temptime);
1701 			if (compare) {
1702 			    if (!(*retcmpp)) {
1703 				if (!NFS_CMPTIME(temptime, nap->na_mtime))
1704 					*retcmpp = NFSERR_NOTSAME;
1705 			    }
1706 			} else if (nap != NULL) {
1707 				nap->na_mtime = temptime;
1708 			}
1709 			attrsum += NFSX_V4TIME;
1710 			break;
1711 		case NFSATTRBIT_TIMEMODIFYSET:
1712 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1713 			attrsum += NFSX_UNSIGNED;
1714 			i = fxdr_unsigned(int, *tl);
1715 			if (i == NFSV4SATTRTIME_TOCLIENT) {
1716 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1717 				attrsum += NFSX_V4TIME;
1718 			}
1719 			if (compare && !(*retcmpp))
1720 				*retcmpp = NFSERR_INVAL;
1721 			break;
1722 		case NFSATTRBIT_MOUNTEDONFILEID:
1723 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1724 			thyp = fxdr_hyper(tl);
1725 			if (compare) {
1726 			    if (!(*retcmpp)) {
1727 				if (*tl++) {
1728 					*retcmpp = NFSERR_NOTSAME;
1729 				} else {
1730 					if (!vp || !nfsrv_atroot(vp, &fid))
1731 						fid = nap->na_fileid;
1732 					if ((u_int64_t)fid != thyp)
1733 						*retcmpp = NFSERR_NOTSAME;
1734 				}
1735 			    }
1736 			} else if (nap != NULL) {
1737 			    if (*tl++)
1738 				printf("NFSv4 mounted on fileid > 32bits\n");
1739 			    nap->na_mntonfileno = thyp;
1740 			}
1741 			attrsum += NFSX_HYPER;
1742 			break;
1743 		case NFSATTRBIT_SUPPATTREXCLCREAT:
1744 			retnotsup = 0;
1745 			error = nfsrv_getattrbits(nd, &retattrbits,
1746 			    &cnt, &retnotsup);
1747 			if (error)
1748 			    goto nfsmout;
1749 			if (compare && !(*retcmpp)) {
1750 			   NFSSETSUPP_ATTRBIT(&checkattrbits);
1751 			   NFSCLRNOTSETABLE_ATTRBIT(&checkattrbits);
1752 			   NFSCLRBIT_ATTRBIT(&checkattrbits,
1753 				NFSATTRBIT_TIMEACCESSSET);
1754 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
1755 			       || retnotsup)
1756 				*retcmpp = NFSERR_NOTSAME;
1757 			}
1758 			attrsum += cnt;
1759 			break;
1760 		default:
1761 			printf("EEK! nfsv4_loadattr unknown attr=%d\n",
1762 				bitpos);
1763 			if (compare && !(*retcmpp))
1764 				*retcmpp = NFSERR_ATTRNOTSUPP;
1765 			/*
1766 			 * and get out of the loop, since we can't parse
1767 			 * the unknown attrbute data.
1768 			 */
1769 			bitpos = NFSATTRBIT_MAX;
1770 			break;
1771 		};
1772 	}
1773 
1774 	/*
1775 	 * some clients pad the attrlist, so we need to skip over the
1776 	 * padding.
1777 	 */
1778 	if (attrsum > attrsize) {
1779 		error = NFSERR_BADXDR;
1780 	} else {
1781 		attrsize = NFSM_RNDUP(attrsize);
1782 		if (attrsum < attrsize)
1783 			error = nfsm_advance(nd, attrsize - attrsum, -1);
1784 	}
1785 nfsmout:
1786 	NFSEXITCODE2(error, nd);
1787 	return (error);
1788 }
1789 
1790 /*
1791  * Implement sleep locks for newnfs. The nfslock_usecnt allows for a
1792  * shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
1793  * The first argument is a pointer to an nfsv4lock structure.
1794  * The second argument is 1 iff a blocking lock is wanted.
1795  * If this argument is 0, the call waits until no thread either wants nor
1796  * holds an exclusive lock.
1797  * It returns 1 if the lock was acquired, 0 otherwise.
1798  * If several processes call this function concurrently wanting the exclusive
1799  * lock, one will get the lock and the rest will return without getting the
1800  * lock. (If the caller must have the lock, it simply calls this function in a
1801  *  loop until the function returns 1 to indicate the lock was acquired.)
1802  * Any usecnt must be decremented by calling nfsv4_relref() before
1803  * calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
1804  * be called in a loop.
1805  * The isleptp argument is set to indicate if the call slept, iff not NULL
1806  * and the mp argument indicates to check for a forced dismount, iff not
1807  * NULL.
1808  */
1809 APPLESTATIC int
1810 nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
1811     void *mutex, struct mount *mp)
1812 {
1813 
1814 	if (isleptp)
1815 		*isleptp = 0;
1816 	/*
1817 	 * If a lock is wanted, loop around until the lock is acquired by
1818 	 * someone and then released. If I want the lock, try to acquire it.
1819 	 * For a lock to be issued, no lock must be in force and the usecnt
1820 	 * must be zero.
1821 	 */
1822 	if (iwantlock) {
1823 	    if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1824 		lp->nfslock_usecnt == 0) {
1825 		lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1826 		lp->nfslock_lock |= NFSV4LOCK_LOCK;
1827 		return (1);
1828 	    }
1829 	    lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
1830 	}
1831 	while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
1832 		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1833 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1834 			return (0);
1835 		}
1836 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1837 		if (isleptp)
1838 			*isleptp = 1;
1839 		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1840 		    PZERO - 1, "nfsv4lck", NULL);
1841 		if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1842 		    lp->nfslock_usecnt == 0) {
1843 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1844 			lp->nfslock_lock |= NFSV4LOCK_LOCK;
1845 			return (1);
1846 		}
1847 	}
1848 	return (0);
1849 }
1850 
1851 /*
1852  * Release the lock acquired by nfsv4_lock().
1853  * The second argument is set to 1 to indicate the nfslock_usecnt should be
1854  * incremented, as well.
1855  */
1856 APPLESTATIC void
1857 nfsv4_unlock(struct nfsv4lock *lp, int incref)
1858 {
1859 
1860 	lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
1861 	if (incref)
1862 		lp->nfslock_usecnt++;
1863 	nfsv4_wanted(lp);
1864 }
1865 
1866 /*
1867  * Release a reference cnt.
1868  */
1869 APPLESTATIC void
1870 nfsv4_relref(struct nfsv4lock *lp)
1871 {
1872 
1873 	if (lp->nfslock_usecnt <= 0)
1874 		panic("nfsv4root ref cnt");
1875 	lp->nfslock_usecnt--;
1876 	if (lp->nfslock_usecnt == 0)
1877 		nfsv4_wanted(lp);
1878 }
1879 
1880 /*
1881  * Get a reference cnt.
1882  * This function will wait for any exclusive lock to be released, but will
1883  * not wait for threads that want the exclusive lock. If priority needs
1884  * to be given to threads that need the exclusive lock, a call to nfsv4_lock()
1885  * with the 2nd argument == 0 should be done before calling nfsv4_getref().
1886  * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and
1887  * return without getting a refcnt for that case.
1888  */
1889 APPLESTATIC void
1890 nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex,
1891     struct mount *mp)
1892 {
1893 
1894 	if (isleptp)
1895 		*isleptp = 0;
1896 
1897 	/*
1898 	 * Wait for a lock held.
1899 	 */
1900 	while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
1901 		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1902 			return;
1903 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1904 		if (isleptp)
1905 			*isleptp = 1;
1906 		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1907 		    PZERO - 1, "nfsv4gr", NULL);
1908 	}
1909 	if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1910 		return;
1911 
1912 	lp->nfslock_usecnt++;
1913 }
1914 
1915 /*
1916  * Get a reference as above, but return failure instead of sleeping if
1917  * an exclusive lock is held.
1918  */
1919 APPLESTATIC int
1920 nfsv4_getref_nonblock(struct nfsv4lock *lp)
1921 {
1922 
1923 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
1924 		return (0);
1925 
1926 	lp->nfslock_usecnt++;
1927 	return (1);
1928 }
1929 
1930 /*
1931  * Test for a lock. Return 1 if locked, 0 otherwise.
1932  */
1933 APPLESTATIC int
1934 nfsv4_testlock(struct nfsv4lock *lp)
1935 {
1936 
1937 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
1938 	    lp->nfslock_usecnt == 0)
1939 		return (0);
1940 	return (1);
1941 }
1942 
1943 /*
1944  * Wake up anyone sleeping, waiting for this lock.
1945  */
1946 static void
1947 nfsv4_wanted(struct nfsv4lock *lp)
1948 {
1949 
1950 	if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
1951 		lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
1952 		wakeup((caddr_t)&lp->nfslock_lock);
1953 	}
1954 }
1955 
1956 /*
1957  * Copy a string from an mbuf list into a character array.
1958  * Return EBADRPC if there is an mbuf error,
1959  * 0 otherwise.
1960  */
1961 APPLESTATIC int
1962 nfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
1963 {
1964 	char *cp;
1965 	int xfer, len;
1966 	mbuf_t mp;
1967 	int rem, error = 0;
1968 
1969 	mp = nd->nd_md;
1970 	cp = nd->nd_dpos;
1971 	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - cp;
1972 	rem = NFSM_RNDUP(siz) - siz;
1973 	while (siz > 0) {
1974 		if (len > siz)
1975 			xfer = siz;
1976 		else
1977 			xfer = len;
1978 		NFSBCOPY(cp, str, xfer);
1979 		str += xfer;
1980 		siz -= xfer;
1981 		if (siz > 0) {
1982 			mp = mbuf_next(mp);
1983 			if (mp == NULL) {
1984 				error = EBADRPC;
1985 				goto out;
1986 			}
1987 			cp = NFSMTOD(mp, caddr_t);
1988 			len = mbuf_len(mp);
1989 		} else {
1990 			cp += xfer;
1991 			len -= xfer;
1992 		}
1993 	}
1994 	*str = '\0';
1995 	nd->nd_dpos = cp;
1996 	nd->nd_md = mp;
1997 	if (rem > 0) {
1998 		if (len < rem)
1999 			error = nfsm_advance(nd, rem, len);
2000 		else
2001 			nd->nd_dpos += rem;
2002 	}
2003 
2004 out:
2005 	NFSEXITCODE2(error, nd);
2006 	return (error);
2007 }
2008 
2009 /*
2010  * Fill in the attributes as marked by the bitmap (V4).
2011  */
2012 APPLESTATIC int
2013 nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
2014     NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
2015     nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
2016     int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
2017 {
2018 	int bitpos, retnum = 0;
2019 	u_int32_t *tl;
2020 	int siz, prefixnum, error;
2021 	u_char *cp, namestr[NFSV4_SMALLSTR];
2022 	nfsattrbit_t attrbits, retbits;
2023 	nfsattrbit_t *retbitp = &retbits;
2024 	u_int32_t freenum, *retnump;
2025 	u_int64_t uquad;
2026 	struct statfs fs;
2027 	struct nfsfsinfo fsinf;
2028 	struct timespec temptime;
2029 	NFSACL_T *aclp, *naclp = NULL;
2030 #ifdef QUOTA
2031 	struct dqblk dqb;
2032 	uid_t savuid;
2033 #endif
2034 
2035 	/*
2036 	 * First, set the bits that can be filled and get fsinfo.
2037 	 */
2038 	NFSSET_ATTRBIT(retbitp, attrbitp);
2039 	/*
2040 	 * If both p and cred are NULL, it is a client side setattr call.
2041 	 * If both p and cred are not NULL, it is a server side reply call.
2042 	 * If p is not NULL and cred is NULL, it is a client side callback
2043 	 * reply call.
2044 	 */
2045 	if (p == NULL && cred == NULL) {
2046 		NFSCLRNOTSETABLE_ATTRBIT(retbitp);
2047 		aclp = saclp;
2048 	} else {
2049 		NFSCLRNOTFILLABLE_ATTRBIT(retbitp);
2050 		naclp = acl_alloc(M_WAITOK);
2051 		aclp = naclp;
2052 	}
2053 	nfsvno_getfs(&fsinf, isdgram);
2054 #ifndef APPLE
2055 	/*
2056 	 * Get the VFS_STATFS(), since some attributes need them.
2057 	 */
2058 	if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
2059 		error = VFS_STATFS(mp, &fs);
2060 		if (error != 0) {
2061 			if (reterr) {
2062 				nd->nd_repstat = NFSERR_ACCES;
2063 				return (0);
2064 			}
2065 			NFSCLRSTATFS_ATTRBIT(retbitp);
2066 		}
2067 	}
2068 #endif
2069 
2070 	/*
2071 	 * And the NFSv4 ACL...
2072 	 */
2073 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
2074 	    (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2075 		supports_nfsv4acls == 0))) {
2076 		NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
2077 	}
2078 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
2079 		if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2080 		    supports_nfsv4acls == 0)) {
2081 			NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2082 		} else if (naclp != NULL) {
2083 			if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
2084 				error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
2085 				if (error == 0)
2086 					error = VOP_GETACL(vp, ACL_TYPE_NFS4,
2087 					    naclp, cred, p);
2088 				NFSVOPUNLOCK(vp, 0);
2089 			} else
2090 				error = NFSERR_PERM;
2091 			if (error != 0) {
2092 				if (reterr) {
2093 					nd->nd_repstat = NFSERR_ACCES;
2094 					return (0);
2095 				}
2096 				NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2097 			}
2098 		}
2099 	}
2100 
2101 	/*
2102 	 * Put out the attribute bitmap for the ones being filled in
2103 	 * and get the field for the number of attributes returned.
2104 	 */
2105 	prefixnum = nfsrv_putattrbit(nd, retbitp);
2106 	NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
2107 	prefixnum += NFSX_UNSIGNED;
2108 
2109 	/*
2110 	 * Now, loop around filling in the attributes for each bit set.
2111 	 */
2112 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
2113 	    if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
2114 		switch (bitpos) {
2115 		case NFSATTRBIT_SUPPORTEDATTRS:
2116 			NFSSETSUPP_ATTRBIT(&attrbits);
2117 			if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
2118 			    && supports_nfsv4acls == 0)) {
2119 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
2120 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
2121 			}
2122 			retnum += nfsrv_putattrbit(nd, &attrbits);
2123 			break;
2124 		case NFSATTRBIT_TYPE:
2125 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2126 			*tl = vtonfsv34_type(vap->va_type);
2127 			retnum += NFSX_UNSIGNED;
2128 			break;
2129 		case NFSATTRBIT_FHEXPIRETYPE:
2130 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2131 			*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
2132 			retnum += NFSX_UNSIGNED;
2133 			break;
2134 		case NFSATTRBIT_CHANGE:
2135 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2136 			txdr_hyper(vap->va_filerev, tl);
2137 			retnum += NFSX_HYPER;
2138 			break;
2139 		case NFSATTRBIT_SIZE:
2140 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2141 			txdr_hyper(vap->va_size, tl);
2142 			retnum += NFSX_HYPER;
2143 			break;
2144 		case NFSATTRBIT_LINKSUPPORT:
2145 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2146 			if (fsinf.fs_properties & NFSV3FSINFO_LINK)
2147 				*tl = newnfs_true;
2148 			else
2149 				*tl = newnfs_false;
2150 			retnum += NFSX_UNSIGNED;
2151 			break;
2152 		case NFSATTRBIT_SYMLINKSUPPORT:
2153 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2154 			if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
2155 				*tl = newnfs_true;
2156 			else
2157 				*tl = newnfs_false;
2158 			retnum += NFSX_UNSIGNED;
2159 			break;
2160 		case NFSATTRBIT_NAMEDATTR:
2161 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2162 			*tl = newnfs_false;
2163 			retnum += NFSX_UNSIGNED;
2164 			break;
2165 		case NFSATTRBIT_FSID:
2166 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
2167 			*tl++ = 0;
2168 			*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
2169 			*tl++ = 0;
2170 			*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
2171 			retnum += NFSX_V4FSID;
2172 			break;
2173 		case NFSATTRBIT_UNIQUEHANDLES:
2174 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2175 			*tl = newnfs_true;
2176 			retnum += NFSX_UNSIGNED;
2177 			break;
2178 		case NFSATTRBIT_LEASETIME:
2179 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2180 			*tl = txdr_unsigned(nfsrv_lease);
2181 			retnum += NFSX_UNSIGNED;
2182 			break;
2183 		case NFSATTRBIT_RDATTRERROR:
2184 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2185 			*tl = txdr_unsigned(rderror);
2186 			retnum += NFSX_UNSIGNED;
2187 			break;
2188 		/*
2189 		 * Recommended Attributes. (Only the supported ones.)
2190 		 */
2191 		case NFSATTRBIT_ACL:
2192 			retnum += nfsrv_buildacl(nd, aclp, vnode_vtype(vp), p);
2193 			break;
2194 		case NFSATTRBIT_ACLSUPPORT:
2195 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2196 			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
2197 			retnum += NFSX_UNSIGNED;
2198 			break;
2199 		case NFSATTRBIT_CANSETTIME:
2200 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2201 			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
2202 				*tl = newnfs_true;
2203 			else
2204 				*tl = newnfs_false;
2205 			retnum += NFSX_UNSIGNED;
2206 			break;
2207 		case NFSATTRBIT_CASEINSENSITIVE:
2208 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2209 			*tl = newnfs_false;
2210 			retnum += NFSX_UNSIGNED;
2211 			break;
2212 		case NFSATTRBIT_CASEPRESERVING:
2213 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2214 			*tl = newnfs_true;
2215 			retnum += NFSX_UNSIGNED;
2216 			break;
2217 		case NFSATTRBIT_CHOWNRESTRICTED:
2218 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2219 			*tl = newnfs_true;
2220 			retnum += NFSX_UNSIGNED;
2221 			break;
2222 		case NFSATTRBIT_FILEHANDLE:
2223 			retnum += nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
2224 			break;
2225 		case NFSATTRBIT_FILEID:
2226 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2227 			*tl++ = 0;
2228 			*tl = txdr_unsigned(vap->va_fileid);
2229 			retnum += NFSX_HYPER;
2230 			break;
2231 		case NFSATTRBIT_FILESAVAIL:
2232 			/*
2233 			 * Check quota and use min(quota, f_ffree).
2234 			 */
2235 			freenum = fs.f_ffree;
2236 #ifdef QUOTA
2237 			/*
2238 			 * ufs_quotactl() insists that the uid argument
2239 			 * equal p_ruid for non-root quota access, so
2240 			 * we'll just make sure that's the case.
2241 			 */
2242 			savuid = p->p_cred->p_ruid;
2243 			p->p_cred->p_ruid = cred->cr_uid;
2244 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2245 			    cred->cr_uid, (caddr_t)&dqb))
2246 			    freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
2247 				freenum);
2248 			p->p_cred->p_ruid = savuid;
2249 #endif	/* QUOTA */
2250 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2251 			*tl++ = 0;
2252 			*tl = txdr_unsigned(freenum);
2253 			retnum += NFSX_HYPER;
2254 			break;
2255 		case NFSATTRBIT_FILESFREE:
2256 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2257 			*tl++ = 0;
2258 			*tl = txdr_unsigned(fs.f_ffree);
2259 			retnum += NFSX_HYPER;
2260 			break;
2261 		case NFSATTRBIT_FILESTOTAL:
2262 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2263 			*tl++ = 0;
2264 			*tl = txdr_unsigned(fs.f_files);
2265 			retnum += NFSX_HYPER;
2266 			break;
2267 		case NFSATTRBIT_FSLOCATIONS:
2268 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2269 			*tl++ = 0;
2270 			*tl = 0;
2271 			retnum += 2 * NFSX_UNSIGNED;
2272 			break;
2273 		case NFSATTRBIT_HOMOGENEOUS:
2274 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2275 			if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
2276 				*tl = newnfs_true;
2277 			else
2278 				*tl = newnfs_false;
2279 			retnum += NFSX_UNSIGNED;
2280 			break;
2281 		case NFSATTRBIT_MAXFILESIZE:
2282 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2283 			uquad = NFSRV_MAXFILESIZE;
2284 			txdr_hyper(uquad, tl);
2285 			retnum += NFSX_HYPER;
2286 			break;
2287 		case NFSATTRBIT_MAXLINK:
2288 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2289 			*tl = txdr_unsigned(LINK_MAX);
2290 			retnum += NFSX_UNSIGNED;
2291 			break;
2292 		case NFSATTRBIT_MAXNAME:
2293 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2294 			*tl = txdr_unsigned(NFS_MAXNAMLEN);
2295 			retnum += NFSX_UNSIGNED;
2296 			break;
2297 		case NFSATTRBIT_MAXREAD:
2298 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2299 			*tl++ = 0;
2300 			*tl = txdr_unsigned(fsinf.fs_rtmax);
2301 			retnum += NFSX_HYPER;
2302 			break;
2303 		case NFSATTRBIT_MAXWRITE:
2304 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2305 			*tl++ = 0;
2306 			*tl = txdr_unsigned(fsinf.fs_wtmax);
2307 			retnum += NFSX_HYPER;
2308 			break;
2309 		case NFSATTRBIT_MODE:
2310 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2311 			*tl = vtonfsv34_mode(vap->va_mode);
2312 			retnum += NFSX_UNSIGNED;
2313 			break;
2314 		case NFSATTRBIT_NOTRUNC:
2315 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2316 			*tl = newnfs_true;
2317 			retnum += NFSX_UNSIGNED;
2318 			break;
2319 		case NFSATTRBIT_NUMLINKS:
2320 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2321 			*tl = txdr_unsigned(vap->va_nlink);
2322 			retnum += NFSX_UNSIGNED;
2323 			break;
2324 		case NFSATTRBIT_OWNER:
2325 			cp = namestr;
2326 			nfsv4_uidtostr(vap->va_uid, &cp, &siz, p);
2327 			retnum += nfsm_strtom(nd, cp, siz);
2328 			if (cp != namestr)
2329 				free(cp, M_NFSSTRING);
2330 			break;
2331 		case NFSATTRBIT_OWNERGROUP:
2332 			cp = namestr;
2333 			nfsv4_gidtostr(vap->va_gid, &cp, &siz, p);
2334 			retnum += nfsm_strtom(nd, cp, siz);
2335 			if (cp != namestr)
2336 				free(cp, M_NFSSTRING);
2337 			break;
2338 		case NFSATTRBIT_QUOTAHARD:
2339 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2340 				freenum = fs.f_bfree;
2341 			else
2342 				freenum = fs.f_bavail;
2343 #ifdef QUOTA
2344 			/*
2345 			 * ufs_quotactl() insists that the uid argument
2346 			 * equal p_ruid for non-root quota access, so
2347 			 * we'll just make sure that's the case.
2348 			 */
2349 			savuid = p->p_cred->p_ruid;
2350 			p->p_cred->p_ruid = cred->cr_uid;
2351 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2352 			    cred->cr_uid, (caddr_t)&dqb))
2353 			    freenum = min(dqb.dqb_bhardlimit, freenum);
2354 			p->p_cred->p_ruid = savuid;
2355 #endif	/* QUOTA */
2356 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2357 			uquad = (u_int64_t)freenum;
2358 			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2359 			txdr_hyper(uquad, tl);
2360 			retnum += NFSX_HYPER;
2361 			break;
2362 		case NFSATTRBIT_QUOTASOFT:
2363 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2364 				freenum = fs.f_bfree;
2365 			else
2366 				freenum = fs.f_bavail;
2367 #ifdef QUOTA
2368 			/*
2369 			 * ufs_quotactl() insists that the uid argument
2370 			 * equal p_ruid for non-root quota access, so
2371 			 * we'll just make sure that's the case.
2372 			 */
2373 			savuid = p->p_cred->p_ruid;
2374 			p->p_cred->p_ruid = cred->cr_uid;
2375 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2376 			    cred->cr_uid, (caddr_t)&dqb))
2377 			    freenum = min(dqb.dqb_bsoftlimit, freenum);
2378 			p->p_cred->p_ruid = savuid;
2379 #endif	/* QUOTA */
2380 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2381 			uquad = (u_int64_t)freenum;
2382 			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2383 			txdr_hyper(uquad, tl);
2384 			retnum += NFSX_HYPER;
2385 			break;
2386 		case NFSATTRBIT_QUOTAUSED:
2387 			freenum = 0;
2388 #ifdef QUOTA
2389 			/*
2390 			 * ufs_quotactl() insists that the uid argument
2391 			 * equal p_ruid for non-root quota access, so
2392 			 * we'll just make sure that's the case.
2393 			 */
2394 			savuid = p->p_cred->p_ruid;
2395 			p->p_cred->p_ruid = cred->cr_uid;
2396 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2397 			    cred->cr_uid, (caddr_t)&dqb))
2398 			    freenum = dqb.dqb_curblocks;
2399 			p->p_cred->p_ruid = savuid;
2400 #endif	/* QUOTA */
2401 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2402 			uquad = (u_int64_t)freenum;
2403 			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2404 			txdr_hyper(uquad, tl);
2405 			retnum += NFSX_HYPER;
2406 			break;
2407 		case NFSATTRBIT_RAWDEV:
2408 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
2409 			*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
2410 			*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
2411 			retnum += NFSX_V4SPECDATA;
2412 			break;
2413 		case NFSATTRBIT_SPACEAVAIL:
2414 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2415 			if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE, 0))
2416 				uquad = (u_int64_t)fs.f_bfree;
2417 			else
2418 				uquad = (u_int64_t)fs.f_bavail;
2419 			uquad *= fs.f_bsize;
2420 			txdr_hyper(uquad, tl);
2421 			retnum += NFSX_HYPER;
2422 			break;
2423 		case NFSATTRBIT_SPACEFREE:
2424 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2425 			uquad = (u_int64_t)fs.f_bfree;
2426 			uquad *= fs.f_bsize;
2427 			txdr_hyper(uquad, tl);
2428 			retnum += NFSX_HYPER;
2429 			break;
2430 		case NFSATTRBIT_SPACETOTAL:
2431 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2432 			uquad = (u_int64_t)fs.f_blocks;
2433 			uquad *= fs.f_bsize;
2434 			txdr_hyper(uquad, tl);
2435 			retnum += NFSX_HYPER;
2436 			break;
2437 		case NFSATTRBIT_SPACEUSED:
2438 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2439 			txdr_hyper(vap->va_bytes, tl);
2440 			retnum += NFSX_HYPER;
2441 			break;
2442 		case NFSATTRBIT_TIMEACCESS:
2443 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2444 			txdr_nfsv4time(&vap->va_atime, tl);
2445 			retnum += NFSX_V4TIME;
2446 			break;
2447 		case NFSATTRBIT_TIMEACCESSSET:
2448 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2449 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2450 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2451 				txdr_nfsv4time(&vap->va_atime, tl);
2452 				retnum += NFSX_V4SETTIME;
2453 			} else {
2454 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2455 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2456 				retnum += NFSX_UNSIGNED;
2457 			}
2458 			break;
2459 		case NFSATTRBIT_TIMEDELTA:
2460 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2461 			temptime.tv_sec = 0;
2462 			temptime.tv_nsec = 1000000000 / hz;
2463 			txdr_nfsv4time(&temptime, tl);
2464 			retnum += NFSX_V4TIME;
2465 			break;
2466 		case NFSATTRBIT_TIMEMETADATA:
2467 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2468 			txdr_nfsv4time(&vap->va_ctime, tl);
2469 			retnum += NFSX_V4TIME;
2470 			break;
2471 		case NFSATTRBIT_TIMEMODIFY:
2472 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2473 			txdr_nfsv4time(&vap->va_mtime, tl);
2474 			retnum += NFSX_V4TIME;
2475 			break;
2476 		case NFSATTRBIT_TIMEMODIFYSET:
2477 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2478 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2479 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2480 				txdr_nfsv4time(&vap->va_mtime, tl);
2481 				retnum += NFSX_V4SETTIME;
2482 			} else {
2483 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2484 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2485 				retnum += NFSX_UNSIGNED;
2486 			}
2487 			break;
2488 		case NFSATTRBIT_MOUNTEDONFILEID:
2489 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2490 			if (at_root != 0)
2491 				uquad = mounted_on_fileno;
2492 			else
2493 				uquad = (u_int64_t)vap->va_fileid;
2494 			txdr_hyper(uquad, tl);
2495 			retnum += NFSX_HYPER;
2496 			break;
2497 		case NFSATTRBIT_SUPPATTREXCLCREAT:
2498 			NFSSETSUPP_ATTRBIT(&attrbits);
2499 			NFSCLRNOTSETABLE_ATTRBIT(&attrbits);
2500 			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
2501 			retnum += nfsrv_putattrbit(nd, &attrbits);
2502 			break;
2503 		default:
2504 			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
2505 		};
2506 	    }
2507 	}
2508 	if (naclp != NULL)
2509 		acl_free(naclp);
2510 	*retnump = txdr_unsigned(retnum);
2511 	return (retnum + prefixnum);
2512 }
2513 
2514 /*
2515  * Put the attribute bits onto an mbuf list.
2516  * Return the number of bytes of output generated.
2517  */
2518 APPLESTATIC int
2519 nfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
2520 {
2521 	u_int32_t *tl;
2522 	int cnt, i, bytesize;
2523 
2524 	for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
2525 		if (attrbitp->bits[cnt - 1])
2526 			break;
2527 	bytesize = (cnt + 1) * NFSX_UNSIGNED;
2528 	NFSM_BUILD(tl, u_int32_t *, bytesize);
2529 	*tl++ = txdr_unsigned(cnt);
2530 	for (i = 0; i < cnt; i++)
2531 		*tl++ = txdr_unsigned(attrbitp->bits[i]);
2532 	return (bytesize);
2533 }
2534 
2535 /*
2536  * Convert a uid to a string.
2537  * If the lookup fails, just output the digits.
2538  * uid - the user id
2539  * cpp - points to a buffer of size NFSV4_SMALLSTR
2540  *       (malloc a larger one, as required)
2541  * retlenp - pointer to length to be returned
2542  */
2543 APPLESTATIC void
2544 nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2545 {
2546 	int i;
2547 	struct nfsusrgrp *usrp;
2548 	u_char *cp = *cpp;
2549 	uid_t tmp;
2550 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2551 
2552 	cnt = 0;
2553 tryagain:
2554 	NFSLOCKNAMEID();
2555 	if (nfsrv_dnsname) {
2556 		/*
2557 		 * Always map nfsrv_defaultuid to "nobody".
2558 		 */
2559 		if (uid == nfsrv_defaultuid) {
2560 			i = nfsrv_dnsnamelen + 7;
2561 			if (i > len) {
2562 				NFSUNLOCKNAMEID();
2563 				if (len > NFSV4_SMALLSTR)
2564 					free(cp, M_NFSSTRING);
2565 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2566 				*cpp = cp;
2567 				len = i;
2568 				goto tryagain;
2569 			}
2570 			*retlenp = i;
2571 			NFSBCOPY("nobody@", cp, 7);
2572 			cp += 7;
2573 			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2574 			NFSUNLOCKNAMEID();
2575 			return;
2576 		}
2577 		hasampersand = 0;
2578 		LIST_FOREACH(usrp, NFSUSERHASH(uid), lug_numhash) {
2579 			if (usrp->lug_uid == uid) {
2580 				if (usrp->lug_expiry < NFSD_MONOSEC)
2581 					break;
2582 				/*
2583 				 * If the name doesn't already have an '@'
2584 				 * in it, append @domainname to it.
2585 				 */
2586 				for (i = 0; i < usrp->lug_namelen; i++) {
2587 					if (usrp->lug_name[i] == '@') {
2588 						hasampersand = 1;
2589 						break;
2590 					}
2591 				}
2592 				if (hasampersand)
2593 					i = usrp->lug_namelen;
2594 				else
2595 					i = usrp->lug_namelen +
2596 					    nfsrv_dnsnamelen + 1;
2597 				if (i > len) {
2598 					NFSUNLOCKNAMEID();
2599 					if (len > NFSV4_SMALLSTR)
2600 						free(cp, M_NFSSTRING);
2601 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2602 					*cpp = cp;
2603 					len = i;
2604 					goto tryagain;
2605 				}
2606 				*retlenp = i;
2607 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2608 				if (!hasampersand) {
2609 					cp += usrp->lug_namelen;
2610 					*cp++ = '@';
2611 					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2612 				}
2613 				TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2614 				TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2615 				NFSUNLOCKNAMEID();
2616 				return;
2617 			}
2618 		}
2619 		NFSUNLOCKNAMEID();
2620 		cnt++;
2621 		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2622 		    NULL, p);
2623 		if (ret == 0 && cnt < 2)
2624 			goto tryagain;
2625 	} else {
2626 		NFSUNLOCKNAMEID();
2627 	}
2628 
2629 	/*
2630 	 * No match, just return a string of digits.
2631 	 */
2632 	tmp = uid;
2633 	i = 0;
2634 	while (tmp || i == 0) {
2635 		tmp /= 10;
2636 		i++;
2637 	}
2638 	len = (i > len) ? len : i;
2639 	*retlenp = len;
2640 	cp += (len - 1);
2641 	tmp = uid;
2642 	for (i = 0; i < len; i++) {
2643 		*cp-- = '0' + (tmp % 10);
2644 		tmp /= 10;
2645 	}
2646 	return;
2647 }
2648 
2649 /*
2650  * Convert a string to a uid.
2651  * If no conversion is possible return NFSERR_BADOWNER, otherwise
2652  * return 0.
2653  * If this is called from a client side mount using AUTH_SYS and the
2654  * string is made up entirely of digits, just convert the string to
2655  * a number.
2656  */
2657 APPLESTATIC int
2658 nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
2659     NFSPROC_T *p)
2660 {
2661 	int i;
2662 	char *cp, *endstr, *str0;
2663 	struct nfsusrgrp *usrp;
2664 	int cnt, ret;
2665 	int error = 0;
2666 	uid_t tuid;
2667 
2668 	if (len == 0) {
2669 		error = NFSERR_BADOWNER;
2670 		goto out;
2671 	}
2672 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2673 	str0 = str;
2674 	tuid = (uid_t)strtoul(str0, &endstr, 10);
2675 	if ((endstr - str0) == len) {
2676 		/* A numeric string. */
2677 		if ((nd->nd_flag & ND_KERBV) == 0 &&
2678 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2679 		      nfsd_enable_stringtouid != 0))
2680 			*uidp = tuid;
2681 		else
2682 			error = NFSERR_BADOWNER;
2683 		goto out;
2684 	}
2685 	/*
2686 	 * Look for an '@'.
2687 	 */
2688 	cp = strchr(str0, '@');
2689 	if (cp != NULL)
2690 		i = (int)(cp++ - str0);
2691 	else
2692 		i = len;
2693 
2694 	cnt = 0;
2695 tryagain:
2696 	NFSLOCKNAMEID();
2697 	/*
2698 	 * If an '@' is found and the domain name matches, search for the name
2699 	 * with dns stripped off.
2700 	 * Mixed case alpahbetics will match for the domain name, but all
2701 	 * upper case will not.
2702 	 */
2703 	if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
2704 	    (len - 1 - i) == nfsrv_dnsnamelen &&
2705 	    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2706 		len -= (nfsrv_dnsnamelen + 1);
2707 		*(cp - 1) = '\0';
2708 	}
2709 
2710 	/*
2711 	 * Check for the special case of "nobody".
2712 	 */
2713 	if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
2714 		*uidp = nfsrv_defaultuid;
2715 		NFSUNLOCKNAMEID();
2716 		error = 0;
2717 		goto out;
2718 	}
2719 
2720 	LIST_FOREACH(usrp, NFSUSERNAMEHASH(str, len), lug_namehash) {
2721 		if (usrp->lug_namelen == len &&
2722 		    !NFSBCMP(usrp->lug_name, str, len)) {
2723 			if (usrp->lug_expiry < NFSD_MONOSEC)
2724 				break;
2725 			*uidp = usrp->lug_uid;
2726 			TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2727 			TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2728 			NFSUNLOCKNAMEID();
2729 			error = 0;
2730 			goto out;
2731 		}
2732 	}
2733 	NFSUNLOCKNAMEID();
2734 	cnt++;
2735 	ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
2736 	    str, p);
2737 	if (ret == 0 && cnt < 2)
2738 		goto tryagain;
2739 	error = NFSERR_BADOWNER;
2740 
2741 out:
2742 	NFSEXITCODE(error);
2743 	return (error);
2744 }
2745 
2746 /*
2747  * Convert a gid to a string.
2748  * gid - the group id
2749  * cpp - points to a buffer of size NFSV4_SMALLSTR
2750  *       (malloc a larger one, as required)
2751  * retlenp - pointer to length to be returned
2752  */
2753 APPLESTATIC void
2754 nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2755 {
2756 	int i;
2757 	struct nfsusrgrp *usrp;
2758 	u_char *cp = *cpp;
2759 	gid_t tmp;
2760 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2761 
2762 	cnt = 0;
2763 tryagain:
2764 	NFSLOCKNAMEID();
2765 	if (nfsrv_dnsname) {
2766 		/*
2767 		 * Always map nfsrv_defaultgid to "nogroup".
2768 		 */
2769 		if (gid == nfsrv_defaultgid) {
2770 			i = nfsrv_dnsnamelen + 8;
2771 			if (i > len) {
2772 				NFSUNLOCKNAMEID();
2773 				if (len > NFSV4_SMALLSTR)
2774 					free(cp, M_NFSSTRING);
2775 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2776 				*cpp = cp;
2777 				len = i;
2778 				goto tryagain;
2779 			}
2780 			*retlenp = i;
2781 			NFSBCOPY("nogroup@", cp, 8);
2782 			cp += 8;
2783 			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2784 			NFSUNLOCKNAMEID();
2785 			return;
2786 		}
2787 		hasampersand = 0;
2788 		LIST_FOREACH(usrp, NFSGROUPHASH(gid), lug_numhash) {
2789 			if (usrp->lug_gid == gid) {
2790 				if (usrp->lug_expiry < NFSD_MONOSEC)
2791 					break;
2792 				/*
2793 				 * If the name doesn't already have an '@'
2794 				 * in it, append @domainname to it.
2795 				 */
2796 				for (i = 0; i < usrp->lug_namelen; i++) {
2797 					if (usrp->lug_name[i] == '@') {
2798 						hasampersand = 1;
2799 						break;
2800 					}
2801 				}
2802 				if (hasampersand)
2803 					i = usrp->lug_namelen;
2804 				else
2805 					i = usrp->lug_namelen +
2806 					    nfsrv_dnsnamelen + 1;
2807 				if (i > len) {
2808 					NFSUNLOCKNAMEID();
2809 					if (len > NFSV4_SMALLSTR)
2810 						free(cp, M_NFSSTRING);
2811 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2812 					*cpp = cp;
2813 					len = i;
2814 					goto tryagain;
2815 				}
2816 				*retlenp = i;
2817 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2818 				if (!hasampersand) {
2819 					cp += usrp->lug_namelen;
2820 					*cp++ = '@';
2821 					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2822 				}
2823 				TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2824 				TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2825 				NFSUNLOCKNAMEID();
2826 				return;
2827 			}
2828 		}
2829 		NFSUNLOCKNAMEID();
2830 		cnt++;
2831 		ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
2832 		    NULL, p);
2833 		if (ret == 0 && cnt < 2)
2834 			goto tryagain;
2835 	} else {
2836 		NFSUNLOCKNAMEID();
2837 	}
2838 
2839 	/*
2840 	 * No match, just return a string of digits.
2841 	 */
2842 	tmp = gid;
2843 	i = 0;
2844 	while (tmp || i == 0) {
2845 		tmp /= 10;
2846 		i++;
2847 	}
2848 	len = (i > len) ? len : i;
2849 	*retlenp = len;
2850 	cp += (len - 1);
2851 	tmp = gid;
2852 	for (i = 0; i < len; i++) {
2853 		*cp-- = '0' + (tmp % 10);
2854 		tmp /= 10;
2855 	}
2856 	return;
2857 }
2858 
2859 /*
2860  * Convert a string to a gid.
2861  * If no conversion is possible return NFSERR_BADOWNER, otherwise
2862  * return 0.
2863  * If this is called from a client side mount using AUTH_SYS and the
2864  * string is made up entirely of digits, just convert the string to
2865  * a number.
2866  */
2867 APPLESTATIC int
2868 nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
2869     NFSPROC_T *p)
2870 {
2871 	int i;
2872 	char *cp, *endstr, *str0;
2873 	struct nfsusrgrp *usrp;
2874 	int cnt, ret;
2875 	int error = 0;
2876 	gid_t tgid;
2877 
2878 	if (len == 0) {
2879 		error =  NFSERR_BADOWNER;
2880 		goto out;
2881 	}
2882 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2883 	str0 = str;
2884 	tgid = (gid_t)strtoul(str0, &endstr, 10);
2885 	if ((endstr - str0) == len) {
2886 		/* A numeric string. */
2887 		if ((nd->nd_flag & ND_KERBV) == 0 &&
2888 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2889 		      nfsd_enable_stringtouid != 0))
2890 			*gidp = tgid;
2891 		else
2892 			error = NFSERR_BADOWNER;
2893 		goto out;
2894 	}
2895 	/*
2896 	 * Look for an '@'.
2897 	 */
2898 	cp = strchr(str0, '@');
2899 	if (cp != NULL)
2900 		i = (int)(cp++ - str0);
2901 	else
2902 		i = len;
2903 
2904 	cnt = 0;
2905 tryagain:
2906 	NFSLOCKNAMEID();
2907 	/*
2908 	 * If an '@' is found and the dns name matches, search for the name
2909 	 * with the dns stripped off.
2910 	 */
2911 	if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
2912 	    (len - 1 - i) == nfsrv_dnsnamelen &&
2913 	    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2914 		len -= (nfsrv_dnsnamelen + 1);
2915 		*(cp - 1) = '\0';
2916 	}
2917 
2918 	/*
2919 	 * Check for the special case of "nogroup".
2920 	 */
2921 	if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
2922 		*gidp = nfsrv_defaultgid;
2923 		NFSUNLOCKNAMEID();
2924 		error = 0;
2925 		goto out;
2926 	}
2927 
2928 	LIST_FOREACH(usrp, NFSGROUPNAMEHASH(str, len), lug_namehash) {
2929 		if (usrp->lug_namelen == len &&
2930 		    !NFSBCMP(usrp->lug_name, str, len)) {
2931 			if (usrp->lug_expiry < NFSD_MONOSEC)
2932 				break;
2933 			*gidp = usrp->lug_gid;
2934 			TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
2935 			TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
2936 			NFSUNLOCKNAMEID();
2937 			error = 0;
2938 			goto out;
2939 		}
2940 	}
2941 	NFSUNLOCKNAMEID();
2942 	cnt++;
2943 	ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
2944 	    str, p);
2945 	if (ret == 0 && cnt < 2)
2946 		goto tryagain;
2947 	error = NFSERR_BADOWNER;
2948 
2949 out:
2950 	NFSEXITCODE(error);
2951 	return (error);
2952 }
2953 
2954 /*
2955  * Cmp len chars, allowing mixed case in the first argument to match lower
2956  * case in the second, but not if the first argument is all upper case.
2957  * Return 0 for a match, 1 otherwise.
2958  */
2959 static int
2960 nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len)
2961 {
2962 	int i;
2963 	u_char tmp;
2964 	int fndlower = 0;
2965 
2966 	for (i = 0; i < len; i++) {
2967 		if (*cp >= 'A' && *cp <= 'Z') {
2968 			tmp = *cp++ + ('a' - 'A');
2969 		} else {
2970 			tmp = *cp++;
2971 			if (tmp >= 'a' && tmp <= 'z')
2972 				fndlower = 1;
2973 		}
2974 		if (tmp != *cp2++)
2975 			return (1);
2976 	}
2977 	if (fndlower)
2978 		return (0);
2979 	else
2980 		return (1);
2981 }
2982 
2983 /*
2984  * Set the port for the nfsuserd.
2985  */
2986 APPLESTATIC int
2987 nfsrv_nfsuserdport(u_short port, NFSPROC_T *p)
2988 {
2989 	struct nfssockreq *rp;
2990 	struct sockaddr_in *ad;
2991 	int error;
2992 
2993 	NFSLOCKNAMEID();
2994 	if (nfsrv_nfsuserd) {
2995 		NFSUNLOCKNAMEID();
2996 		error = EPERM;
2997 		goto out;
2998 	}
2999 	nfsrv_nfsuserd = 1;
3000 	NFSUNLOCKNAMEID();
3001 	/*
3002 	 * Set up the socket record and connect.
3003 	 */
3004 	rp = &nfsrv_nfsuserdsock;
3005 	rp->nr_client = NULL;
3006 	rp->nr_sotype = SOCK_DGRAM;
3007 	rp->nr_soproto = IPPROTO_UDP;
3008 	rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
3009 	rp->nr_cred = NULL;
3010 	NFSSOCKADDRALLOC(rp->nr_nam);
3011 	NFSSOCKADDRSIZE(rp->nr_nam, sizeof (struct sockaddr_in));
3012 	ad = NFSSOCKADDR(rp->nr_nam, struct sockaddr_in *);
3013 	ad->sin_family = AF_INET;
3014 	ad->sin_addr.s_addr = htonl((u_int32_t)0x7f000001);	/* 127.0.0.1 */
3015 	ad->sin_port = port;
3016 	rp->nr_prog = RPCPROG_NFSUSERD;
3017 	rp->nr_vers = RPCNFSUSERD_VERS;
3018 	error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0);
3019 	if (error) {
3020 		NFSSOCKADDRFREE(rp->nr_nam);
3021 		nfsrv_nfsuserd = 0;
3022 	}
3023 out:
3024 	NFSEXITCODE(error);
3025 	return (error);
3026 }
3027 
3028 /*
3029  * Delete the nfsuserd port.
3030  */
3031 APPLESTATIC void
3032 nfsrv_nfsuserddelport(void)
3033 {
3034 
3035 	NFSLOCKNAMEID();
3036 	if (nfsrv_nfsuserd == 0) {
3037 		NFSUNLOCKNAMEID();
3038 		return;
3039 	}
3040 	nfsrv_nfsuserd = 0;
3041 	NFSUNLOCKNAMEID();
3042 	newnfs_disconnect(&nfsrv_nfsuserdsock);
3043 	NFSSOCKADDRFREE(nfsrv_nfsuserdsock.nr_nam);
3044 }
3045 
3046 /*
3047  * Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
3048  * name<-->id cache.
3049  * Returns 0 upon success, non-zero otherwise.
3050  */
3051 static int
3052 nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name, NFSPROC_T *p)
3053 {
3054 	u_int32_t *tl;
3055 	struct nfsrv_descript *nd;
3056 	int len;
3057 	struct nfsrv_descript nfsd;
3058 	struct ucred *cred;
3059 	int error;
3060 
3061 	NFSLOCKNAMEID();
3062 	if (nfsrv_nfsuserd == 0) {
3063 		NFSUNLOCKNAMEID();
3064 		error = EPERM;
3065 		goto out;
3066 	}
3067 	NFSUNLOCKNAMEID();
3068 	nd = &nfsd;
3069 	cred = newnfs_getcred();
3070 	nd->nd_flag = ND_GSSINITREPLY;
3071 	nfsrvd_rephead(nd);
3072 
3073 	nd->nd_procnum = procnum;
3074 	if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
3075 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3076 		if (procnum == RPCNFSUSERD_GETUID)
3077 			*tl = txdr_unsigned(uid);
3078 		else
3079 			*tl = txdr_unsigned(gid);
3080 	} else {
3081 		len = strlen(name);
3082 		(void) nfsm_strtom(nd, name, len);
3083 	}
3084 	error = newnfs_request(nd, NULL, NULL, &nfsrv_nfsuserdsock, NULL, NULL,
3085 		cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0, NULL, NULL);
3086 	NFSFREECRED(cred);
3087 	if (!error) {
3088 		mbuf_freem(nd->nd_mrep);
3089 		error = nd->nd_repstat;
3090 	}
3091 out:
3092 	NFSEXITCODE(error);
3093 	return (error);
3094 }
3095 
3096 /*
3097  * This function is called from the nfssvc(2) system call, to update the
3098  * kernel user/group name list(s) for the V4 owner and ownergroup attributes.
3099  */
3100 APPLESTATIC int
3101 nfssvc_idname(struct nfsd_idargs *nidp)
3102 {
3103 	struct nfsusrgrp *nusrp, *usrp, *newusrp;
3104 	struct nfsuserhashhead *hp;
3105 	int i;
3106 	int error = 0;
3107 	u_char *cp;
3108 
3109 	if (nidp->nid_flag & NFSID_INITIALIZE) {
3110 	    cp = (u_char *)malloc(nidp->nid_namelen + 1,
3111 		M_NFSSTRING, M_WAITOK);
3112 	    error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
3113 		nidp->nid_namelen);
3114 	    NFSLOCKNAMEID();
3115 	    if (nfsrv_dnsname) {
3116 		/*
3117 		 * Free up all the old stuff and reinitialize hash lists.
3118 		 */
3119 		TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
3120 			nfsrv_removeuser(usrp);
3121 		}
3122 		free(nfsrv_dnsname, M_NFSSTRING);
3123 		nfsrv_dnsname = NULL;
3124 	    }
3125 	    TAILQ_INIT(&nfsuserlruhead);
3126 	    for (i = 0; i < NFSUSERHASHSIZE; i++)
3127 		LIST_INIT(&nfsuserhash[i]);
3128 	    for (i = 0; i < NFSGROUPHASHSIZE; i++)
3129 		LIST_INIT(&nfsgrouphash[i]);
3130 	    for (i = 0; i < NFSUSERHASHSIZE; i++)
3131 		LIST_INIT(&nfsusernamehash[i]);
3132 	    for (i = 0; i < NFSGROUPHASHSIZE; i++)
3133 		LIST_INIT(&nfsgroupnamehash[i]);
3134 
3135 	    /*
3136 	     * Put name in "DNS" string.
3137 	     */
3138 	    if (!error) {
3139 		nfsrv_dnsname = cp;
3140 		nfsrv_dnsnamelen = nidp->nid_namelen;
3141 		nfsrv_defaultuid = nidp->nid_uid;
3142 		nfsrv_defaultgid = nidp->nid_gid;
3143 		nfsrv_usercnt = 0;
3144 		nfsrv_usermax = nidp->nid_usermax;
3145 	    }
3146 	    NFSUNLOCKNAMEID();
3147 	    if (error)
3148 		free(cp, M_NFSSTRING);
3149 	    goto out;
3150 	}
3151 
3152 	/*
3153 	 * malloc the new one now, so any potential sleep occurs before
3154 	 * manipulation of the lists.
3155 	 */
3156 	MALLOC(newusrp, struct nfsusrgrp *, sizeof (struct nfsusrgrp) +
3157 	    nidp->nid_namelen, M_NFSUSERGROUP, M_WAITOK);
3158 	error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
3159 	    nidp->nid_namelen);
3160 	if (error) {
3161 		free((caddr_t)newusrp, M_NFSUSERGROUP);
3162 		goto out;
3163 	}
3164 	newusrp->lug_namelen = nidp->nid_namelen;
3165 
3166 	NFSLOCKNAMEID();
3167 	/*
3168 	 * Delete old entries, as required.
3169 	 */
3170 	if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
3171 		hp = NFSUSERHASH(nidp->nid_uid);
3172 		LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
3173 			if (usrp->lug_uid == nidp->nid_uid)
3174 				nfsrv_removeuser(usrp);
3175 		}
3176 	}
3177 	if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
3178 		hp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3179 		LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
3180 			if (usrp->lug_namelen == newusrp->lug_namelen &&
3181 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3182 			    usrp->lug_namelen))
3183 				nfsrv_removeuser(usrp);
3184 		}
3185 	}
3186 	if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
3187 		hp = NFSGROUPHASH(nidp->nid_gid);
3188 		LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
3189 			if (usrp->lug_gid == nidp->nid_gid)
3190 				nfsrv_removeuser(usrp);
3191 		}
3192 	}
3193 	if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
3194 		hp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3195 		LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
3196 			if (usrp->lug_namelen == newusrp->lug_namelen &&
3197 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3198 			    usrp->lug_namelen))
3199 				nfsrv_removeuser(usrp);
3200 		}
3201 	}
3202 	TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
3203 		if (usrp->lug_expiry < NFSD_MONOSEC)
3204 			nfsrv_removeuser(usrp);
3205 	}
3206 	while (nfsrv_usercnt >= nfsrv_usermax) {
3207 		usrp = TAILQ_FIRST(&nfsuserlruhead);
3208 		nfsrv_removeuser(usrp);
3209 	}
3210 
3211 	/*
3212 	 * Now, we can add the new one.
3213 	 */
3214 	if (nidp->nid_usertimeout)
3215 		newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
3216 	else
3217 		newusrp->lug_expiry = NFSD_MONOSEC + 5;
3218 	if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
3219 		newusrp->lug_uid = nidp->nid_uid;
3220 		LIST_INSERT_HEAD(NFSUSERHASH(newusrp->lug_uid), newusrp,
3221 		    lug_numhash);
3222 		LIST_INSERT_HEAD(NFSUSERNAMEHASH(newusrp->lug_name,
3223 		    newusrp->lug_namelen), newusrp, lug_namehash);
3224 		TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
3225 		nfsrv_usercnt++;
3226 	} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
3227 		newusrp->lug_gid = nidp->nid_gid;
3228 		LIST_INSERT_HEAD(NFSGROUPHASH(newusrp->lug_gid), newusrp,
3229 		    lug_numhash);
3230 		LIST_INSERT_HEAD(NFSGROUPNAMEHASH(newusrp->lug_name,
3231 		    newusrp->lug_namelen), newusrp, lug_namehash);
3232 		TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
3233 		nfsrv_usercnt++;
3234 	} else
3235 		FREE((caddr_t)newusrp, M_NFSUSERGROUP);
3236 	NFSUNLOCKNAMEID();
3237 out:
3238 	NFSEXITCODE(error);
3239 	return (error);
3240 }
3241 
3242 /*
3243  * Remove a user/group name element.
3244  */
3245 static void
3246 nfsrv_removeuser(struct nfsusrgrp *usrp)
3247 {
3248 
3249 	NFSNAMEIDREQUIRED();
3250 	LIST_REMOVE(usrp, lug_numhash);
3251 	LIST_REMOVE(usrp, lug_namehash);
3252 	TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
3253 	nfsrv_usercnt--;
3254 	FREE((caddr_t)usrp, M_NFSUSERGROUP);
3255 }
3256 
3257 /*
3258  * This function scans a byte string and checks for UTF-8 compliance.
3259  * It returns 0 if it conforms and NFSERR_INVAL if not.
3260  */
3261 APPLESTATIC int
3262 nfsrv_checkutf8(u_int8_t *cp, int len)
3263 {
3264 	u_int32_t val = 0x0;
3265 	int cnt = 0, gotd = 0, shift = 0;
3266 	u_int8_t byte;
3267 	static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
3268 	int error = 0;
3269 
3270 	/*
3271 	 * Here are what the variables are used for:
3272 	 * val - the calculated value of a multibyte char, used to check
3273 	 *       that it was coded with the correct range
3274 	 * cnt - the number of 10xxxxxx bytes to follow
3275 	 * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
3276 	 * shift - lower order bits of range (ie. "val >> shift" should
3277 	 *       not be 0, in other words, dividing by the lower bound
3278 	 *       of the range should get a non-zero value)
3279 	 * byte - used to calculate cnt
3280 	 */
3281 	while (len > 0) {
3282 		if (cnt > 0) {
3283 			/* This handles the 10xxxxxx bytes */
3284 			if ((*cp & 0xc0) != 0x80 ||
3285 			    (gotd && (*cp & 0x20))) {
3286 				error = NFSERR_INVAL;
3287 				goto out;
3288 			}
3289 			gotd = 0;
3290 			val <<= 6;
3291 			val |= (*cp & 0x3f);
3292 			cnt--;
3293 			if (cnt == 0 && (val >> shift) == 0x0) {
3294 				error = NFSERR_INVAL;
3295 				goto out;
3296 			}
3297 		} else if (*cp & 0x80) {
3298 			/* first byte of multi byte char */
3299 			byte = *cp;
3300 			while ((byte & 0x40) && cnt < 6) {
3301 				cnt++;
3302 				byte <<= 1;
3303 			}
3304 			if (cnt == 0 || cnt == 6) {
3305 				error = NFSERR_INVAL;
3306 				goto out;
3307 			}
3308 			val = (*cp & (0x3f >> cnt));
3309 			shift = utf8_shift[cnt - 1];
3310 			if (cnt == 2 && val == 0xd)
3311 				/* Check for the 0xd800-0xdfff case */
3312 				gotd = 1;
3313 		}
3314 		cp++;
3315 		len--;
3316 	}
3317 	if (cnt > 0)
3318 		error = NFSERR_INVAL;
3319 
3320 out:
3321 	NFSEXITCODE(error);
3322 	return (error);
3323 }
3324 
3325 /*
3326  * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
3327  * strings, one with the root path in it and the other with the list of
3328  * locations. The list is in the same format as is found in nfr_refs.
3329  * It is a "," separated list of entries, where each of them is of the
3330  * form <server>:<rootpath>. For example
3331  * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
3332  * The nilp argument is set to 1 for the special case of a null fs_root
3333  * and an empty server list.
3334  * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
3335  * number of xdr bytes parsed in sump.
3336  */
3337 static int
3338 nfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
3339     int *sump, int *nilp)
3340 {
3341 	u_int32_t *tl;
3342 	u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
3343 	int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
3344 	struct list {
3345 		SLIST_ENTRY(list) next;
3346 		int len;
3347 		u_char host[1];
3348 	} *lsp, *nlsp;
3349 	SLIST_HEAD(, list) head;
3350 
3351 	*fsrootp = NULL;
3352 	*srvp = NULL;
3353 	*nilp = 0;
3354 
3355 	/*
3356 	 * Get the fs_root path and check for the special case of null path
3357 	 * and 0 length server list.
3358 	 */
3359 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3360 	len = fxdr_unsigned(int, *tl);
3361 	if (len < 0 || len > 10240) {
3362 		error = NFSERR_BADXDR;
3363 		goto nfsmout;
3364 	}
3365 	if (len == 0) {
3366 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3367 		if (*tl != 0) {
3368 			error = NFSERR_BADXDR;
3369 			goto nfsmout;
3370 		}
3371 		*nilp = 1;
3372 		*sump = 2 * NFSX_UNSIGNED;
3373 		error = 0;
3374 		goto nfsmout;
3375 	}
3376 	cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
3377 	error = nfsrv_mtostr(nd, cp, len);
3378 	if (!error) {
3379 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3380 		cnt = fxdr_unsigned(int, *tl);
3381 		if (cnt <= 0)
3382 			error = NFSERR_BADXDR;
3383 	}
3384 	if (error)
3385 		goto nfsmout;
3386 
3387 	/*
3388 	 * Now, loop through the location list and make up the srvlist.
3389 	 */
3390 	xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3391 	cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
3392 	slen = 1024;
3393 	siz = 0;
3394 	for (i = 0; i < cnt; i++) {
3395 		SLIST_INIT(&head);
3396 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3397 		nsrv = fxdr_unsigned(int, *tl);
3398 		if (nsrv <= 0) {
3399 			error = NFSERR_BADXDR;
3400 			goto nfsmout;
3401 		}
3402 
3403 		/*
3404 		 * Handle the first server by putting it in the srvstr.
3405 		 */
3406 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3407 		len = fxdr_unsigned(int, *tl);
3408 		if (len <= 0 || len > 1024) {
3409 			error = NFSERR_BADXDR;
3410 			goto nfsmout;
3411 		}
3412 		nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
3413 		if (cp3 != cp2) {
3414 			*cp3++ = ',';
3415 			siz++;
3416 		}
3417 		error = nfsrv_mtostr(nd, cp3, len);
3418 		if (error)
3419 			goto nfsmout;
3420 		cp3 += len;
3421 		*cp3++ = ':';
3422 		siz += (len + 1);
3423 		xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3424 		for (j = 1; j < nsrv; j++) {
3425 			/*
3426 			 * Yuck, put them in an slist and process them later.
3427 			 */
3428 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3429 			len = fxdr_unsigned(int, *tl);
3430 			if (len <= 0 || len > 1024) {
3431 				error = NFSERR_BADXDR;
3432 				goto nfsmout;
3433 			}
3434 			lsp = (struct list *)malloc(sizeof (struct list)
3435 			    + len, M_TEMP, M_WAITOK);
3436 			error = nfsrv_mtostr(nd, lsp->host, len);
3437 			if (error)
3438 				goto nfsmout;
3439 			xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3440 			lsp->len = len;
3441 			SLIST_INSERT_HEAD(&head, lsp, next);
3442 		}
3443 
3444 		/*
3445 		 * Finally, we can get the path.
3446 		 */
3447 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3448 		len = fxdr_unsigned(int, *tl);
3449 		if (len <= 0 || len > 1024) {
3450 			error = NFSERR_BADXDR;
3451 			goto nfsmout;
3452 		}
3453 		nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
3454 		error = nfsrv_mtostr(nd, cp3, len);
3455 		if (error)
3456 			goto nfsmout;
3457 		xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3458 		str = cp3;
3459 		stringlen = len;
3460 		cp3 += len;
3461 		siz += len;
3462 		SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
3463 			nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
3464 			    &cp2, &cp3, &slen);
3465 			*cp3++ = ',';
3466 			NFSBCOPY(lsp->host, cp3, lsp->len);
3467 			cp3 += lsp->len;
3468 			*cp3++ = ':';
3469 			NFSBCOPY(str, cp3, stringlen);
3470 			cp3 += stringlen;
3471 			*cp3 = '\0';
3472 			siz += (lsp->len + stringlen + 2);
3473 			free((caddr_t)lsp, M_TEMP);
3474 		}
3475 	}
3476 	*fsrootp = cp;
3477 	*srvp = cp2;
3478 	*sump = xdrsum;
3479 	NFSEXITCODE2(0, nd);
3480 	return (0);
3481 nfsmout:
3482 	if (cp != NULL)
3483 		free(cp, M_NFSSTRING);
3484 	if (cp2 != NULL)
3485 		free(cp2, M_NFSSTRING);
3486 	NFSEXITCODE2(error, nd);
3487 	return (error);
3488 }
3489 
3490 /*
3491  * Make the malloc'd space large enough. This is a pain, but the xdr
3492  * doesn't set an upper bound on the side, so...
3493  */
3494 static void
3495 nfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
3496 {
3497 	u_char *cp;
3498 	int i;
3499 
3500 	if (siz <= *slenp)
3501 		return;
3502 	cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
3503 	NFSBCOPY(*cpp, cp, *slenp);
3504 	free(*cpp, M_NFSSTRING);
3505 	i = *cpp2 - *cpp;
3506 	*cpp = cp;
3507 	*cpp2 = cp + i;
3508 	*slenp = siz + 1024;
3509 }
3510 
3511 /*
3512  * Initialize the reply header data structures.
3513  */
3514 APPLESTATIC void
3515 nfsrvd_rephead(struct nfsrv_descript *nd)
3516 {
3517 	mbuf_t mreq;
3518 
3519 	/*
3520 	 * If this is a big reply, use a cluster.
3521 	 */
3522 	if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
3523 	    nfs_bigreply[nd->nd_procnum]) {
3524 		NFSMCLGET(mreq, M_WAITOK);
3525 		nd->nd_mreq = mreq;
3526 		nd->nd_mb = mreq;
3527 	} else {
3528 		NFSMGET(mreq);
3529 		nd->nd_mreq = mreq;
3530 		nd->nd_mb = mreq;
3531 	}
3532 	nd->nd_bpos = NFSMTOD(mreq, caddr_t);
3533 	mbuf_setlen(mreq, 0);
3534 
3535 	if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
3536 		NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
3537 }
3538 
3539 /*
3540  * Lock a socket against others.
3541  * Currently used to serialize connect/disconnect attempts.
3542  */
3543 int
3544 newnfs_sndlock(int *flagp)
3545 {
3546 	struct timespec ts;
3547 
3548 	NFSLOCKSOCK();
3549 	while (*flagp & NFSR_SNDLOCK) {
3550 		*flagp |= NFSR_WANTSND;
3551 		ts.tv_sec = 0;
3552 		ts.tv_nsec = 0;
3553 		(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
3554 		    PZERO - 1, "nfsndlck", &ts);
3555 	}
3556 	*flagp |= NFSR_SNDLOCK;
3557 	NFSUNLOCKSOCK();
3558 	return (0);
3559 }
3560 
3561 /*
3562  * Unlock the stream socket for others.
3563  */
3564 void
3565 newnfs_sndunlock(int *flagp)
3566 {
3567 
3568 	NFSLOCKSOCK();
3569 	if ((*flagp & NFSR_SNDLOCK) == 0)
3570 		panic("nfs sndunlock");
3571 	*flagp &= ~NFSR_SNDLOCK;
3572 	if (*flagp & NFSR_WANTSND) {
3573 		*flagp &= ~NFSR_WANTSND;
3574 		wakeup((caddr_t)flagp);
3575 	}
3576 	NFSUNLOCKSOCK();
3577 }
3578 
3579 APPLESTATIC int
3580 nfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_storage *sa,
3581     int *isudp)
3582 {
3583 	struct sockaddr_in *sad;
3584 	struct sockaddr_in6 *sad6;
3585 	struct in_addr saddr;
3586 	uint32_t portnum, *tl;
3587 	int af = 0, i, j, k;
3588 	char addr[64], protocol[5], *cp;
3589 	int cantparse = 0, error = 0;
3590 	uint16_t portv;
3591 
3592 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3593 	i = fxdr_unsigned(int, *tl);
3594 	if (i >= 3 && i <= 4) {
3595 		error = nfsrv_mtostr(nd, protocol, i);
3596 		if (error)
3597 			goto nfsmout;
3598 		if (strcmp(protocol, "tcp") == 0) {
3599 			af = AF_INET;
3600 			*isudp = 0;
3601 		} else if (strcmp(protocol, "udp") == 0) {
3602 			af = AF_INET;
3603 			*isudp = 1;
3604 		} else if (strcmp(protocol, "tcp6") == 0) {
3605 			af = AF_INET6;
3606 			*isudp = 0;
3607 		} else if (strcmp(protocol, "udp6") == 0) {
3608 			af = AF_INET6;
3609 			*isudp = 1;
3610 		} else
3611 			cantparse = 1;
3612 	} else {
3613 		cantparse = 1;
3614 		if (i > 0) {
3615 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
3616 			if (error)
3617 				goto nfsmout;
3618 		}
3619 	}
3620 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3621 	i = fxdr_unsigned(int, *tl);
3622 	if (i < 0) {
3623 		error = NFSERR_BADXDR;
3624 		goto nfsmout;
3625 	} else if (cantparse == 0 && i >= 11 && i < 64) {
3626 		/*
3627 		 * The shortest address is 11chars and the longest is < 64.
3628 		 */
3629 		error = nfsrv_mtostr(nd, addr, i);
3630 		if (error)
3631 			goto nfsmout;
3632 
3633 		/* Find the port# at the end and extract that. */
3634 		i = strlen(addr);
3635 		k = 0;
3636 		cp = &addr[i - 1];
3637 		/* Count back two '.'s from end to get port# field. */
3638 		for (j = 0; j < i; j++) {
3639 			if (*cp == '.') {
3640 				k++;
3641 				if (k == 2)
3642 					break;
3643 			}
3644 			cp--;
3645 		}
3646 		if (k == 2) {
3647 			/*
3648 			 * The NFSv4 port# is appended as .N.N, where N is
3649 			 * a decimal # in the range 0-255, just like an inet4
3650 			 * address. Cheat and use inet_aton(), which will
3651 			 * return a Class A address and then shift the high
3652 			 * order 8bits over to convert it to the port#.
3653 			 */
3654 			*cp++ = '\0';
3655 			if (inet_aton(cp, &saddr) == 1) {
3656 				portnum = ntohl(saddr.s_addr);
3657 				portv = (uint16_t)((portnum >> 16) |
3658 				    (portnum & 0xff));
3659 			} else
3660 				cantparse = 1;
3661 		} else
3662 			cantparse = 1;
3663 		if (cantparse == 0) {
3664 			if (af == AF_INET) {
3665 				sad = (struct sockaddr_in *)sa;
3666 				if (inet_pton(af, addr, &sad->sin_addr) == 1) {
3667 					sad->sin_len = sizeof(*sad);
3668 					sad->sin_family = AF_INET;
3669 					sad->sin_port = htons(portv);
3670 					return (0);
3671 				}
3672 			} else {
3673 				sad6 = (struct sockaddr_in6 *)sa;
3674 				if (inet_pton(af, addr, &sad6->sin6_addr)
3675 				    == 1) {
3676 					sad6->sin6_len = sizeof(*sad6);
3677 					sad6->sin6_family = AF_INET6;
3678 					sad6->sin6_port = htons(portv);
3679 					return (0);
3680 				}
3681 			}
3682 		}
3683 	} else {
3684 		if (i > 0) {
3685 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
3686 			if (error)
3687 				goto nfsmout;
3688 		}
3689 	}
3690 	error = EPERM;
3691 nfsmout:
3692 	return (error);
3693 }
3694 
3695 /*
3696  * Handle an NFSv4.1 Sequence request for the session.
3697  * If reply != NULL, use it to return the cached reply, as required.
3698  * The client gets a cached reply via this call for callbacks, however the
3699  * server gets a cached reply via the nfsv4_seqsess_cachereply() call.
3700  */
3701 int
3702 nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot,
3703     struct nfsslot *slots, struct mbuf **reply, uint16_t maxslot)
3704 {
3705 	int error;
3706 
3707 	error = 0;
3708 	if (reply != NULL)
3709 		*reply = NULL;
3710 	if (slotid > maxslot)
3711 		return (NFSERR_BADSLOT);
3712 	if (seqid == slots[slotid].nfssl_seq) {
3713 		/* A retry. */
3714 		if (slots[slotid].nfssl_inprog != 0)
3715 			error = NFSERR_DELAY;
3716 		else if (slots[slotid].nfssl_reply != NULL) {
3717 			if (reply != NULL) {
3718 				*reply = slots[slotid].nfssl_reply;
3719 				slots[slotid].nfssl_reply = NULL;
3720 			}
3721 			slots[slotid].nfssl_inprog = 1;
3722 			error = NFSERR_REPLYFROMCACHE;
3723 		} else
3724 			/* No reply cached, so just do it. */
3725 			slots[slotid].nfssl_inprog = 1;
3726 	} else if ((slots[slotid].nfssl_seq + 1) == seqid) {
3727 		if (slots[slotid].nfssl_reply != NULL)
3728 			m_freem(slots[slotid].nfssl_reply);
3729 		slots[slotid].nfssl_reply = NULL;
3730 		slots[slotid].nfssl_inprog = 1;
3731 		slots[slotid].nfssl_seq++;
3732 	} else
3733 		error = NFSERR_SEQMISORDERED;
3734 	return (error);
3735 }
3736 
3737 /*
3738  * Cache this reply for the slot.
3739  * Use the "rep" argument to return the cached reply if repstat is set to
3740  * NFSERR_REPLYFROMCACHE. The client never sets repstat to this value.
3741  */
3742 void
3743 nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, int repstat,
3744    struct mbuf **rep)
3745 {
3746 
3747 	if (repstat == NFSERR_REPLYFROMCACHE) {
3748 		*rep = slots[slotid].nfssl_reply;
3749 		slots[slotid].nfssl_reply = NULL;
3750 	} else {
3751 		if (slots[slotid].nfssl_reply != NULL)
3752 			m_freem(slots[slotid].nfssl_reply);
3753 		slots[slotid].nfssl_reply = *rep;
3754 	}
3755 	slots[slotid].nfssl_inprog = 0;
3756 }
3757 
3758 /*
3759  * Generate the xdr for an NFSv4.1 Sequence Operation.
3760  */
3761 APPLESTATIC void
3762 nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd,
3763     struct nfsclsession *sep, int dont_replycache)
3764 {
3765 	uint32_t *tl, slotseq = 0;
3766 	int error, maxslot, slotpos;
3767 	uint8_t sessionid[NFSX_V4SESSIONID];
3768 
3769 	error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot, &slotseq,
3770 	    sessionid);
3771 	if (error != 0)
3772 		return;
3773 	KASSERT(maxslot >= 0, ("nfscl_setsequence neg maxslot"));
3774 
3775 	/* Build the Sequence arguments. */
3776 	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
3777 	bcopy(sessionid, tl, NFSX_V4SESSIONID);
3778 	tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
3779 	nd->nd_slotseq = tl;
3780 	*tl++ = txdr_unsigned(slotseq);
3781 	*tl++ = txdr_unsigned(slotpos);
3782 	*tl++ = txdr_unsigned(maxslot);
3783 	if (dont_replycache == 0)
3784 		*tl = newnfs_true;
3785 	else
3786 		*tl = newnfs_false;
3787 	nd->nd_flag |= ND_HASSEQUENCE;
3788 }
3789 
3790 int
3791 nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep,
3792     int *slotposp, int *maxslotp, uint32_t *slotseqp, uint8_t *sessionid)
3793 {
3794 	int i, maxslot, slotpos;
3795 	uint64_t bitval;
3796 
3797 	/* Find an unused slot. */
3798 	slotpos = -1;
3799 	maxslot = -1;
3800 	mtx_lock(&sep->nfsess_mtx);
3801 	do {
3802 		bitval = 1;
3803 		for (i = 0; i < sep->nfsess_foreslots; i++) {
3804 			if ((bitval & sep->nfsess_slots) == 0) {
3805 				slotpos = i;
3806 				sep->nfsess_slots |= bitval;
3807 				sep->nfsess_slotseq[i]++;
3808 				*slotseqp = sep->nfsess_slotseq[i];
3809 				break;
3810 			}
3811 			bitval <<= 1;
3812 		}
3813 		if (slotpos == -1) {
3814 			/*
3815 			 * If a forced dismount is in progress, just return.
3816 			 * This RPC attempt will fail when it calls
3817 			 * newnfs_request().
3818 			 */
3819 			if (nmp != NULL &&
3820 			    (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)
3821 			    != 0) {
3822 				mtx_unlock(&sep->nfsess_mtx);
3823 				return (ESTALE);
3824 			}
3825 			/* Wake up once/sec, to check for a forced dismount. */
3826 			(void)mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx,
3827 			    PZERO, "nfsclseq", hz);
3828 		}
3829 	} while (slotpos == -1);
3830 	/* Now, find the highest slot in use. (nfsc_slots is 64bits) */
3831 	bitval = 1;
3832 	for (i = 0; i < 64; i++) {
3833 		if ((bitval & sep->nfsess_slots) != 0)
3834 			maxslot = i;
3835 		bitval <<= 1;
3836 	}
3837 	bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID);
3838 	mtx_unlock(&sep->nfsess_mtx);
3839 	*slotposp = slotpos;
3840 	*maxslotp = maxslot;
3841 	return (0);
3842 }
3843 
3844 /*
3845  * Free a session slot.
3846  */
3847 APPLESTATIC void
3848 nfsv4_freeslot(struct nfsclsession *sep, int slot)
3849 {
3850 	uint64_t bitval;
3851 
3852 	bitval = 1;
3853 	if (slot > 0)
3854 		bitval <<= slot;
3855 	mtx_lock(&sep->nfsess_mtx);
3856 	if ((bitval & sep->nfsess_slots) == 0)
3857 		printf("freeing free slot!!\n");
3858 	sep->nfsess_slots &= ~bitval;
3859 	wakeup(&sep->nfsess_slots);
3860 	mtx_unlock(&sep->nfsess_mtx);
3861 }
3862 
3863