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