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