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