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