nfs_commonsubs.c revision 306809
167754Smsmith/*-
267754Smsmith * Copyright (c) 1989, 1993
377424Smsmith *	The Regents of the University of California.  All rights reserved.
467754Smsmith *
567754Smsmith * This code is derived from software contributed to Berkeley by
667754Smsmith * Rick Macklem at The University of Guelph.
7217365Sjkim *
8306536Sjkim * Redistribution and use in source and binary forms, with or without
970243Smsmith * modification, are permitted provided that the following conditions
1067754Smsmith * are met:
11217365Sjkim * 1. Redistributions of source code must retain the above copyright
12217365Sjkim *    notice, this list of conditions and the following disclaimer.
13217365Sjkim * 2. Redistributions in binary form must reproduce the above copyright
14217365Sjkim *    notice, this list of conditions and the following disclaimer in the
15217365Sjkim *    documentation and/or other materials provided with the distribution.
16217365Sjkim * 4. Neither the name of the University nor the names of its contributors
17217365Sjkim *    may be used to endorse or promote products derived from this software
18217365Sjkim *    without specific prior written permission.
19217365Sjkim *
20217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23217365Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24217365Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2567754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28217365Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2967754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30217365Sjkim * SUCH DAMAGE.
31217365Sjkim *
32217365Sjkim */
33217365Sjkim
34217365Sjkim#include <sys/cdefs.h>
35217365Sjkim__FBSDID("$FreeBSD: stable/10/sys/fs/nfs/nfs_commonsubs.c 306809 2016-10-07 14:46:34Z emaste $");
36217365Sjkim
37217365Sjkim/*
38217365Sjkim * These functions support the macros and help fiddle mbuf chains for
39217365Sjkim * the nfs op functions. They do things like create the rpc header and
40217365Sjkim * copy data between mbuf chains and uio lists.
41217365Sjkim */
42217365Sjkim#ifndef APPLEKEXT
4367754Smsmith#include "opt_inet6.h"
44193341Sjkim
45193341Sjkim#include <fs/nfs/nfsport.h>
46193341Sjkim
47193341Sjkim#include <security/mac/mac_framework.h>
48193341Sjkim
4967754Smsmith/*
50193267Sjkim * Data items converted to xdr at startup, since they are constant
5177424Smsmith * This is kinda hokey, but may save a little time doing byte swaps
5291116Smsmith */
5367754Smsmithu_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
5467754Smsmith
5567754Smsmith/* And other global data */
5667754Smsmithnfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
57102550Siwasaki		      NFFIFO, NFNON };
5867754Smsmithenum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
59151937Sjkimenum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
60151937Sjkimstruct timeval nfsboottime;	/* Copy boottime once, so it never changes */
61151937Sjkimint nfscl_ticks;
62151937Sjkimint nfsrv_useacl = 1;
63306536Sjkimstruct nfssockreq nfsrv_nfsuserdsock;
64306536Sjkimint nfsrv_nfsuserd = 0;
65151937Sjkimstruct nfsreqhead nfsd_reqq;
66151937Sjkimuid_t nfsrv_defaultuid;
67151937Sjkimgid_t nfsrv_defaultgid;
68306536Sjkimint nfsrv_lease = NFSRV_LEASE;
69306536Sjkimint ncl_mbuf_mlen = MLEN;
70151937Sjkimint nfsd_enable_stringtouid = 0;
71151937SjkimNFSNAMEIDMUTEX;
72167802SjkimNFSSOCKMUTEX;
73167802Sjkimextern int nfsrv_lughashsize;
74167802Sjkim
75151937Sjkim/*
76151937Sjkim * This array of structures indicates, for V4:
77167802Sjkim * retfh - which of 3 types of calling args are used
78151937Sjkim *	0 - doesn't change cfh or use a sfh
79151937Sjkim *	1 - replaces cfh with a new one (unless it returns an error status)
80151937Sjkim *	2 - uses cfh and sfh
81167802Sjkim * needscfh - if the op wants a cfh and premtime
82151937Sjkim *	0 - doesn't use a cfh
83151937Sjkim *	1 - uses a cfh, but doesn't want pre-op attributes
84151937Sjkim *	2 - uses a cfh and wants pre-op attributes
85151937Sjkim * savereply - indicates a non-idempotent Op
86151937Sjkim *	0 - not non-idempotent
87151937Sjkim *	1 - non-idempotent
8867754Smsmith * Ops that are ordered via seqid# are handled separately from these
89167802Sjkim * non-idempotent Ops.
90167802Sjkim * Define it here, since it is used by both the client and server.
91167802Sjkim */
92167802Sjkimstruct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS] = {
93167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
94167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
95167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* undef */
96167802Sjkim	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Access */
97167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Close */
98167802Sjkim	{ 0, 2, 0, 1, LK_EXCLUSIVE, 1 },		/* Commit */
99167802Sjkim	{ 1, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Create */
100167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegpurge */
101167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Delegreturn */
102167802Sjkim	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Getattr */
103167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* GetFH */
104167802Sjkim	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Link */
105167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Lock */
106167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockT */
107167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* LockU */
108167802Sjkim	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookup */
109167802Sjkim	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1 },		/* Lookupp */
110167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* NVerify */
111167802Sjkim	{ 1, 1, 0, 1, LK_EXCLUSIVE, 1 },		/* Open */
112167802Sjkim	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenAttr */
113167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenConfirm */
114167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* OpenDowngrade */
115281075Sdim	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutFH */
116167802Sjkim	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutPubFH */
117167802Sjkim	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* PutRootFH */
118167802Sjkim	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Read */
119281075Sdim	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* Readdir */
120167802Sjkim	{ 0, 1, 0, 0, LK_SHARED, 1 },			/* ReadLink */
121167802Sjkim	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Remove */
122281075Sdim	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1 },		/* Rename */
123167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Renew */
124167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* RestoreFH */
125167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SaveFH */
126167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* SecInfo */
127167802Sjkim	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Setattr */
128167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientID */
129167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* SetClientIDConfirm */
130167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Verify */
131167802Sjkim	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1 },		/* Write */
132234623Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* ReleaseLockOwner */
133281075Sdim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Backchannel Ctrl */
134281075Sdim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Bind Conn to Sess */
135167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Exchange ID */
136167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Create Session */
137167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy Session */
138167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Free StateID */
139167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Dir Deleg */
140167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device Info */
141167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Get Device List */
142167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Commit */
143193267Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Get */
144167802Sjkim	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1 },		/* Layout Return */
145167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Secinfo No name */
146217365Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Sequence */
147193267Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Set SSV */
148167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Test StateID */
149167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Want Delegation */
150167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0 },		/* Destroy ClientID */
151167802Sjkim	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1 },		/* Reclaim Complete */
152167802Sjkim};
153167802Sjkim#endif	/* !APPLEKEXT */
154167802Sjkim
155167802Sjkimstatic int ncl_mbuf_mhlen = MHLEN;
156281075Sdimstatic int nfsrv_usercnt = 0;
157167802Sjkimstatic int nfsrv_dnsnamelen;
158167802Sjkimstatic u_char *nfsrv_dnsname = NULL;
159167802Sjkimstatic int nfsrv_usermax = 999999999;
160281075Sdimstruct nfsrv_lughash {
161167802Sjkim	struct mtx		mtx;
162167802Sjkim	struct nfsuserhashhead	lughead;
163167802Sjkim};
164167802Sjkimstatic struct nfsrv_lughash	*nfsuserhash;
165167802Sjkimstatic struct nfsrv_lughash	*nfsusernamehash;
166281075Sdimstatic struct nfsrv_lughash	*nfsgrouphash;
167167802Sjkimstatic struct nfsrv_lughash	*nfsgroupnamehash;
168167802Sjkim
169167802Sjkim/*
170167802Sjkim * This static array indicates whether or not the RPC generates a large
171281075Sdim * reply. This is used by nfs_reply() to decide whether or not an mbuf
172167802Sjkim * cluster should be allocated. (If a cluster is required by an RPC
173167802Sjkim * marked 0 in this array, the code will still work, just not quite as
174281075Sdim * efficiently.)
175167802Sjkim */
176167802Sjkimint nfs_bigreply[NFSV41_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
177167802Sjkim    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,
178281075Sdim    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
179167802Sjkim
180167802Sjkim/* local functions */
181167802Sjkimstatic int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
182167802Sjkimstatic void nfsv4_wanted(struct nfsv4lock *lp);
183234623Sjkimstatic int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
184281075Sdimstatic int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
185281075Sdim    NFSPROC_T *p);
186167802Sjkimstatic void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
187167802Sjkimstatic int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
188167802Sjkim    int *, int *);
189167802Sjkimstatic void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
190167802Sjkim
191167802Sjkim
192167802Sjkim#ifndef APPLE
193167802Sjkim/*
194234623Sjkim * copies mbuf chain to the uio scatter/gather list
195234623Sjkim */
196167802Sjkimint
197167802Sjkimnfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
198167802Sjkim{
199167802Sjkim	char *mbufcp, *uiocp;
200167802Sjkim	int xfer, left, len;
201167802Sjkim	mbuf_t mp;
202234623Sjkim	long uiosiz, rem;
203234623Sjkim	int error = 0;
204167802Sjkim
205167802Sjkim	mp = nd->nd_md;
206167802Sjkim	mbufcp = nd->nd_dpos;
207167802Sjkim	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - mbufcp;
208167802Sjkim	rem = NFSM_RNDUP(siz) - siz;
209167802Sjkim	while (siz > 0) {
210167802Sjkim		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) {
211167802Sjkim			error = EBADRPC;
212167802Sjkim			goto out;
213167802Sjkim		}
214228110Sjkim		left = uiop->uio_iov->iov_len;
215167802Sjkim		uiocp = uiop->uio_iov->iov_base;
216167802Sjkim		if (left > siz)
217167802Sjkim			left = siz;
218228110Sjkim		uiosiz = left;
219228110Sjkim		while (left > 0) {
220228110Sjkim			while (len == 0) {
221167802Sjkim				mp = mbuf_next(mp);
222167802Sjkim				if (mp == NULL) {
223167802Sjkim					error = EBADRPC;
224167802Sjkim					goto out;
225167802Sjkim				}
226167802Sjkim				mbufcp = NFSMTOD(mp, caddr_t);
227167802Sjkim				len = mbuf_len(mp);
228167802Sjkim				KASSERT(len >= 0,
229167802Sjkim				    ("len %d, corrupted mbuf?", len));
230167802Sjkim			}
231167802Sjkim			xfer = (left > len) ? len : left;
232167802Sjkim#ifdef notdef
233167802Sjkim			/* Not Yet.. */
234167802Sjkim			if (uiop->uio_iov->iov_op != NULL)
235167802Sjkim				(*(uiop->uio_iov->iov_op))
236167802Sjkim				(mbufcp, uiocp, xfer);
237167802Sjkim			else
238167802Sjkim#endif
239167802Sjkim			if (uiop->uio_segflg == UIO_SYSSPACE)
240167802Sjkim				NFSBCOPY(mbufcp, uiocp, xfer);
241306536Sjkim			else
242167802Sjkim				copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
243167802Sjkim			left -= xfer;
244193267Sjkim			len -= xfer;
245167802Sjkim			mbufcp += xfer;
246193267Sjkim			uiocp += xfer;
247167802Sjkim			uiop->uio_offset += xfer;
248281075Sdim			uiop->uio_resid -= xfer;
249167802Sjkim		}
250306536Sjkim		if (uiop->uio_iov->iov_len <= siz) {
251167802Sjkim			uiop->uio_iovcnt--;
252167802Sjkim			uiop->uio_iov++;
253167802Sjkim		} else {
254167802Sjkim			uiop->uio_iov->iov_base = (void *)
255167802Sjkim				((char *)uiop->uio_iov->iov_base + uiosiz);
256167802Sjkim			uiop->uio_iov->iov_len -= uiosiz;
257167802Sjkim		}
258281075Sdim		siz -= uiosiz;
259281075Sdim	}
260281075Sdim	nd->nd_dpos = mbufcp;
261167802Sjkim	nd->nd_md = mp;
262167802Sjkim	if (rem > 0) {
263167802Sjkim		if (len < rem)
264234623Sjkim			error = nfsm_advance(nd, rem, len);
265167802Sjkim		else
266167802Sjkim			nd->nd_dpos += rem;
267281075Sdim	}
268234623Sjkim
269234623Sjkimout:
270234623Sjkim	NFSEXITCODE2(error, nd);
271234623Sjkim	return (error);
272234623Sjkim}
273167802Sjkim#endif	/* !APPLE */
274167802Sjkim
275281075Sdim/*
276281075Sdim * Help break down an mbuf chain by setting the first siz bytes contiguous
277281075Sdim * pointed to by returned val.
278281075Sdim * This is used by the macro NFSM_DISSECT for tough
279281075Sdim * cases.
280281075Sdim */
281281075SdimAPPLESTATIC void *
282281075Sdimnfsm_dissct(struct nfsrv_descript *nd, int siz, int how)
283281075Sdim{
284167802Sjkim	mbuf_t mp2;
285281075Sdim	int siz2, xfer;
286281075Sdim	caddr_t p;
287281075Sdim	int left;
288281075Sdim	caddr_t retp;
289281075Sdim
290281075Sdim	retp = NULL;
291281075Sdim	left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
292167802Sjkim	while (left == 0) {
293167802Sjkim		nd->nd_md = mbuf_next(nd->nd_md);
294281075Sdim		if (nd->nd_md == NULL)
295167802Sjkim			return (retp);
296167802Sjkim		left = mbuf_len(nd->nd_md);
297167802Sjkim		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
298167802Sjkim	}
299281075Sdim	if (left >= siz) {
300281075Sdim		retp = nd->nd_dpos;
301167802Sjkim		nd->nd_dpos += siz;
302167802Sjkim	} else if (mbuf_next(nd->nd_md) == NULL) {
303167802Sjkim		return (retp);
304167802Sjkim	} else if (siz > ncl_mbuf_mhlen) {
305167802Sjkim		panic("nfs S too big");
306167802Sjkim	} else {
307167802Sjkim		MGET(mp2, MT_DATA, how);
308167802Sjkim		if (mp2 == NULL)
309167802Sjkim			return (NULL);
310167802Sjkim		mbuf_setnext(mp2, mbuf_next(nd->nd_md));
311281075Sdim		mbuf_setnext(nd->nd_md, mp2);
312167802Sjkim		mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
313167802Sjkim		nd->nd_md = mp2;
314281075Sdim		retp = p = NFSMTOD(mp2, caddr_t);
315167802Sjkim		NFSBCOPY(nd->nd_dpos, p, left);	/* Copy what was left */
316167802Sjkim		siz2 = siz - left;
317167802Sjkim		p += left;
318167802Sjkim		mp2 = mbuf_next(mp2);
319281075Sdim		/* Loop around copying up the siz2 bytes */
320281075Sdim		while (siz2 > 0) {
321281075Sdim			if (mp2 == NULL)
322281075Sdim				return (NULL);
323167802Sjkim			xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
324167802Sjkim			if (xfer > 0) {
325167802Sjkim				NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
326167802Sjkim				NFSM_DATAP(mp2, xfer);
327167802Sjkim				mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
328167802Sjkim				p += xfer;
329167802Sjkim				siz2 -= xfer;
330167802Sjkim			}
331167802Sjkim			if (siz2 > 0)
332167802Sjkim				mp2 = mbuf_next(mp2);
333167802Sjkim		}
334167802Sjkim		mbuf_setlen(nd->nd_md, siz);
335167802Sjkim		nd->nd_md = mp2;
336167802Sjkim		nd->nd_dpos = NFSMTOD(mp2, caddr_t);
337167802Sjkim	}
338167802Sjkim	return (retp);
339167802Sjkim}
340167802Sjkim
341167802Sjkim/*
342167802Sjkim * Advance the position in the mbuf chain.
343167802Sjkim * If offs == 0, this is a no-op, but it is simpler to just return from
344167802Sjkim * here than check for offs > 0 for all calls to nfsm_advance.
345167802Sjkim * If left == -1, it should be calculated here.
346167802Sjkim */
347167802SjkimAPPLESTATIC int
348167802Sjkimnfsm_advance(struct nfsrv_descript *nd, int offs, int left)
349167802Sjkim{
350167802Sjkim	int error = 0;
351167802Sjkim
352167802Sjkim	if (offs == 0)
353167802Sjkim		goto out;
354167802Sjkim	/*
355167802Sjkim	 * A negative offs should be considered a serious problem.
356167802Sjkim	 */
357281075Sdim	if (offs < 0)
358281075Sdim		panic("nfsrv_advance");
359281075Sdim
360167802Sjkim	/*
361167802Sjkim	 * If left == -1, calculate it here.
362167802Sjkim	 */
363167802Sjkim	if (left == -1)
364167802Sjkim		left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
365167802Sjkim		    nd->nd_dpos;
366167802Sjkim
367167802Sjkim	/*
368167802Sjkim	 * Loop around, advancing over the mbuf data.
369167802Sjkim	 */
370167802Sjkim	while (offs > left) {
371167802Sjkim		offs -= left;
372167802Sjkim		nd->nd_md = mbuf_next(nd->nd_md);
373167802Sjkim		if (nd->nd_md == NULL) {
374167802Sjkim			error = EBADRPC;
375167802Sjkim			goto out;
376167802Sjkim		}
377167802Sjkim		left = mbuf_len(nd->nd_md);
378167802Sjkim		nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
379167802Sjkim	}
380167802Sjkim	nd->nd_dpos += offs;
381167802Sjkim
382167802Sjkimout:
383306536Sjkim	NFSEXITCODE(error);
384167802Sjkim	return (error);
385281075Sdim}
386281075Sdim
387281075Sdim/*
388281075Sdim * Copy a string into mbuf(s).
389167802Sjkim * Return the number of bytes output, including XDR overheads.
390167802Sjkim */
391167802SjkimAPPLESTATIC int
392167802Sjkimnfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
393167802Sjkim{
394167802Sjkim	mbuf_t m2;
395167802Sjkim	int xfer, left;
396167802Sjkim	mbuf_t m1;
397167802Sjkim	int rem, bytesize;
398167802Sjkim	u_int32_t *tl;
399167802Sjkim	char *cp2;
400167802Sjkim
401167802Sjkim	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
402167802Sjkim	*tl = txdr_unsigned(siz);
403167802Sjkim	rem = NFSM_RNDUP(siz) - siz;
404167802Sjkim	bytesize = NFSX_UNSIGNED + siz + rem;
405167802Sjkim	m2 = nd->nd_mb;
406167802Sjkim	cp2 = nd->nd_bpos;
407167802Sjkim	left = M_TRAILINGSPACE(m2);
408167802Sjkim
409167802Sjkim	/*
410167802Sjkim	 * Loop around copying the string to mbuf(s).
411250838Sjkim	 */
412167802Sjkim	while (siz > 0) {
413167802Sjkim		if (left == 0) {
414167802Sjkim			if (siz > ncl_mbuf_mlen)
415193267Sjkim				NFSMCLGET(m1, M_WAITOK);
416281075Sdim			else
417281075Sdim				NFSMGET(m1);
418167802Sjkim			mbuf_setlen(m1, 0);
419167802Sjkim			mbuf_setnext(m2, m1);
420167802Sjkim			m2 = m1;
421167802Sjkim			cp2 = NFSMTOD(m2, caddr_t);
422167802Sjkim			left = M_TRAILINGSPACE(m2);
423167802Sjkim		}
424167802Sjkim		if (left >= siz)
425167802Sjkim			xfer = siz;
426167802Sjkim		else
427167802Sjkim			xfer = left;
428167802Sjkim		NFSBCOPY(cp, cp2, xfer);
429167802Sjkim		cp += xfer;
430167802Sjkim		mbuf_setlen(m2, mbuf_len(m2) + xfer);
431167802Sjkim		siz -= xfer;
432167802Sjkim		left -= xfer;
433167802Sjkim		if (siz == 0 && rem) {
434167802Sjkim			if (left < rem)
435167802Sjkim				panic("nfsm_strtom");
436167802Sjkim			NFSBZERO(cp2 + xfer, rem);
437167802Sjkim			mbuf_setlen(m2, mbuf_len(m2) + rem);
438193267Sjkim		}
439167802Sjkim	}
440167802Sjkim	nd->nd_mb = m2;
441167802Sjkim	nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
442167802Sjkim	return (bytesize);
443167802Sjkim}
444167802Sjkim
445167802Sjkim/*
446167802Sjkim * Called once to initialize data structures...
447167802Sjkim */
448167802SjkimAPPLESTATIC void
449167802Sjkimnewnfs_init(void)
450167802Sjkim{
451167802Sjkim	static int nfs_inited = 0;
452167802Sjkim
453167802Sjkim	if (nfs_inited)
454167802Sjkim		return;
455306536Sjkim	nfs_inited = 1;
456306536Sjkim
457167802Sjkim	newnfs_true = txdr_unsigned(TRUE);
458167802Sjkim	newnfs_false = txdr_unsigned(FALSE);
459167802Sjkim	newnfs_xdrneg1 = txdr_unsigned(-1);
460167802Sjkim	nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
461167802Sjkim	if (nfscl_ticks < 1)
462167802Sjkim		nfscl_ticks = 1;
463167802Sjkim	NFSSETBOOTTIME(nfsboottime);
464167802Sjkim
465167802Sjkim	/*
466167802Sjkim	 * Initialize reply list and start timer
467167802Sjkim	 */
468167802Sjkim	TAILQ_INIT(&nfsd_reqq);
469167802Sjkim	NFS_TIMERINIT;
470167802Sjkim}
471167802Sjkim
472167802Sjkim/*
473167802Sjkim * Put a file handle in an mbuf list.
474306536Sjkim * If the size argument == 0, just use the default size.
475167802Sjkim * set_true == 1 if there should be an newnfs_true prepended on the file handle.
476167802Sjkim * Return the number of bytes output, including XDR overhead.
477167802Sjkim */
478281075SdimAPPLESTATIC int
479281075Sdimnfsm_fhtom(struct nfsrv_descript *nd, u_int8_t *fhp, int size, int set_true)
480281075Sdim{
481281075Sdim	u_int32_t *tl;
482281075Sdim	u_int8_t *cp;
483281075Sdim	int fullsiz, rem, bytesize = 0;
484281075Sdim
485281075Sdim	if (size == 0)
486281075Sdim		size = NFSX_MYFH;
487281075Sdim	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
488281075Sdim	case ND_NFSV2:
489281075Sdim		if (size > NFSX_V2FH)
490281075Sdim			panic("fh size > NFSX_V2FH for NFSv2");
491281075Sdim		NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
492281075Sdim		NFSBCOPY(fhp, cp, size);
493281075Sdim		if (size < NFSX_V2FH)
494281075Sdim			NFSBZERO(cp + size, NFSX_V2FH - size);
495281075Sdim		bytesize = NFSX_V2FH;
496281075Sdim		break;
497281075Sdim	case ND_NFSV3:
498281075Sdim	case ND_NFSV4:
499281075Sdim		fullsiz = NFSM_RNDUP(size);
500281075Sdim		rem = fullsiz - size;
501281075Sdim		if (set_true) {
502281075Sdim		    bytesize = 2 * NFSX_UNSIGNED + fullsiz;
503306536Sjkim		    NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
504306536Sjkim		    *tl = newnfs_true;
505281075Sdim		} else {
506281075Sdim		    bytesize = NFSX_UNSIGNED + fullsiz;
507281075Sdim		}
508281075Sdim		(void) nfsm_strtom(nd, fhp, size);
509281075Sdim		break;
510281075Sdim	};
511281075Sdim	return (bytesize);
512281075Sdim}
513281075Sdim
514281075Sdim/*
515281075Sdim * This function compares two net addresses by family and returns TRUE
516281075Sdim * if they are the same host.
517281075Sdim * If there is any doubt, return FALSE.
518281075Sdim * The AF_INET family is handled as a special case so that address mbufs
519281075Sdim * don't need to be saved to store "struct in_addr", which is only 4 bytes.
520281075Sdim */
521281075SdimAPPLESTATIC int
522306536Sjkimnfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
523306536Sjkim{
524281075Sdim	struct sockaddr_in *inetaddr;
525281075Sdim
526281075Sdim	switch (family) {
527281075Sdim	case AF_INET:
528281075Sdim		inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
529281075Sdim		if (inetaddr->sin_family == AF_INET &&
530281075Sdim		    inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
531281075Sdim			return (1);
532281075Sdim		break;
533281075Sdim#ifdef INET6
534281075Sdim	case AF_INET6:
535306536Sjkim		{
536306536Sjkim		struct sockaddr_in6 *inetaddr6;
537281075Sdim
538281075Sdim		inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
539281075Sdim		/* XXX - should test sin6_scope_id ? */
540306536Sjkim		if (inetaddr6->sin6_family == AF_INET6 &&
541306536Sjkim		    IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
542281075Sdim			  &haddr->had_inet6))
543281075Sdim			return (1);
544281075Sdim		}
545281075Sdim		break;
546281075Sdim#endif
547281075Sdim	};
548281075Sdim	return (0);
549281075Sdim}
550281075Sdim
551281075Sdim/*
552281075Sdim * Similar to the above, but takes to NFSSOCKADDR_T args.
553281075Sdim */
554281075SdimAPPLESTATIC int
555281075Sdimnfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
556281075Sdim{
557281075Sdim	struct sockaddr_in *addr1, *addr2;
558281075Sdim	struct sockaddr *inaddr;
559281075Sdim
560281075Sdim	inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
561281075Sdim	switch (inaddr->sa_family) {
562281075Sdim	case AF_INET:
563281075Sdim		addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
564281075Sdim		addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
565281075Sdim		if (addr2->sin_family == AF_INET &&
566281075Sdim		    addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
567281075Sdim			return (1);
568281075Sdim		break;
569281075Sdim#ifdef INET6
570281075Sdim	case AF_INET6:
571281075Sdim		{
572281075Sdim		struct sockaddr_in6 *inet6addr1, *inet6addr2;
573281075Sdim
574281075Sdim		inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
575306536Sjkim		inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
576306536Sjkim		/* XXX - should test sin6_scope_id ? */
577281075Sdim		if (inet6addr2->sin6_family == AF_INET6 &&
578281075Sdim		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
579281075Sdim			  &inet6addr2->sin6_addr))
580281075Sdim			return (1);
581281075Sdim		}
582281075Sdim		break;
583281075Sdim#endif
584281075Sdim	};
585281075Sdim	return (0);
586281075Sdim}
587281075Sdim
588281075Sdim
589281075Sdim/*
590281075Sdim * Trim the stuff already dissected off the mbuf list.
591281075Sdim */
592281075SdimAPPLESTATIC void
593281075Sdimnewnfs_trimleading(nd)
594281075Sdim	struct nfsrv_descript *nd;
595281075Sdim{
596281075Sdim	mbuf_t m, n;
597167802Sjkim	int offs;
598193267Sjkim
599193267Sjkim	/*
600193267Sjkim	 * First, free up leading mbufs.
601167802Sjkim	 */
602167802Sjkim	if (nd->nd_mrep != nd->nd_md) {
603167802Sjkim		m = nd->nd_mrep;
604167802Sjkim		while (mbuf_next(m) != nd->nd_md) {
605167802Sjkim			if (mbuf_next(m) == NULL)
606167802Sjkim				panic("nfsm trim leading");
607167802Sjkim			m = mbuf_next(m);
608167802Sjkim		}
609167802Sjkim		mbuf_setnext(m, NULL);
610167802Sjkim		mbuf_freem(nd->nd_mrep);
611167802Sjkim	}
61277424Smsmith	m = nd->nd_md;
61367754Smsmith
614151937Sjkim	/*
615151937Sjkim	 * Now, adjust this mbuf, based on nd_dpos.
61667754Smsmith	 */
617138287Smarks	offs = nd->nd_dpos - NFSMTOD(m, caddr_t);
61867754Smsmith	if (offs == mbuf_len(m)) {
61991116Smsmith		n = m;
62067754Smsmith		m = mbuf_next(m);
621151937Sjkim		if (m == NULL)
62267754Smsmith			panic("nfsm trim leading2");
62399679Siwasaki		mbuf_setnext(n, NULL);
62477424Smsmith		mbuf_freem(n);
625138287Smarks	} else if (offs > 0) {
626138287Smarks		mbuf_setlen(m, mbuf_len(m) - offs);
62767754Smsmith		NFSM_DATAP(m, offs);
62867754Smsmith	} else if (offs < 0)
629138287Smarks		panic("nfsm trimleading offs");
63067754Smsmith	nd->nd_mrep = m;
63167754Smsmith	nd->nd_md = m;
632167802Sjkim	nd->nd_dpos = NFSMTOD(m, caddr_t);
63377424Smsmith}
63477424Smsmith
635245582Sjkim/*
636245582Sjkim * Trim trailing data off the mbuf list being built.
637245582Sjkim */
63887031SmsmithAPPLESTATIC void
63999679Siwasakinewnfs_trimtrailing(nd, mb, bpos)
64087031Smsmith	struct nfsrv_descript *nd;
64187031Smsmith	mbuf_t mb;
64291116Smsmith	caddr_t bpos;
64367754Smsmith{
644151937Sjkim
645151937Sjkim	if (mbuf_next(mb)) {
646138287Smarks		mbuf_freem(mbuf_next(mb));
64799679Siwasaki		mbuf_setnext(mb, NULL);
64867754Smsmith	}
64967754Smsmith	mbuf_setlen(mb, bpos - NFSMTOD(mb, caddr_t));
65091116Smsmith	nd->nd_mb = mb;
65167754Smsmith	nd->nd_bpos = bpos;
652151937Sjkim}
65399146Siwasaki
65499679Siwasaki/*
65567754Smsmith * Dissect a file handle on the client.
65667754Smsmith */
65799679SiwasakiAPPLESTATIC int
65867754Smsmithnfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
659123315Snjl{
660138287Smarks	u_int32_t *tl;
661138287Smarks	struct nfsfh *nfhp;
66291116Smsmith	int error, len;
66399679Siwasaki
66467754Smsmith	*nfhpp = NULL;
66567754Smsmith	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
666123315Snjl		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
66767754Smsmith		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
668138287Smarks			len > NFSX_FHMAX) {
669138287Smarks			error = EBADRPC;
670138287Smarks			goto nfsmout;
671138287Smarks		}
672138287Smarks	} else
673138287Smarks		len = NFSX_V2FH;
674138287Smarks	MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + len,
675138287Smarks	    M_NFSFH, M_WAITOK);
676138287Smarks	error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
67767754Smsmith	if (error) {
678151937Sjkim		FREE((caddr_t)nfhp, M_NFSFH);
679138287Smarks		goto nfsmout;
680193267Sjkim	}
68167754Smsmith	nfhp->nfh_len = len;
682107325Siwasaki	*nfhpp = nfhp;
68367754Smsmithnfsmout:
684306536Sjkim	NFSEXITCODE2(error, nd);
685306536Sjkim	return (error);
686193267Sjkim}
687193267Sjkim
68867754Smsmith/*
689193267Sjkim * Break down the nfsv4 acl.
69067754Smsmith * If the aclp == NULL or won't fit in an acl, just discard the acl info.
691193267Sjkim */
69267754SmsmithAPPLESTATIC int
69367754Smsmithnfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, int *aclerrp,
694193267Sjkim    int *aclsizep, __unused NFSPROC_T *p)
69567754Smsmith{
696193267Sjkim	u_int32_t *tl;
69767754Smsmith	int i, aclsize;
69867754Smsmith	int acecnt, error = 0, aceerr = 0, acesize;
699193267Sjkim
700100966Siwasaki	*aclerrp = 0;
701193267Sjkim	if (aclp)
702100966Siwasaki		aclp->acl_cnt = 0;
703100966Siwasaki	/*
704193267Sjkim	 * Parse out the ace entries and expect them to conform to
70567754Smsmith	 * what can be supported by R/W/X bits.
706193267Sjkim	 */
707193267Sjkim	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
708193267Sjkim	aclsize = NFSX_UNSIGNED;
70967754Smsmith	acecnt = fxdr_unsigned(int, *tl);
71067754Smsmith	if (acecnt > ACL_MAX_ENTRIES)
711193267Sjkim		aceerr = NFSERR_ATTRNOTSUPP;
71267754Smsmith	if (nfsrv_useacl == 0)
713306536Sjkim		aceerr = NFSERR_ATTRNOTSUPP;
714306536Sjkim	for (i = 0; i < acecnt; i++) {
71567754Smsmith		if (aclp && !aceerr)
71667754Smsmith			error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
717193267Sjkim			    &aceerr, &acesize, p);
718193267Sjkim		else
719100966Siwasaki			error = nfsrv_skipace(nd, &acesize);
720193267Sjkim		if (error)
72167754Smsmith			goto nfsmout;
72267754Smsmith		aclsize += acesize;
723193267Sjkim	}
72467754Smsmith	if (aclp && !aceerr)
725193267Sjkim		aclp->acl_cnt = acecnt;
72667754Smsmith	if (aceerr)
72767754Smsmith		*aclerrp = aceerr;
72867754Smsmith	if (aclsizep)
72967754Smsmith		*aclsizep = aclsize;
73067754Smsmithnfsmout:
73167754Smsmith	NFSEXITCODE2(error, nd);
732193267Sjkim	return (error);
733138287Smarks}
73467754Smsmith
73567754Smsmith/*
73667754Smsmith * Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
73791116Smsmith */
73867754Smsmithstatic int
739193267Sjkimnfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
740193267Sjkim{
74167754Smsmith	u_int32_t *tl;
742193267Sjkim	int error, len = 0;
74367754Smsmith
744193267Sjkim	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
745306536Sjkim	len = fxdr_unsigned(int, *(tl + 3));
746306536Sjkim	error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
747193267Sjkimnfsmout:
74867754Smsmith	*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
74967754Smsmith	NFSEXITCODE2(error, nd);
75067754Smsmith	return (error);
75171867Smsmith}
75267754Smsmith
75387031Smsmith/*
754138287Smarks * Get attribute bits from an mbuf list.
75567754Smsmith * Returns EBADRPC for a parsing error, 0 otherwise.
75667754Smsmith * If the clearinvalid flag is set, clear the bits not supported.
75767754Smsmith */
75867754SmsmithAPPLESTATIC int
759138287Smarksnfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
760138287Smarks    int *retnotsupp)
76167754Smsmith{
76267754Smsmith	u_int32_t *tl;
763138287Smarks	int cnt, i, outcnt;
76467754Smsmith	int error = 0;
76567754Smsmith
76691116Smsmith	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
76791116Smsmith	cnt = fxdr_unsigned(int, *tl);
76883174Smsmith	if (cnt < 0) {
76967754Smsmith		error = NFSERR_BADXDR;
770138287Smarks		goto nfsmout;
77167754Smsmith	}
772306536Sjkim	if (cnt > NFSATTRBIT_MAXWORDS)
773306536Sjkim		outcnt = NFSATTRBIT_MAXWORDS;
77467754Smsmith	else
77567754Smsmith		outcnt = cnt;
77667754Smsmith	NFSZERO_ATTRBIT(attrbitp);
77767754Smsmith	if (outcnt > 0) {
77867754Smsmith		NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
77967754Smsmith		for (i = 0; i < outcnt; i++)
78087031Smsmith			attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
78191116Smsmith	}
78291116Smsmith	for (i = 0; i < (cnt - outcnt); i++) {
78367754Smsmith		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
78467754Smsmith		if (retnotsupp != NULL && *tl != 0)
78567754Smsmith			*retnotsupp = NFSERR_ATTRNOTSUPP;
78667754Smsmith	}
78767754Smsmith	if (cntp)
78891116Smsmith		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
78967754Smsmithnfsmout:
79087031Smsmith	NFSEXITCODE2(error, nd);
79167754Smsmith	return (error);
79267754Smsmith}
79367754Smsmith
79487031Smsmith/*
795281687Sjkim * Get the attributes for V4.
79691116Smsmith * If the compare flag is true, test for any attribute changes,
79767754Smsmith * otherwise return the attribute values.
79867754Smsmith * These attributes cover fields in "struct vattr", "struct statfs",
79967754Smsmith * "struct nfsfsinfo", the file handle and the lease duration.
80067754Smsmith * The value of retcmpp is set to 1 if all attributes are the same,
80167754Smsmith * and 0 otherwise.
802107325Siwasaki * Returns EBADRPC if it can't be parsed, 0 otherwise.
803151937Sjkim */
804151937SjkimAPPLESTATIC int
805151937Sjkimnfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
806107325Siwasaki    struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
807107325Siwasaki    struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
80867754Smsmith    struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
80967754Smsmith    u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
810107325Siwasaki{
81167754Smsmith	u_int32_t *tl;
81287031Smsmith	int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
81367754Smsmith	int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
81467754Smsmith	u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
815107325Siwasaki	nfsattrbit_t attrbits, retattrbits, checkattrbits;
81667754Smsmith	struct nfsfh *tnfhp;
817193267Sjkim	struct nfsreferral *refp;
818193267Sjkim	u_quad_t tquad;
819151937Sjkim	nfsquad_t tnfsquad;
820151937Sjkim	struct timespec temptime;
82191116Smsmith	uid_t uid;
82291116Smsmith	gid_t gid;
823151937Sjkim	long fid;
824151937Sjkim	u_int32_t freenum = 0, tuint;
825151937Sjkim	u_int64_t uquad = 0, thyp, thyp2;
826306536Sjkim#ifdef QUOTA
82767754Smsmith	struct dqblk dqb;
82867754Smsmith	uid_t savuid;
829107325Siwasaki#endif
83067754Smsmith
83187031Smsmith	if (compare) {
83267754Smsmith		retnotsup = 0;
83367754Smsmith		error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
83477424Smsmith	} else {
83567754Smsmith		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
836193267Sjkim	}
837151937Sjkim	if (error)
838151937Sjkim		goto nfsmout;
83991116Smsmith
84067754Smsmith	if (compare) {
84191116Smsmith		*retcmpp = retnotsup;
84267754Smsmith	} else {
843151937Sjkim		/*
84467754Smsmith		 * Just set default values to some of the important ones.
845193267Sjkim		 */
846306536Sjkim		if (nap != NULL) {
84767754Smsmith			nap->na_type = VREG;
848151937Sjkim			nap->na_mode = 0;
84967754Smsmith			nap->na_rdev = (NFSDEV_T)0;
85067754Smsmith			nap->na_mtime.tv_sec = 0;
85167754Smsmith			nap->na_mtime.tv_nsec = 0;
852306536Sjkim			nap->na_gen = 0;
85367754Smsmith			nap->na_flags = 0;
85467754Smsmith			nap->na_blocksize = NFS_FABLKSIZE;
85567754Smsmith		}
85667754Smsmith		if (sbp != NULL) {
85767754Smsmith			sbp->f_bsize = NFS_FABLKSIZE;
85887031Smsmith			sbp->f_blocks = 0;
85967754Smsmith			sbp->f_bfree = 0;
86067754Smsmith			sbp->f_bavail = 0;
86167754Smsmith			sbp->f_files = 0;
86267754Smsmith			sbp->f_ffree = 0;
863151937Sjkim		}
86491116Smsmith		if (fsp != NULL) {
865151937Sjkim			fsp->fs_rtmax = 8192;
866151937Sjkim			fsp->fs_rtpref = 8192;
86767754Smsmith			fsp->fs_maxname = NFS_MAXNAMLEN;
86867754Smsmith			fsp->fs_wtmax = 8192;
86967754Smsmith			fsp->fs_wtpref = 8192;
87067754Smsmith			fsp->fs_wtmult = NFS_FABLKSIZE;
87187031Smsmith			fsp->fs_dtpref = 8192;
87267754Smsmith			fsp->fs_maxfilesize = 0xffffffffffffffffull;
87367754Smsmith			fsp->fs_timedelta.tv_sec = 0;
87467754Smsmith			fsp->fs_timedelta.tv_nsec = 1;
87567754Smsmith			fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
87687031Smsmith				NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
87767754Smsmith		}
87867754Smsmith		if (pc != NULL) {
87967754Smsmith			pc->pc_linkmax = LINK_MAX;
88067754Smsmith			pc->pc_namemax = NAME_MAX;
88187031Smsmith			pc->pc_notrunc = 0;
88267754Smsmith			pc->pc_chownrestricted = 0;
88367754Smsmith			pc->pc_caseinsensitive = 0;
88467754Smsmith			pc->pc_casepreserving = 1;
88567754Smsmith		}
88687031Smsmith	}
88767754Smsmith
88867754Smsmith	/*
88967754Smsmith	 * Loop around getting the attributes.
89067754Smsmith	 */
89187031Smsmith	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
89267754Smsmith	attrsize = fxdr_unsigned(int, *tl);
89367754Smsmith	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
894250838Sjkim	    if (attrsum > attrsize) {
89567754Smsmith		error = NFSERR_BADXDR;
89699679Siwasaki		goto nfsmout;
89767754Smsmith	    }
898193267Sjkim	    if (NFSISSET_ATTRBIT(&attrbits, bitpos))
89967754Smsmith		switch (bitpos) {
90067754Smsmith		case NFSATTRBIT_SUPPORTEDATTRS:
90167754Smsmith			retnotsup = 0;
90299679Siwasaki			if (compare || nap == NULL)
90367754Smsmith			    error = nfsrv_getattrbits(nd, &retattrbits,
90467754Smsmith				&cnt, &retnotsup);
90567754Smsmith			else
906151937Sjkim			    error = nfsrv_getattrbits(nd, &nap->na_suppattr,
90767754Smsmith				&cnt, &retnotsup);
90877424Smsmith			if (error)
90967754Smsmith			    goto nfsmout;
910193267Sjkim			if (compare && !(*retcmpp)) {
911193267Sjkim			   NFSSETSUPP_ATTRBIT(&checkattrbits);
912193267Sjkim			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
91367754Smsmith			       || retnotsup)
914193267Sjkim				*retcmpp = NFSERR_NOTSAME;
91567754Smsmith			}
916151937Sjkim			attrsum += cnt;
91767754Smsmith			break;
91867754Smsmith		case NFSATTRBIT_TYPE:
91977424Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
92067754Smsmith			if (compare) {
921193267Sjkim				if (!(*retcmpp)) {
922193267Sjkim				    if (nap->na_type != nfsv34tov_type(*tl))
92367754Smsmith					*retcmpp = NFSERR_NOTSAME;
924167802Sjkim				}
92582367Smsmith			} else if (nap != NULL) {
92683174Smsmith				nap->na_type = nfsv34tov_type(*tl);
927193267Sjkim			}
92867754Smsmith			attrsum += NFSX_UNSIGNED;
929193267Sjkim			break;
93067754Smsmith		case NFSATTRBIT_FHEXPIRETYPE:
93167754Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
93299146Siwasaki			if (compare && !(*retcmpp)) {
933209746Sjkim				if (fxdr_unsigned(int, *tl) !=
934193267Sjkim					NFSV4FHTYPE_PERSISTENT)
93567754Smsmith					*retcmpp = NFSERR_NOTSAME;
936193267Sjkim			}
93777424Smsmith			attrsum += NFSX_UNSIGNED;
938193267Sjkim			break;
93977424Smsmith		case NFSATTRBIT_CHANGE:
94067754Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
941193267Sjkim			if (compare) {
94267754Smsmith				if (!(*retcmpp)) {
943193267Sjkim				    if (nap->na_filerev != fxdr_hyper(tl))
94467754Smsmith					*retcmpp = NFSERR_NOTSAME;
945193267Sjkim				}
946193267Sjkim			} else if (nap != NULL) {
947193267Sjkim				nap->na_filerev = fxdr_hyper(tl);
94867754Smsmith			}
94967754Smsmith			attrsum += NFSX_HYPER;
95099146Siwasaki			break;
951193267Sjkim		case NFSATTRBIT_SIZE:
95267754Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
95367754Smsmith			if (compare) {
95467754Smsmith				if (!(*retcmpp)) {
95567754Smsmith				    if (nap->na_size != fxdr_hyper(tl))
956151937Sjkim					*retcmpp = NFSERR_NOTSAME;
95767754Smsmith				}
958151937Sjkim			} else if (nap != NULL) {
95987031Smsmith				nap->na_size = fxdr_hyper(tl);
96087031Smsmith			}
96187031Smsmith			attrsum += NFSX_HYPER;
96287031Smsmith			break;
963241973Sjkim		case NFSATTRBIT_LINKSUPPORT:
96487031Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
96587031Smsmith			if (compare) {
96687031Smsmith				if (!(*retcmpp)) {
967151937Sjkim				    if (fsp->fs_properties & NFSV3_FSFLINK) {
96887031Smsmith					if (*tl == newnfs_false)
969151937Sjkim						*retcmpp = NFSERR_NOTSAME;
97087031Smsmith				    } else {
971306536Sjkim					if (*tl == newnfs_true)
972306536Sjkim						*retcmpp = NFSERR_NOTSAME;
97387031Smsmith				    }
97487031Smsmith				}
97587031Smsmith			} else if (fsp != NULL) {
97687031Smsmith				if (*tl == newnfs_true)
977151937Sjkim					fsp->fs_properties |= NFSV3_FSFLINK;
97887031Smsmith				else
979306536Sjkim					fsp->fs_properties &= ~NFSV3_FSFLINK;
980306536Sjkim			}
98187031Smsmith			attrsum += NFSX_UNSIGNED;
98287031Smsmith			break;
98387031Smsmith		case NFSATTRBIT_SYMLINKSUPPORT:
98487031Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
98587031Smsmith			if (compare) {
986151937Sjkim				if (!(*retcmpp)) {
98787031Smsmith				    if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
988167802Sjkim					if (*tl == newnfs_false)
98967754Smsmith						*retcmpp = NFSERR_NOTSAME;
990167802Sjkim				    } else {
991151937Sjkim					if (*tl == newnfs_true)
99267754Smsmith						*retcmpp = NFSERR_NOTSAME;
99367754Smsmith				    }
99467754Smsmith				}
995151937Sjkim			} else if (fsp != NULL) {
99667754Smsmith				if (*tl == newnfs_true)
99767754Smsmith					fsp->fs_properties |= NFSV3_FSFSYMLINK;
998167802Sjkim				else
99967754Smsmith					fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
100067754Smsmith			}
100167754Smsmith			attrsum += NFSX_UNSIGNED;
100267754Smsmith			break;
100391116Smsmith		case NFSATTRBIT_NAMEDATTR:
100483174Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
100583174Smsmith			if (compare && !(*retcmpp)) {
100667754Smsmith				if (*tl != newnfs_false)
100767754Smsmith					*retcmpp = NFSERR_NOTSAME;
1008245582Sjkim			}
1009245582Sjkim			attrsum += NFSX_UNSIGNED;
1010245582Sjkim			break;
101167754Smsmith		case NFSATTRBIT_FSID:
101267754Smsmith			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
101367754Smsmith			thyp = fxdr_hyper(tl);
101467754Smsmith			tl += 2;
101567754Smsmith			thyp2 = fxdr_hyper(tl);
1016193267Sjkim			if (compare) {
1017281075Sdim			    if (*retcmpp == 0) {
1018281075Sdim				if (thyp != (u_int64_t)
1019167802Sjkim				    vfs_statfs(vnode_mount(vp))->f_fsid.val[0] ||
1020167802Sjkim				    thyp2 != (u_int64_t)
1021167802Sjkim				    vfs_statfs(vnode_mount(vp))->f_fsid.val[1])
102267754Smsmith					*retcmpp = NFSERR_NOTSAME;
102367754Smsmith			    }
102467754Smsmith			} else if (nap != NULL) {
1025151937Sjkim				nap->na_filesid[0] = thyp;
102667754Smsmith				nap->na_filesid[1] = thyp2;
1027167802Sjkim			}
1028151937Sjkim			attrsum += (4 * NFSX_UNSIGNED);
1029151937Sjkim			break;
1030151937Sjkim		case NFSATTRBIT_UNIQUEHANDLES:
1031151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1032151937Sjkim			if (compare && !(*retcmpp)) {
1033151937Sjkim				if (*tl != newnfs_true)
1034151937Sjkim					*retcmpp = NFSERR_NOTSAME;
1035151937Sjkim			}
1036167802Sjkim			attrsum += NFSX_UNSIGNED;
1037151937Sjkim			break;
1038151937Sjkim		case NFSATTRBIT_LEASETIME:
1039151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1040151937Sjkim			if (compare) {
1041151937Sjkim				if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
1042151937Sjkim				    !(*retcmpp))
1043167802Sjkim					*retcmpp = NFSERR_NOTSAME;
1044167802Sjkim			} else if (leasep != NULL) {
1045193267Sjkim				*leasep = fxdr_unsigned(u_int32_t, *tl);
1046151937Sjkim			}
1047193267Sjkim			attrsum += NFSX_UNSIGNED;
1048167802Sjkim			break;
1049306536Sjkim		case NFSATTRBIT_RDATTRERROR:
1050306536Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1051151937Sjkim			if (compare) {
1052151937Sjkim				 if (!(*retcmpp))
1053193267Sjkim					*retcmpp = NFSERR_INVAL;
1054151937Sjkim			} else if (rderrp != NULL) {
1055151937Sjkim				*rderrp = fxdr_unsigned(u_int32_t, *tl);
1056151937Sjkim			}
1057151937Sjkim			attrsum += NFSX_UNSIGNED;
1058167802Sjkim			break;
1059151937Sjkim		case NFSATTRBIT_ACL:
1060151937Sjkim			if (compare) {
1061151937Sjkim			  if (!(*retcmpp)) {
1062151937Sjkim			    if (nfsrv_useacl) {
1063193267Sjkim				NFSACL_T *naclp;
1064193267Sjkim
1065306536Sjkim				naclp = acl_alloc(M_WAITOK);
1066306536Sjkim				error = nfsrv_dissectacl(nd, naclp, &aceerr,
1067193267Sjkim				    &cnt, p);
1068193267Sjkim				if (error) {
1069306536Sjkim				    acl_free(naclp);
1070306536Sjkim				    goto nfsmout;
1071193267Sjkim				}
1072193267Sjkim				if (aceerr || aclp == NULL ||
1073193267Sjkim				    nfsrv_compareacl(aclp, naclp))
1074306536Sjkim				    *retcmpp = NFSERR_NOTSAME;
1075193267Sjkim				acl_free(naclp);
1076306536Sjkim			    } else {
1077193267Sjkim				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1078193267Sjkim				    &cnt, p);
1079193267Sjkim				*retcmpp = NFSERR_ATTRNOTSUPP;
1080193267Sjkim			    }
1081193267Sjkim			  }
1082193267Sjkim			} else {
1083151937Sjkim			    if (vp != NULL && aclp != NULL)
1084151937Sjkim				error = nfsrv_dissectacl(nd, aclp, &aceerr,
1085151937Sjkim				    &cnt, p);
1086151937Sjkim			    else
1087151937Sjkim				error = nfsrv_dissectacl(nd, NULL, &aceerr,
1088151937Sjkim				    &cnt, p);
1089167802Sjkim			    if (error)
1090151937Sjkim				goto nfsmout;
1091167802Sjkim			}
1092151937Sjkim			attrsum += cnt;
1093151937Sjkim			break;
1094151937Sjkim		case NFSATTRBIT_ACLSUPPORT:
1095151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1096151937Sjkim			if (compare && !(*retcmpp)) {
1097151937Sjkim				if (nfsrv_useacl) {
1098151937Sjkim					if (fxdr_unsigned(u_int32_t, *tl) !=
1099151937Sjkim					    NFSV4ACE_SUPTYPES)
1100167802Sjkim						*retcmpp = NFSERR_NOTSAME;
1101151937Sjkim				} else {
1102151937Sjkim					*retcmpp = NFSERR_ATTRNOTSUPP;
1103151937Sjkim				}
1104151937Sjkim			}
1105151937Sjkim			attrsum += NFSX_UNSIGNED;
1106151937Sjkim			break;
1107151937Sjkim		case NFSATTRBIT_ARCHIVE:
1108151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1109151937Sjkim			if (compare && !(*retcmpp))
1110151937Sjkim				*retcmpp = NFSERR_ATTRNOTSUPP;
1111151937Sjkim			attrsum += NFSX_UNSIGNED;
1112151937Sjkim			break;
1113151937Sjkim		case NFSATTRBIT_CANSETTIME:
1114151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1115151937Sjkim			if (compare) {
1116151937Sjkim				if (!(*retcmpp)) {
1117151937Sjkim				    if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
1118151937Sjkim					if (*tl == newnfs_false)
1119151937Sjkim						*retcmpp = NFSERR_NOTSAME;
1120151937Sjkim				    } else {
1121151937Sjkim					if (*tl == newnfs_true)
1122151937Sjkim						*retcmpp = NFSERR_NOTSAME;
1123151937Sjkim				    }
1124151937Sjkim				}
1125151937Sjkim			} else if (fsp != NULL) {
1126151937Sjkim				if (*tl == newnfs_true)
1127151937Sjkim					fsp->fs_properties |= NFSV3_FSFCANSETTIME;
1128151937Sjkim				else
1129151937Sjkim					fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
1130151937Sjkim			}
1131151937Sjkim			attrsum += NFSX_UNSIGNED;
1132193267Sjkim			break;
1133151937Sjkim		case NFSATTRBIT_CASEINSENSITIVE:
1134151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1135151937Sjkim			if (compare) {
1136151937Sjkim				if (!(*retcmpp)) {
1137193267Sjkim				    if (*tl != newnfs_false)
1138151937Sjkim					*retcmpp = NFSERR_NOTSAME;
1139151937Sjkim				}
1140151937Sjkim			} else if (pc != NULL) {
1141151937Sjkim				pc->pc_caseinsensitive =
1142151937Sjkim				    fxdr_unsigned(u_int32_t, *tl);
1143234623Sjkim			}
1144151937Sjkim			attrsum += NFSX_UNSIGNED;
1145151937Sjkim			break;
1146151937Sjkim		case NFSATTRBIT_CASEPRESERVING:
1147151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1148151937Sjkim			if (compare) {
1149151937Sjkim				if (!(*retcmpp)) {
1150151937Sjkim				    if (*tl != newnfs_true)
1151151937Sjkim					*retcmpp = NFSERR_NOTSAME;
1152306536Sjkim				}
1153306536Sjkim			} else if (pc != NULL) {
1154193267Sjkim				pc->pc_casepreserving =
1155151937Sjkim				    fxdr_unsigned(u_int32_t, *tl);
1156151937Sjkim			}
1157151937Sjkim			attrsum += NFSX_UNSIGNED;
1158151937Sjkim			break;
1159151937Sjkim		case NFSATTRBIT_CHOWNRESTRICTED:
1160151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1161151937Sjkim			if (compare) {
1162151937Sjkim				if (!(*retcmpp)) {
1163151937Sjkim				    if (*tl != newnfs_true)
1164209746Sjkim					*retcmpp = NFSERR_NOTSAME;
1165193267Sjkim				}
1166151937Sjkim			} else if (pc != NULL) {
1167151937Sjkim				pc->pc_chownrestricted =
1168151937Sjkim				    fxdr_unsigned(u_int32_t, *tl);
1169306536Sjkim			}
1170306536Sjkim			attrsum += NFSX_UNSIGNED;
1171151937Sjkim			break;
1172151937Sjkim		case NFSATTRBIT_FILEHANDLE:
1173151937Sjkim			error = nfsm_getfh(nd, &tnfhp);
1174151937Sjkim			if (error)
1175151937Sjkim				goto nfsmout;
1176193267Sjkim			tfhsize = tnfhp->nfh_len;
1177193267Sjkim			if (compare) {
1178193267Sjkim				if (!(*retcmpp) &&
1179167802Sjkim				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
1180151937Sjkim				     fhp, fhsize))
1181151937Sjkim					*retcmpp = NFSERR_NOTSAME;
1182151937Sjkim				FREE((caddr_t)tnfhp, M_NFSFH);
1183151937Sjkim			} else if (nfhpp != NULL) {
1184193267Sjkim				*nfhpp = tnfhp;
1185151937Sjkim			} else {
1186151937Sjkim				FREE((caddr_t)tnfhp, M_NFSFH);
1187151937Sjkim			}
1188151937Sjkim			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
1189151937Sjkim			break;
1190151937Sjkim		case NFSATTRBIT_FILEID:
1191151937Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
119277424Smsmith			thyp = fxdr_hyper(tl);
119367754Smsmith			if (compare) {
1194167802Sjkim				if (!(*retcmpp)) {
1195151937Sjkim				    if ((u_int64_t)nap->na_fileid != thyp)
119667754Smsmith					*retcmpp = NFSERR_NOTSAME;
119767754Smsmith				}
119867754Smsmith			} else if (nap != NULL) {
1199151937Sjkim				if (*tl++)
120067754Smsmith					printf("NFSv4 fileid > 32bits\n");
120167754Smsmith				nap->na_fileid = thyp;
120277424Smsmith			}
120367754Smsmith			attrsum += NFSX_HYPER;
120467754Smsmith			break;
120567754Smsmith		case NFSATTRBIT_FILESAVAIL:
1206167802Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
120782367Smsmith			if (compare) {
120882367Smsmith				if (!(*retcmpp) &&
1209151937Sjkim				    sfp->sf_afiles != fxdr_hyper(tl))
1210151937Sjkim					*retcmpp = NFSERR_NOTSAME;
1211151937Sjkim			} else if (sfp != NULL) {
1212151937Sjkim				sfp->sf_afiles = fxdr_hyper(tl);
121367754Smsmith			}
121467754Smsmith			attrsum += NFSX_HYPER;
121567754Smsmith			break;
1216245582Sjkim		case NFSATTRBIT_FILESFREE:
1217245582Sjkim			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1218245582Sjkim			if (compare) {
121967754Smsmith				if (!(*retcmpp) &&
1220100966Siwasaki				    sfp->sf_ffiles != fxdr_hyper(tl))
122167754Smsmith					*retcmpp = NFSERR_NOTSAME;
122267754Smsmith			} else if (sfp != NULL) {
122367754Smsmith				sfp->sf_ffiles = fxdr_hyper(tl);
1224104470Siwasaki			}
1225104470Siwasaki			attrsum += NFSX_HYPER;
1226167802Sjkim			break;
1227167802Sjkim		case NFSATTRBIT_FILESTOTAL:
1228138287Smarks			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1229138287Smarks			if (compare) {
1230167802Sjkim				if (!(*retcmpp) &&
1231281075Sdim				    sfp->sf_tfiles != fxdr_hyper(tl))
1232281075Sdim					*retcmpp = NFSERR_NOTSAME;
1233104470Siwasaki			} else if (sfp != NULL) {
1234104470Siwasaki				sfp->sf_tfiles = fxdr_hyper(tl);
123599679Siwasaki			}
123667754Smsmith			attrsum += NFSX_HYPER;
1237138287Smarks			break;
1238281075Sdim		case NFSATTRBIT_FSLOCATIONS:
1239138287Smarks			error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
1240100966Siwasaki			if (error)
124167754Smsmith				goto nfsmout;
124267754Smsmith			attrsum += l;
1243281075Sdim			if (compare && !(*retcmpp)) {
1244281075Sdim				refp = nfsv4root_getreferral(vp, NULL, 0);
1245281075Sdim				if (refp != NULL) {
124667754Smsmith					if (cp == NULL || cp2 == NULL ||
1247281075Sdim					    strcmp(cp, "/") ||
1248281075Sdim					    strcmp(cp2, refp->nfr_srvlist))
1249167802Sjkim						*retcmpp = NFSERR_NOTSAME;
1250167802Sjkim				} else if (m == 0) {
125167754Smsmith					*retcmpp = NFSERR_NOTSAME;
1252281075Sdim				}
1253281075Sdim			}
1254281075Sdim			if (cp != NULL)
1255167802Sjkim				free(cp, M_NFSSTRING);
125667754Smsmith			if (cp2 != NULL)
1257167802Sjkim				free(cp2, M_NFSSTRING);
125867754Smsmith			break;
1259167802Sjkim		case NFSATTRBIT_HIDDEN:
126067754Smsmith			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1261167802Sjkim			if (compare && !(*retcmpp))
1262281075Sdim				*retcmpp = NFSERR_ATTRNOTSUPP;
1263281075Sdim			attrsum += NFSX_UNSIGNED;
1264281075Sdim			break;
1265281075Sdim		case NFSATTRBIT_HOMOGENEOUS:
1266281075Sdim			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1267281075Sdim			if (compare) {
1268306536Sjkim				if (!(*retcmpp)) {
1269306536Sjkim				    if (fsp->fs_properties &
1270281075Sdim					NFSV3_FSFHOMOGENEOUS) {
1271281075Sdim					if (*tl == newnfs_false)
1272281075Sdim						*retcmpp = NFSERR_NOTSAME;
1273281075Sdim				    } else {
1274281075Sdim					if (*tl == newnfs_true)
1275281075Sdim						*retcmpp = NFSERR_NOTSAME;
1276281075Sdim				    }
1277281075Sdim				}
1278281075Sdim			} else if (fsp != NULL) {
127967754Smsmith				if (*tl == newnfs_true)
128067754Smsmith				    fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
128167754Smsmith				else
128267754Smsmith				    fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
1283			}
1284			attrsum += NFSX_UNSIGNED;
1285			break;
1286		case NFSATTRBIT_MAXFILESIZE:
1287			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1288			tnfsquad.qval = fxdr_hyper(tl);
1289			if (compare) {
1290				if (!(*retcmpp)) {
1291					tquad = NFSRV_MAXFILESIZE;
1292					if (tquad != tnfsquad.qval)
1293						*retcmpp = NFSERR_NOTSAME;
1294				}
1295			} else if (fsp != NULL) {
1296				fsp->fs_maxfilesize = tnfsquad.qval;
1297			}
1298			attrsum += NFSX_HYPER;
1299			break;
1300		case NFSATTRBIT_MAXLINK:
1301			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1302			if (compare) {
1303				if (!(*retcmpp)) {
1304				    if (fxdr_unsigned(int, *tl) != LINK_MAX)
1305					*retcmpp = NFSERR_NOTSAME;
1306				}
1307			} else if (pc != NULL) {
1308				pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
1309			}
1310			attrsum += NFSX_UNSIGNED;
1311			break;
1312		case NFSATTRBIT_MAXNAME:
1313			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1314			if (compare) {
1315				if (!(*retcmpp)) {
1316				    if (fsp->fs_maxname !=
1317					fxdr_unsigned(u_int32_t, *tl))
1318						*retcmpp = NFSERR_NOTSAME;
1319				}
1320			} else {
1321				tuint = fxdr_unsigned(u_int32_t, *tl);
1322				/*
1323				 * Some Linux NFSv4 servers report this
1324				 * as 0 or 4billion, so I'll set it to
1325				 * NFS_MAXNAMLEN. If a server actually creates
1326				 * a name longer than NFS_MAXNAMLEN, it will
1327				 * get an error back.
1328				 */
1329				if (tuint == 0 || tuint > NFS_MAXNAMLEN)
1330					tuint = NFS_MAXNAMLEN;
1331				if (fsp != NULL)
1332					fsp->fs_maxname = tuint;
1333				if (pc != NULL)
1334					pc->pc_namemax = tuint;
1335			}
1336			attrsum += NFSX_UNSIGNED;
1337			break;
1338		case NFSATTRBIT_MAXREAD:
1339			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1340			if (compare) {
1341				if (!(*retcmpp)) {
1342				    if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
1343					*(tl + 1)) || *tl != 0)
1344					*retcmpp = NFSERR_NOTSAME;
1345				}
1346			} else if (fsp != NULL) {
1347				fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
1348				fsp->fs_rtpref = fsp->fs_rtmax;
1349				fsp->fs_dtpref = fsp->fs_rtpref;
1350			}
1351			attrsum += NFSX_HYPER;
1352			break;
1353		case NFSATTRBIT_MAXWRITE:
1354			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1355			if (compare) {
1356				if (!(*retcmpp)) {
1357				    if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
1358					*(tl + 1)) || *tl != 0)
1359					*retcmpp = NFSERR_NOTSAME;
1360				}
1361			} else if (fsp != NULL) {
1362				fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
1363				fsp->fs_wtpref = fsp->fs_wtmax;
1364			}
1365			attrsum += NFSX_HYPER;
1366			break;
1367		case NFSATTRBIT_MIMETYPE:
1368			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1369			i = fxdr_unsigned(int, *tl);
1370			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
1371			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
1372			if (error)
1373				goto nfsmout;
1374			if (compare && !(*retcmpp))
1375				*retcmpp = NFSERR_ATTRNOTSUPP;
1376			break;
1377		case NFSATTRBIT_MODE:
1378			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1379			if (compare) {
1380				if (!(*retcmpp)) {
1381				    if (nap->na_mode != nfstov_mode(*tl))
1382					*retcmpp = NFSERR_NOTSAME;
1383				}
1384			} else if (nap != NULL) {
1385				nap->na_mode = nfstov_mode(*tl);
1386			}
1387			attrsum += NFSX_UNSIGNED;
1388			break;
1389		case NFSATTRBIT_NOTRUNC:
1390			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1391			if (compare) {
1392				if (!(*retcmpp)) {
1393				    if (*tl != newnfs_true)
1394					*retcmpp = NFSERR_NOTSAME;
1395				}
1396			} else if (pc != NULL) {
1397				pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
1398			}
1399			attrsum += NFSX_UNSIGNED;
1400			break;
1401		case NFSATTRBIT_NUMLINKS:
1402			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1403			tuint = fxdr_unsigned(u_int32_t, *tl);
1404			if (compare) {
1405			    if (!(*retcmpp)) {
1406				if ((u_int32_t)nap->na_nlink != tuint)
1407					*retcmpp = NFSERR_NOTSAME;
1408			    }
1409			} else if (nap != NULL) {
1410				nap->na_nlink = tuint;
1411			}
1412			attrsum += NFSX_UNSIGNED;
1413			break;
1414		case NFSATTRBIT_OWNER:
1415			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1416			j = fxdr_unsigned(int, *tl);
1417			if (j < 0) {
1418				error = NFSERR_BADXDR;
1419				goto nfsmout;
1420			}
1421			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1422			if (j > NFSV4_SMALLSTR)
1423				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1424			else
1425				cp = namestr;
1426			error = nfsrv_mtostr(nd, cp, j);
1427			if (error) {
1428				if (j > NFSV4_SMALLSTR)
1429					free(cp, M_NFSSTRING);
1430				goto nfsmout;
1431			}
1432			if (compare) {
1433			    if (!(*retcmpp)) {
1434				if (nfsv4_strtouid(nd, cp, j, &uid, p) ||
1435				    nap->na_uid != uid)
1436				    *retcmpp = NFSERR_NOTSAME;
1437			    }
1438			} else if (nap != NULL) {
1439				if (nfsv4_strtouid(nd, cp, j, &uid, p))
1440					nap->na_uid = nfsrv_defaultuid;
1441				else
1442					nap->na_uid = uid;
1443			}
1444			if (j > NFSV4_SMALLSTR)
1445				free(cp, M_NFSSTRING);
1446			break;
1447		case NFSATTRBIT_OWNERGROUP:
1448			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1449			j = fxdr_unsigned(int, *tl);
1450			if (j < 0) {
1451				error =  NFSERR_BADXDR;
1452				goto nfsmout;
1453			}
1454			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
1455			if (j > NFSV4_SMALLSTR)
1456				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
1457			else
1458				cp = namestr;
1459			error = nfsrv_mtostr(nd, cp, j);
1460			if (error) {
1461				if (j > NFSV4_SMALLSTR)
1462					free(cp, M_NFSSTRING);
1463				goto nfsmout;
1464			}
1465			if (compare) {
1466			    if (!(*retcmpp)) {
1467				if (nfsv4_strtogid(nd, cp, j, &gid, p) ||
1468				    nap->na_gid != gid)
1469				    *retcmpp = NFSERR_NOTSAME;
1470			    }
1471			} else if (nap != NULL) {
1472				if (nfsv4_strtogid(nd, cp, j, &gid, p))
1473					nap->na_gid = nfsrv_defaultgid;
1474				else
1475					nap->na_gid = gid;
1476			}
1477			if (j > NFSV4_SMALLSTR)
1478				free(cp, M_NFSSTRING);
1479			break;
1480		case NFSATTRBIT_QUOTAHARD:
1481			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1482			if (sbp != NULL) {
1483			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1484				freenum = sbp->f_bfree;
1485			    else
1486				freenum = sbp->f_bavail;
1487#ifdef QUOTA
1488			    /*
1489			     * ufs_quotactl() insists that the uid argument
1490			     * equal p_ruid for non-root quota access, so
1491			     * we'll just make sure that's the case.
1492			     */
1493			    savuid = p->p_cred->p_ruid;
1494			    p->p_cred->p_ruid = cred->cr_uid;
1495			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1496				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1497				freenum = min(dqb.dqb_bhardlimit, freenum);
1498			    p->p_cred->p_ruid = savuid;
1499#endif	/* QUOTA */
1500			    uquad = (u_int64_t)freenum;
1501			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1502			}
1503			if (compare && !(*retcmpp)) {
1504				if (uquad != fxdr_hyper(tl))
1505					*retcmpp = NFSERR_NOTSAME;
1506			}
1507			attrsum += NFSX_HYPER;
1508			break;
1509		case NFSATTRBIT_QUOTASOFT:
1510			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1511			if (sbp != NULL) {
1512			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
1513				freenum = sbp->f_bfree;
1514			    else
1515				freenum = sbp->f_bavail;
1516#ifdef QUOTA
1517			    /*
1518			     * ufs_quotactl() insists that the uid argument
1519			     * equal p_ruid for non-root quota access, so
1520			     * we'll just make sure that's the case.
1521			     */
1522			    savuid = p->p_cred->p_ruid;
1523			    p->p_cred->p_ruid = cred->cr_uid;
1524			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1525				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1526				freenum = min(dqb.dqb_bsoftlimit, freenum);
1527			    p->p_cred->p_ruid = savuid;
1528#endif	/* QUOTA */
1529			    uquad = (u_int64_t)freenum;
1530			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1531			}
1532			if (compare && !(*retcmpp)) {
1533				if (uquad != fxdr_hyper(tl))
1534					*retcmpp = NFSERR_NOTSAME;
1535			}
1536			attrsum += NFSX_HYPER;
1537			break;
1538		case NFSATTRBIT_QUOTAUSED:
1539			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1540			if (sbp != NULL) {
1541			    freenum = 0;
1542#ifdef QUOTA
1543			    /*
1544			     * ufs_quotactl() insists that the uid argument
1545			     * equal p_ruid for non-root quota access, so
1546			     * we'll just make sure that's the case.
1547			     */
1548			    savuid = p->p_cred->p_ruid;
1549			    p->p_cred->p_ruid = cred->cr_uid;
1550			    if (!VFS_QUOTACTL(vnode_mount(vp),QCMD(Q_GETQUOTA,
1551				USRQUOTA), cred->cr_uid, (caddr_t)&dqb))
1552				freenum = dqb.dqb_curblocks;
1553			    p->p_cred->p_ruid = savuid;
1554#endif	/* QUOTA */
1555			    uquad = (u_int64_t)freenum;
1556			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
1557			}
1558			if (compare && !(*retcmpp)) {
1559				if (uquad != fxdr_hyper(tl))
1560					*retcmpp = NFSERR_NOTSAME;
1561			}
1562			attrsum += NFSX_HYPER;
1563			break;
1564		case NFSATTRBIT_RAWDEV:
1565			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
1566			j = fxdr_unsigned(int, *tl++);
1567			k = fxdr_unsigned(int, *tl);
1568			if (compare) {
1569			    if (!(*retcmpp)) {
1570				if (nap->na_rdev != NFSMAKEDEV(j, k))
1571					*retcmpp = NFSERR_NOTSAME;
1572			    }
1573			} else if (nap != NULL) {
1574				nap->na_rdev = NFSMAKEDEV(j, k);
1575			}
1576			attrsum += NFSX_V4SPECDATA;
1577			break;
1578		case NFSATTRBIT_SPACEAVAIL:
1579			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1580			if (compare) {
1581				if (!(*retcmpp) &&
1582				    sfp->sf_abytes != fxdr_hyper(tl))
1583					*retcmpp = NFSERR_NOTSAME;
1584			} else if (sfp != NULL) {
1585				sfp->sf_abytes = fxdr_hyper(tl);
1586			}
1587			attrsum += NFSX_HYPER;
1588			break;
1589		case NFSATTRBIT_SPACEFREE:
1590			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1591			if (compare) {
1592				if (!(*retcmpp) &&
1593				    sfp->sf_fbytes != fxdr_hyper(tl))
1594					*retcmpp = NFSERR_NOTSAME;
1595			} else if (sfp != NULL) {
1596				sfp->sf_fbytes = fxdr_hyper(tl);
1597			}
1598			attrsum += NFSX_HYPER;
1599			break;
1600		case NFSATTRBIT_SPACETOTAL:
1601			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1602			if (compare) {
1603				if (!(*retcmpp) &&
1604				    sfp->sf_tbytes != fxdr_hyper(tl))
1605					*retcmpp = NFSERR_NOTSAME;
1606			} else if (sfp != NULL) {
1607				sfp->sf_tbytes = fxdr_hyper(tl);
1608			}
1609			attrsum += NFSX_HYPER;
1610			break;
1611		case NFSATTRBIT_SPACEUSED:
1612			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1613			thyp = fxdr_hyper(tl);
1614			if (compare) {
1615			    if (!(*retcmpp)) {
1616				if ((u_int64_t)nap->na_bytes != thyp)
1617					*retcmpp = NFSERR_NOTSAME;
1618			    }
1619			} else if (nap != NULL) {
1620				nap->na_bytes = thyp;
1621			}
1622			attrsum += NFSX_HYPER;
1623			break;
1624		case NFSATTRBIT_SYSTEM:
1625			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1626			if (compare && !(*retcmpp))
1627				*retcmpp = NFSERR_ATTRNOTSUPP;
1628			attrsum += NFSX_UNSIGNED;
1629			break;
1630		case NFSATTRBIT_TIMEACCESS:
1631			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1632			fxdr_nfsv4time(tl, &temptime);
1633			if (compare) {
1634			    if (!(*retcmpp)) {
1635				if (!NFS_CMPTIME(temptime, nap->na_atime))
1636					*retcmpp = NFSERR_NOTSAME;
1637			    }
1638			} else if (nap != NULL) {
1639				nap->na_atime = temptime;
1640			}
1641			attrsum += NFSX_V4TIME;
1642			break;
1643		case NFSATTRBIT_TIMEACCESSSET:
1644			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1645			attrsum += NFSX_UNSIGNED;
1646			i = fxdr_unsigned(int, *tl);
1647			if (i == NFSV4SATTRTIME_TOCLIENT) {
1648				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1649				attrsum += NFSX_V4TIME;
1650			}
1651			if (compare && !(*retcmpp))
1652				*retcmpp = NFSERR_INVAL;
1653			break;
1654		case NFSATTRBIT_TIMEBACKUP:
1655			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1656			if (compare && !(*retcmpp))
1657				*retcmpp = NFSERR_ATTRNOTSUPP;
1658			attrsum += NFSX_V4TIME;
1659			break;
1660		case NFSATTRBIT_TIMECREATE:
1661			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1662			if (compare && !(*retcmpp))
1663				*retcmpp = NFSERR_ATTRNOTSUPP;
1664			attrsum += NFSX_V4TIME;
1665			break;
1666		case NFSATTRBIT_TIMEDELTA:
1667			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1668			if (fsp != NULL) {
1669			    if (compare) {
1670				if (!(*retcmpp)) {
1671				    if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
1672					fxdr_unsigned(u_int32_t, *(tl + 1)) ||
1673				        (u_int32_t)fsp->fs_timedelta.tv_nsec !=
1674					(fxdr_unsigned(u_int32_t, *(tl + 2)) %
1675					 1000000000) ||
1676					*tl != 0)
1677					    *retcmpp = NFSERR_NOTSAME;
1678				}
1679			    } else {
1680				fxdr_nfsv4time(tl, &fsp->fs_timedelta);
1681			    }
1682			}
1683			attrsum += NFSX_V4TIME;
1684			break;
1685		case NFSATTRBIT_TIMEMETADATA:
1686			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1687			fxdr_nfsv4time(tl, &temptime);
1688			if (compare) {
1689			    if (!(*retcmpp)) {
1690				if (!NFS_CMPTIME(temptime, nap->na_ctime))
1691					*retcmpp = NFSERR_NOTSAME;
1692			    }
1693			} else if (nap != NULL) {
1694				nap->na_ctime = temptime;
1695			}
1696			attrsum += NFSX_V4TIME;
1697			break;
1698		case NFSATTRBIT_TIMEMODIFY:
1699			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1700			fxdr_nfsv4time(tl, &temptime);
1701			if (compare) {
1702			    if (!(*retcmpp)) {
1703				if (!NFS_CMPTIME(temptime, nap->na_mtime))
1704					*retcmpp = NFSERR_NOTSAME;
1705			    }
1706			} else if (nap != NULL) {
1707				nap->na_mtime = temptime;
1708			}
1709			attrsum += NFSX_V4TIME;
1710			break;
1711		case NFSATTRBIT_TIMEMODIFYSET:
1712			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1713			attrsum += NFSX_UNSIGNED;
1714			i = fxdr_unsigned(int, *tl);
1715			if (i == NFSV4SATTRTIME_TOCLIENT) {
1716				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
1717				attrsum += NFSX_V4TIME;
1718			}
1719			if (compare && !(*retcmpp))
1720				*retcmpp = NFSERR_INVAL;
1721			break;
1722		case NFSATTRBIT_MOUNTEDONFILEID:
1723			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
1724			thyp = fxdr_hyper(tl);
1725			if (compare) {
1726			    if (!(*retcmpp)) {
1727				if (*tl++) {
1728					*retcmpp = NFSERR_NOTSAME;
1729				} else {
1730					if (!vp || !nfsrv_atroot(vp, &fid))
1731						fid = nap->na_fileid;
1732					if ((u_int64_t)fid != thyp)
1733						*retcmpp = NFSERR_NOTSAME;
1734				}
1735			    }
1736			} else if (nap != NULL) {
1737			    if (*tl++)
1738				printf("NFSv4 mounted on fileid > 32bits\n");
1739			    nap->na_mntonfileno = thyp;
1740			}
1741			attrsum += NFSX_HYPER;
1742			break;
1743		case NFSATTRBIT_SUPPATTREXCLCREAT:
1744			retnotsup = 0;
1745			error = nfsrv_getattrbits(nd, &retattrbits,
1746			    &cnt, &retnotsup);
1747			if (error)
1748			    goto nfsmout;
1749			if (compare && !(*retcmpp)) {
1750			   NFSSETSUPP_ATTRBIT(&checkattrbits);
1751			   NFSCLRNOTSETABLE_ATTRBIT(&checkattrbits);
1752			   NFSCLRBIT_ATTRBIT(&checkattrbits,
1753				NFSATTRBIT_TIMEACCESSSET);
1754			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
1755			       || retnotsup)
1756				*retcmpp = NFSERR_NOTSAME;
1757			}
1758			attrsum += cnt;
1759			break;
1760		default:
1761			printf("EEK! nfsv4_loadattr unknown attr=%d\n",
1762				bitpos);
1763			if (compare && !(*retcmpp))
1764				*retcmpp = NFSERR_ATTRNOTSUPP;
1765			/*
1766			 * and get out of the loop, since we can't parse
1767			 * the unknown attrbute data.
1768			 */
1769			bitpos = NFSATTRBIT_MAX;
1770			break;
1771		};
1772	}
1773
1774	/*
1775	 * some clients pad the attrlist, so we need to skip over the
1776	 * padding.
1777	 */
1778	if (attrsum > attrsize) {
1779		error = NFSERR_BADXDR;
1780	} else {
1781		attrsize = NFSM_RNDUP(attrsize);
1782		if (attrsum < attrsize)
1783			error = nfsm_advance(nd, attrsize - attrsum, -1);
1784	}
1785nfsmout:
1786	NFSEXITCODE2(error, nd);
1787	return (error);
1788}
1789
1790/*
1791 * Implement sleep locks for newnfs. The nfslock_usecnt allows for a
1792 * shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
1793 * The first argument is a pointer to an nfsv4lock structure.
1794 * The second argument is 1 iff a blocking lock is wanted.
1795 * If this argument is 0, the call waits until no thread either wants nor
1796 * holds an exclusive lock.
1797 * It returns 1 if the lock was acquired, 0 otherwise.
1798 * If several processes call this function concurrently wanting the exclusive
1799 * lock, one will get the lock and the rest will return without getting the
1800 * lock. (If the caller must have the lock, it simply calls this function in a
1801 *  loop until the function returns 1 to indicate the lock was acquired.)
1802 * Any usecnt must be decremented by calling nfsv4_relref() before
1803 * calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
1804 * be called in a loop.
1805 * The isleptp argument is set to indicate if the call slept, iff not NULL
1806 * and the mp argument indicates to check for a forced dismount, iff not
1807 * NULL.
1808 */
1809APPLESTATIC int
1810nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
1811    void *mutex, struct mount *mp)
1812{
1813
1814	if (isleptp)
1815		*isleptp = 0;
1816	/*
1817	 * If a lock is wanted, loop around until the lock is acquired by
1818	 * someone and then released. If I want the lock, try to acquire it.
1819	 * For a lock to be issued, no lock must be in force and the usecnt
1820	 * must be zero.
1821	 */
1822	if (iwantlock) {
1823	    if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1824		lp->nfslock_usecnt == 0) {
1825		lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1826		lp->nfslock_lock |= NFSV4LOCK_LOCK;
1827		return (1);
1828	    }
1829	    lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
1830	}
1831	while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
1832		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1833			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1834			return (0);
1835		}
1836		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1837		if (isleptp)
1838			*isleptp = 1;
1839		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1840		    PZERO - 1, "nfsv4lck", NULL);
1841		if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
1842		    lp->nfslock_usecnt == 0) {
1843			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
1844			lp->nfslock_lock |= NFSV4LOCK_LOCK;
1845			return (1);
1846		}
1847	}
1848	return (0);
1849}
1850
1851/*
1852 * Release the lock acquired by nfsv4_lock().
1853 * The second argument is set to 1 to indicate the nfslock_usecnt should be
1854 * incremented, as well.
1855 */
1856APPLESTATIC void
1857nfsv4_unlock(struct nfsv4lock *lp, int incref)
1858{
1859
1860	lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
1861	if (incref)
1862		lp->nfslock_usecnt++;
1863	nfsv4_wanted(lp);
1864}
1865
1866/*
1867 * Release a reference cnt.
1868 */
1869APPLESTATIC void
1870nfsv4_relref(struct nfsv4lock *lp)
1871{
1872
1873	if (lp->nfslock_usecnt <= 0)
1874		panic("nfsv4root ref cnt");
1875	lp->nfslock_usecnt--;
1876	if (lp->nfslock_usecnt == 0)
1877		nfsv4_wanted(lp);
1878}
1879
1880/*
1881 * Get a reference cnt.
1882 * This function will wait for any exclusive lock to be released, but will
1883 * not wait for threads that want the exclusive lock. If priority needs
1884 * to be given to threads that need the exclusive lock, a call to nfsv4_lock()
1885 * with the 2nd argument == 0 should be done before calling nfsv4_getref().
1886 * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and
1887 * return without getting a refcnt for that case.
1888 */
1889APPLESTATIC void
1890nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex,
1891    struct mount *mp)
1892{
1893
1894	if (isleptp)
1895		*isleptp = 0;
1896
1897	/*
1898	 * Wait for a lock held.
1899	 */
1900	while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
1901		if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1902			return;
1903		lp->nfslock_lock |= NFSV4LOCK_WANTED;
1904		if (isleptp)
1905			*isleptp = 1;
1906		(void) nfsmsleep(&lp->nfslock_lock, mutex,
1907		    PZERO - 1, "nfsv4gr", NULL);
1908	}
1909	if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
1910		return;
1911
1912	lp->nfslock_usecnt++;
1913}
1914
1915/*
1916 * Get a reference as above, but return failure instead of sleeping if
1917 * an exclusive lock is held.
1918 */
1919APPLESTATIC int
1920nfsv4_getref_nonblock(struct nfsv4lock *lp)
1921{
1922
1923	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
1924		return (0);
1925
1926	lp->nfslock_usecnt++;
1927	return (1);
1928}
1929
1930/*
1931 * Test for a lock. Return 1 if locked, 0 otherwise.
1932 */
1933APPLESTATIC int
1934nfsv4_testlock(struct nfsv4lock *lp)
1935{
1936
1937	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
1938	    lp->nfslock_usecnt == 0)
1939		return (0);
1940	return (1);
1941}
1942
1943/*
1944 * Wake up anyone sleeping, waiting for this lock.
1945 */
1946static void
1947nfsv4_wanted(struct nfsv4lock *lp)
1948{
1949
1950	if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
1951		lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
1952		wakeup((caddr_t)&lp->nfslock_lock);
1953	}
1954}
1955
1956/*
1957 * Copy a string from an mbuf list into a character array.
1958 * Return EBADRPC if there is an mbuf error,
1959 * 0 otherwise.
1960 */
1961APPLESTATIC int
1962nfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
1963{
1964	char *cp;
1965	int xfer, len;
1966	mbuf_t mp;
1967	int rem, error = 0;
1968
1969	mp = nd->nd_md;
1970	cp = nd->nd_dpos;
1971	len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - cp;
1972	rem = NFSM_RNDUP(siz) - siz;
1973	while (siz > 0) {
1974		if (len > siz)
1975			xfer = siz;
1976		else
1977			xfer = len;
1978		NFSBCOPY(cp, str, xfer);
1979		str += xfer;
1980		siz -= xfer;
1981		if (siz > 0) {
1982			mp = mbuf_next(mp);
1983			if (mp == NULL) {
1984				error = EBADRPC;
1985				goto out;
1986			}
1987			cp = NFSMTOD(mp, caddr_t);
1988			len = mbuf_len(mp);
1989		} else {
1990			cp += xfer;
1991			len -= xfer;
1992		}
1993	}
1994	*str = '\0';
1995	nd->nd_dpos = cp;
1996	nd->nd_md = mp;
1997	if (rem > 0) {
1998		if (len < rem)
1999			error = nfsm_advance(nd, rem, len);
2000		else
2001			nd->nd_dpos += rem;
2002	}
2003
2004out:
2005	NFSEXITCODE2(error, nd);
2006	return (error);
2007}
2008
2009/*
2010 * Fill in the attributes as marked by the bitmap (V4).
2011 */
2012APPLESTATIC int
2013nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
2014    NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
2015    nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
2016    int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
2017{
2018	int bitpos, retnum = 0;
2019	u_int32_t *tl;
2020	int siz, prefixnum, error;
2021	u_char *cp, namestr[NFSV4_SMALLSTR];
2022	nfsattrbit_t attrbits, retbits;
2023	nfsattrbit_t *retbitp = &retbits;
2024	u_int32_t freenum, *retnump;
2025	u_int64_t uquad;
2026	struct statfs fs;
2027	struct nfsfsinfo fsinf;
2028	struct timespec temptime;
2029	NFSACL_T *aclp, *naclp = NULL;
2030#ifdef QUOTA
2031	struct dqblk dqb;
2032	uid_t savuid;
2033#endif
2034
2035	/*
2036	 * First, set the bits that can be filled and get fsinfo.
2037	 */
2038	NFSSET_ATTRBIT(retbitp, attrbitp);
2039	/*
2040	 * If both p and cred are NULL, it is a client side setattr call.
2041	 * If both p and cred are not NULL, it is a server side reply call.
2042	 * If p is not NULL and cred is NULL, it is a client side callback
2043	 * reply call.
2044	 */
2045	if (p == NULL && cred == NULL) {
2046		NFSCLRNOTSETABLE_ATTRBIT(retbitp);
2047		aclp = saclp;
2048	} else {
2049		NFSCLRNOTFILLABLE_ATTRBIT(retbitp);
2050		naclp = acl_alloc(M_WAITOK);
2051		aclp = naclp;
2052	}
2053	nfsvno_getfs(&fsinf, isdgram);
2054#ifndef APPLE
2055	/*
2056	 * Get the VFS_STATFS(), since some attributes need them.
2057	 */
2058	if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
2059		error = VFS_STATFS(mp, &fs);
2060		if (error != 0) {
2061			if (reterr) {
2062				nd->nd_repstat = NFSERR_ACCES;
2063				return (0);
2064			}
2065			NFSCLRSTATFS_ATTRBIT(retbitp);
2066		}
2067	}
2068#endif
2069
2070	/*
2071	 * And the NFSv4 ACL...
2072	 */
2073	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
2074	    (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2075		supports_nfsv4acls == 0))) {
2076		NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
2077	}
2078	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
2079		if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2080		    supports_nfsv4acls == 0)) {
2081			NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2082		} else if (naclp != NULL) {
2083			if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
2084				error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
2085				if (error == 0)
2086					error = VOP_GETACL(vp, ACL_TYPE_NFS4,
2087					    naclp, cred, p);
2088				NFSVOPUNLOCK(vp, 0);
2089			} else
2090				error = NFSERR_PERM;
2091			if (error != 0) {
2092				if (reterr) {
2093					nd->nd_repstat = NFSERR_ACCES;
2094					return (0);
2095				}
2096				NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
2097			}
2098		}
2099	}
2100	/*
2101	 * Put out the attribute bitmap for the ones being filled in
2102	 * and get the field for the number of attributes returned.
2103	 */
2104	prefixnum = nfsrv_putattrbit(nd, retbitp);
2105	NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
2106	prefixnum += NFSX_UNSIGNED;
2107
2108	/*
2109	 * Now, loop around filling in the attributes for each bit set.
2110	 */
2111	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
2112	    if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
2113		switch (bitpos) {
2114		case NFSATTRBIT_SUPPORTEDATTRS:
2115			NFSSETSUPP_ATTRBIT(&attrbits);
2116			if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
2117			    && supports_nfsv4acls == 0)) {
2118			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
2119			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
2120			}
2121			retnum += nfsrv_putattrbit(nd, &attrbits);
2122			break;
2123		case NFSATTRBIT_TYPE:
2124			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2125			*tl = vtonfsv34_type(vap->va_type);
2126			retnum += NFSX_UNSIGNED;
2127			break;
2128		case NFSATTRBIT_FHEXPIRETYPE:
2129			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2130			*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
2131			retnum += NFSX_UNSIGNED;
2132			break;
2133		case NFSATTRBIT_CHANGE:
2134			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2135			txdr_hyper(vap->va_filerev, tl);
2136			retnum += NFSX_HYPER;
2137			break;
2138		case NFSATTRBIT_SIZE:
2139			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2140			txdr_hyper(vap->va_size, tl);
2141			retnum += NFSX_HYPER;
2142			break;
2143		case NFSATTRBIT_LINKSUPPORT:
2144			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2145			if (fsinf.fs_properties & NFSV3FSINFO_LINK)
2146				*tl = newnfs_true;
2147			else
2148				*tl = newnfs_false;
2149			retnum += NFSX_UNSIGNED;
2150			break;
2151		case NFSATTRBIT_SYMLINKSUPPORT:
2152			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2153			if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
2154				*tl = newnfs_true;
2155			else
2156				*tl = newnfs_false;
2157			retnum += NFSX_UNSIGNED;
2158			break;
2159		case NFSATTRBIT_NAMEDATTR:
2160			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2161			*tl = newnfs_false;
2162			retnum += NFSX_UNSIGNED;
2163			break;
2164		case NFSATTRBIT_FSID:
2165			NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
2166			*tl++ = 0;
2167			*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
2168			*tl++ = 0;
2169			*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
2170			retnum += NFSX_V4FSID;
2171			break;
2172		case NFSATTRBIT_UNIQUEHANDLES:
2173			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2174			*tl = newnfs_true;
2175			retnum += NFSX_UNSIGNED;
2176			break;
2177		case NFSATTRBIT_LEASETIME:
2178			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2179			*tl = txdr_unsigned(nfsrv_lease);
2180			retnum += NFSX_UNSIGNED;
2181			break;
2182		case NFSATTRBIT_RDATTRERROR:
2183			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2184			*tl = txdr_unsigned(rderror);
2185			retnum += NFSX_UNSIGNED;
2186			break;
2187		/*
2188		 * Recommended Attributes. (Only the supported ones.)
2189		 */
2190		case NFSATTRBIT_ACL:
2191			retnum += nfsrv_buildacl(nd, aclp, vnode_vtype(vp), p);
2192			break;
2193		case NFSATTRBIT_ACLSUPPORT:
2194			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2195			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
2196			retnum += NFSX_UNSIGNED;
2197			break;
2198		case NFSATTRBIT_CANSETTIME:
2199			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2200			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
2201				*tl = newnfs_true;
2202			else
2203				*tl = newnfs_false;
2204			retnum += NFSX_UNSIGNED;
2205			break;
2206		case NFSATTRBIT_CASEINSENSITIVE:
2207			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2208			*tl = newnfs_false;
2209			retnum += NFSX_UNSIGNED;
2210			break;
2211		case NFSATTRBIT_CASEPRESERVING:
2212			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2213			*tl = newnfs_true;
2214			retnum += NFSX_UNSIGNED;
2215			break;
2216		case NFSATTRBIT_CHOWNRESTRICTED:
2217			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2218			*tl = newnfs_true;
2219			retnum += NFSX_UNSIGNED;
2220			break;
2221		case NFSATTRBIT_FILEHANDLE:
2222			retnum += nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
2223			break;
2224		case NFSATTRBIT_FILEID:
2225			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2226			*tl++ = 0;
2227			*tl = txdr_unsigned(vap->va_fileid);
2228			retnum += NFSX_HYPER;
2229			break;
2230		case NFSATTRBIT_FILESAVAIL:
2231			/*
2232			 * Check quota and use min(quota, f_ffree).
2233			 */
2234			freenum = fs.f_ffree;
2235#ifdef QUOTA
2236			/*
2237			 * ufs_quotactl() insists that the uid argument
2238			 * equal p_ruid for non-root quota access, so
2239			 * we'll just make sure that's the case.
2240			 */
2241			savuid = p->p_cred->p_ruid;
2242			p->p_cred->p_ruid = cred->cr_uid;
2243			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2244			    cred->cr_uid, (caddr_t)&dqb))
2245			    freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
2246				freenum);
2247			p->p_cred->p_ruid = savuid;
2248#endif	/* QUOTA */
2249			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2250			*tl++ = 0;
2251			*tl = txdr_unsigned(freenum);
2252			retnum += NFSX_HYPER;
2253			break;
2254		case NFSATTRBIT_FILESFREE:
2255			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2256			*tl++ = 0;
2257			*tl = txdr_unsigned(fs.f_ffree);
2258			retnum += NFSX_HYPER;
2259			break;
2260		case NFSATTRBIT_FILESTOTAL:
2261			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2262			*tl++ = 0;
2263			*tl = txdr_unsigned(fs.f_files);
2264			retnum += NFSX_HYPER;
2265			break;
2266		case NFSATTRBIT_FSLOCATIONS:
2267			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
2268			*tl++ = 0;
2269			*tl = 0;
2270			retnum += 2 * NFSX_UNSIGNED;
2271			break;
2272		case NFSATTRBIT_HOMOGENEOUS:
2273			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2274			if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
2275				*tl = newnfs_true;
2276			else
2277				*tl = newnfs_false;
2278			retnum += NFSX_UNSIGNED;
2279			break;
2280		case NFSATTRBIT_MAXFILESIZE:
2281			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2282			uquad = NFSRV_MAXFILESIZE;
2283			txdr_hyper(uquad, tl);
2284			retnum += NFSX_HYPER;
2285			break;
2286		case NFSATTRBIT_MAXLINK:
2287			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2288			*tl = txdr_unsigned(LINK_MAX);
2289			retnum += NFSX_UNSIGNED;
2290			break;
2291		case NFSATTRBIT_MAXNAME:
2292			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2293			*tl = txdr_unsigned(NFS_MAXNAMLEN);
2294			retnum += NFSX_UNSIGNED;
2295			break;
2296		case NFSATTRBIT_MAXREAD:
2297			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2298			*tl++ = 0;
2299			*tl = txdr_unsigned(fsinf.fs_rtmax);
2300			retnum += NFSX_HYPER;
2301			break;
2302		case NFSATTRBIT_MAXWRITE:
2303			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2304			*tl++ = 0;
2305			*tl = txdr_unsigned(fsinf.fs_wtmax);
2306			retnum += NFSX_HYPER;
2307			break;
2308		case NFSATTRBIT_MODE:
2309			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2310			*tl = vtonfsv34_mode(vap->va_mode);
2311			retnum += NFSX_UNSIGNED;
2312			break;
2313		case NFSATTRBIT_NOTRUNC:
2314			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2315			*tl = newnfs_true;
2316			retnum += NFSX_UNSIGNED;
2317			break;
2318		case NFSATTRBIT_NUMLINKS:
2319			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2320			*tl = txdr_unsigned(vap->va_nlink);
2321			retnum += NFSX_UNSIGNED;
2322			break;
2323		case NFSATTRBIT_OWNER:
2324			cp = namestr;
2325			nfsv4_uidtostr(vap->va_uid, &cp, &siz, p);
2326			retnum += nfsm_strtom(nd, cp, siz);
2327			if (cp != namestr)
2328				free(cp, M_NFSSTRING);
2329			break;
2330		case NFSATTRBIT_OWNERGROUP:
2331			cp = namestr;
2332			nfsv4_gidtostr(vap->va_gid, &cp, &siz, p);
2333			retnum += nfsm_strtom(nd, cp, siz);
2334			if (cp != namestr)
2335				free(cp, M_NFSSTRING);
2336			break;
2337		case NFSATTRBIT_QUOTAHARD:
2338			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2339				freenum = fs.f_bfree;
2340			else
2341				freenum = fs.f_bavail;
2342#ifdef QUOTA
2343			/*
2344			 * ufs_quotactl() insists that the uid argument
2345			 * equal p_ruid for non-root quota access, so
2346			 * we'll just make sure that's the case.
2347			 */
2348			savuid = p->p_cred->p_ruid;
2349			p->p_cred->p_ruid = cred->cr_uid;
2350			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2351			    cred->cr_uid, (caddr_t)&dqb))
2352			    freenum = min(dqb.dqb_bhardlimit, freenum);
2353			p->p_cred->p_ruid = savuid;
2354#endif	/* QUOTA */
2355			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2356			uquad = (u_int64_t)freenum;
2357			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2358			txdr_hyper(uquad, tl);
2359			retnum += NFSX_HYPER;
2360			break;
2361		case NFSATTRBIT_QUOTASOFT:
2362			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
2363				freenum = fs.f_bfree;
2364			else
2365				freenum = fs.f_bavail;
2366#ifdef QUOTA
2367			/*
2368			 * ufs_quotactl() insists that the uid argument
2369			 * equal p_ruid for non-root quota access, so
2370			 * we'll just make sure that's the case.
2371			 */
2372			savuid = p->p_cred->p_ruid;
2373			p->p_cred->p_ruid = cred->cr_uid;
2374			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2375			    cred->cr_uid, (caddr_t)&dqb))
2376			    freenum = min(dqb.dqb_bsoftlimit, freenum);
2377			p->p_cred->p_ruid = savuid;
2378#endif	/* QUOTA */
2379			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2380			uquad = (u_int64_t)freenum;
2381			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2382			txdr_hyper(uquad, tl);
2383			retnum += NFSX_HYPER;
2384			break;
2385		case NFSATTRBIT_QUOTAUSED:
2386			freenum = 0;
2387#ifdef QUOTA
2388			/*
2389			 * ufs_quotactl() insists that the uid argument
2390			 * equal p_ruid for non-root quota access, so
2391			 * we'll just make sure that's the case.
2392			 */
2393			savuid = p->p_cred->p_ruid;
2394			p->p_cred->p_ruid = cred->cr_uid;
2395			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
2396			    cred->cr_uid, (caddr_t)&dqb))
2397			    freenum = dqb.dqb_curblocks;
2398			p->p_cred->p_ruid = savuid;
2399#endif	/* QUOTA */
2400			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2401			uquad = (u_int64_t)freenum;
2402			NFSQUOTABLKTOBYTE(uquad, fs.f_bsize);
2403			txdr_hyper(uquad, tl);
2404			retnum += NFSX_HYPER;
2405			break;
2406		case NFSATTRBIT_RAWDEV:
2407			NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
2408			*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
2409			*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
2410			retnum += NFSX_V4SPECDATA;
2411			break;
2412		case NFSATTRBIT_SPACEAVAIL:
2413			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2414			if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE, 0))
2415				uquad = (u_int64_t)fs.f_bfree;
2416			else
2417				uquad = (u_int64_t)fs.f_bavail;
2418			uquad *= fs.f_bsize;
2419			txdr_hyper(uquad, tl);
2420			retnum += NFSX_HYPER;
2421			break;
2422		case NFSATTRBIT_SPACEFREE:
2423			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2424			uquad = (u_int64_t)fs.f_bfree;
2425			uquad *= fs.f_bsize;
2426			txdr_hyper(uquad, tl);
2427			retnum += NFSX_HYPER;
2428			break;
2429		case NFSATTRBIT_SPACETOTAL:
2430			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2431			uquad = (u_int64_t)fs.f_blocks;
2432			uquad *= fs.f_bsize;
2433			txdr_hyper(uquad, tl);
2434			retnum += NFSX_HYPER;
2435			break;
2436		case NFSATTRBIT_SPACEUSED:
2437			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2438			txdr_hyper(vap->va_bytes, tl);
2439			retnum += NFSX_HYPER;
2440			break;
2441		case NFSATTRBIT_TIMEACCESS:
2442			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2443			txdr_nfsv4time(&vap->va_atime, tl);
2444			retnum += NFSX_V4TIME;
2445			break;
2446		case NFSATTRBIT_TIMEACCESSSET:
2447			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2448				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2449				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2450				txdr_nfsv4time(&vap->va_atime, tl);
2451				retnum += NFSX_V4SETTIME;
2452			} else {
2453				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2454				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2455				retnum += NFSX_UNSIGNED;
2456			}
2457			break;
2458		case NFSATTRBIT_TIMEDELTA:
2459			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2460			temptime.tv_sec = 0;
2461			temptime.tv_nsec = 1000000000 / hz;
2462			txdr_nfsv4time(&temptime, tl);
2463			retnum += NFSX_V4TIME;
2464			break;
2465		case NFSATTRBIT_TIMEMETADATA:
2466			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2467			txdr_nfsv4time(&vap->va_ctime, tl);
2468			retnum += NFSX_V4TIME;
2469			break;
2470		case NFSATTRBIT_TIMEMODIFY:
2471			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
2472			txdr_nfsv4time(&vap->va_mtime, tl);
2473			retnum += NFSX_V4TIME;
2474			break;
2475		case NFSATTRBIT_TIMEMODIFYSET:
2476			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
2477				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
2478				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
2479				txdr_nfsv4time(&vap->va_mtime, tl);
2480				retnum += NFSX_V4SETTIME;
2481			} else {
2482				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2483				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
2484				retnum += NFSX_UNSIGNED;
2485			}
2486			break;
2487		case NFSATTRBIT_MOUNTEDONFILEID:
2488			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2489			if (at_root != 0)
2490				uquad = mounted_on_fileno;
2491			else
2492				uquad = (u_int64_t)vap->va_fileid;
2493			txdr_hyper(uquad, tl);
2494			retnum += NFSX_HYPER;
2495			break;
2496		case NFSATTRBIT_SUPPATTREXCLCREAT:
2497			NFSSETSUPP_ATTRBIT(&attrbits);
2498			NFSCLRNOTSETABLE_ATTRBIT(&attrbits);
2499			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
2500			retnum += nfsrv_putattrbit(nd, &attrbits);
2501			break;
2502		default:
2503			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
2504		};
2505	    }
2506	}
2507	if (naclp != NULL)
2508		acl_free(naclp);
2509	*retnump = txdr_unsigned(retnum);
2510	return (retnum + prefixnum);
2511}
2512
2513/*
2514 * Put the attribute bits onto an mbuf list.
2515 * Return the number of bytes of output generated.
2516 */
2517APPLESTATIC int
2518nfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
2519{
2520	u_int32_t *tl;
2521	int cnt, i, bytesize;
2522
2523	for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
2524		if (attrbitp->bits[cnt - 1])
2525			break;
2526	bytesize = (cnt + 1) * NFSX_UNSIGNED;
2527	NFSM_BUILD(tl, u_int32_t *, bytesize);
2528	*tl++ = txdr_unsigned(cnt);
2529	for (i = 0; i < cnt; i++)
2530		*tl++ = txdr_unsigned(attrbitp->bits[i]);
2531	return (bytesize);
2532}
2533
2534/*
2535 * Convert a uid to a string.
2536 * If the lookup fails, just output the digits.
2537 * uid - the user id
2538 * cpp - points to a buffer of size NFSV4_SMALLSTR
2539 *       (malloc a larger one, as required)
2540 * retlenp - pointer to length to be returned
2541 */
2542APPLESTATIC void
2543nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2544{
2545	int i;
2546	struct nfsusrgrp *usrp;
2547	u_char *cp = *cpp;
2548	uid_t tmp;
2549	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2550	struct nfsrv_lughash *hp;
2551
2552	cnt = 0;
2553tryagain:
2554	if (nfsrv_dnsnamelen > 0) {
2555		/*
2556		 * Always map nfsrv_defaultuid to "nobody".
2557		 */
2558		if (uid == nfsrv_defaultuid) {
2559			i = nfsrv_dnsnamelen + 7;
2560			if (i > len) {
2561				if (len > NFSV4_SMALLSTR)
2562					free(cp, M_NFSSTRING);
2563				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2564				*cpp = cp;
2565				len = i;
2566				goto tryagain;
2567			}
2568			*retlenp = i;
2569			NFSBCOPY("nobody@", cp, 7);
2570			cp += 7;
2571			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2572			return;
2573		}
2574		hasampersand = 0;
2575		hp = NFSUSERHASH(uid);
2576		mtx_lock(&hp->mtx);
2577		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2578			if (usrp->lug_uid == uid) {
2579				if (usrp->lug_expiry < NFSD_MONOSEC)
2580					break;
2581				/*
2582				 * If the name doesn't already have an '@'
2583				 * in it, append @domainname to it.
2584				 */
2585				for (i = 0; i < usrp->lug_namelen; i++) {
2586					if (usrp->lug_name[i] == '@') {
2587						hasampersand = 1;
2588						break;
2589					}
2590				}
2591				if (hasampersand)
2592					i = usrp->lug_namelen;
2593				else
2594					i = usrp->lug_namelen +
2595					    nfsrv_dnsnamelen + 1;
2596				if (i > len) {
2597					mtx_unlock(&hp->mtx);
2598					if (len > NFSV4_SMALLSTR)
2599						free(cp, M_NFSSTRING);
2600					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2601					*cpp = cp;
2602					len = i;
2603					goto tryagain;
2604				}
2605				*retlenp = i;
2606				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2607				if (!hasampersand) {
2608					cp += usrp->lug_namelen;
2609					*cp++ = '@';
2610					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2611				}
2612				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2613				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2614				    lug_numhash);
2615				mtx_unlock(&hp->mtx);
2616				return;
2617			}
2618		}
2619		mtx_unlock(&hp->mtx);
2620		cnt++;
2621		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2622		    NULL, p);
2623		if (ret == 0 && cnt < 2)
2624			goto tryagain;
2625	}
2626
2627	/*
2628	 * No match, just return a string of digits.
2629	 */
2630	tmp = uid;
2631	i = 0;
2632	while (tmp || i == 0) {
2633		tmp /= 10;
2634		i++;
2635	}
2636	len = (i > len) ? len : i;
2637	*retlenp = len;
2638	cp += (len - 1);
2639	tmp = uid;
2640	for (i = 0; i < len; i++) {
2641		*cp-- = '0' + (tmp % 10);
2642		tmp /= 10;
2643	}
2644	return;
2645}
2646
2647/*
2648 * Get a credential for the uid with the server's group list.
2649 * If none is found, just return the credential passed in after
2650 * logging a warning message.
2651 */
2652struct ucred *
2653nfsrv_getgrpscred(struct ucred *oldcred)
2654{
2655	struct nfsusrgrp *usrp;
2656	struct ucred *newcred;
2657	int cnt, ret;
2658	uid_t uid;
2659	struct nfsrv_lughash *hp;
2660
2661	cnt = 0;
2662	uid = oldcred->cr_uid;
2663tryagain:
2664	if (nfsrv_dnsnamelen > 0) {
2665		hp = NFSUSERHASH(uid);
2666		mtx_lock(&hp->mtx);
2667		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2668			if (usrp->lug_uid == uid) {
2669				if (usrp->lug_expiry < NFSD_MONOSEC)
2670					break;
2671				if (usrp->lug_cred != NULL) {
2672					newcred = crhold(usrp->lug_cred);
2673					crfree(oldcred);
2674				} else
2675					newcred = oldcred;
2676				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2677				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2678				    lug_numhash);
2679				mtx_unlock(&hp->mtx);
2680				return (newcred);
2681			}
2682		}
2683		mtx_unlock(&hp->mtx);
2684		cnt++;
2685		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
2686		    NULL, curthread);
2687		if (ret == 0 && cnt < 2)
2688			goto tryagain;
2689	}
2690	return (oldcred);
2691}
2692
2693/*
2694 * Convert a string to a uid.
2695 * If no conversion is possible return NFSERR_BADOWNER, otherwise
2696 * return 0.
2697 * If this is called from a client side mount using AUTH_SYS and the
2698 * string is made up entirely of digits, just convert the string to
2699 * a number.
2700 */
2701APPLESTATIC int
2702nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
2703    NFSPROC_T *p)
2704{
2705	int i;
2706	char *cp, *endstr, *str0;
2707	struct nfsusrgrp *usrp;
2708	int cnt, ret;
2709	int error = 0;
2710	uid_t tuid;
2711	struct nfsrv_lughash *hp, *hp2;
2712
2713	if (len == 0) {
2714		error = NFSERR_BADOWNER;
2715		goto out;
2716	}
2717	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2718	str0 = str;
2719	tuid = (uid_t)strtoul(str0, &endstr, 10);
2720	if ((endstr - str0) == len) {
2721		/* A numeric string. */
2722		if ((nd->nd_flag & ND_KERBV) == 0 &&
2723		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2724		      nfsd_enable_stringtouid != 0))
2725			*uidp = tuid;
2726		else
2727			error = NFSERR_BADOWNER;
2728		goto out;
2729	}
2730	/*
2731	 * Look for an '@'.
2732	 */
2733	cp = strchr(str0, '@');
2734	if (cp != NULL)
2735		i = (int)(cp++ - str0);
2736	else
2737		i = len;
2738
2739	cnt = 0;
2740tryagain:
2741	if (nfsrv_dnsnamelen > 0) {
2742		/*
2743		 * If an '@' is found and the domain name matches, search for
2744		 * the name with dns stripped off.
2745		 * Mixed case alpahbetics will match for the domain name, but
2746		 * all upper case will not.
2747		 */
2748		if (cnt == 0 && i < len && i > 0 &&
2749		    (len - 1 - i) == nfsrv_dnsnamelen &&
2750		    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2751			len -= (nfsrv_dnsnamelen + 1);
2752			*(cp - 1) = '\0';
2753		}
2754
2755		/*
2756		 * Check for the special case of "nobody".
2757		 */
2758		if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
2759			*uidp = nfsrv_defaultuid;
2760			error = 0;
2761			goto out;
2762		}
2763
2764		hp = NFSUSERNAMEHASH(str, len);
2765		mtx_lock(&hp->mtx);
2766		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
2767			if (usrp->lug_namelen == len &&
2768			    !NFSBCMP(usrp->lug_name, str, len)) {
2769				if (usrp->lug_expiry < NFSD_MONOSEC)
2770					break;
2771				hp2 = NFSUSERHASH(usrp->lug_uid);
2772				mtx_lock(&hp2->mtx);
2773				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
2774				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
2775				    lug_numhash);
2776				*uidp = usrp->lug_uid;
2777				mtx_unlock(&hp2->mtx);
2778				mtx_unlock(&hp->mtx);
2779				error = 0;
2780				goto out;
2781			}
2782		}
2783		mtx_unlock(&hp->mtx);
2784		cnt++;
2785		ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
2786		    str, p);
2787		if (ret == 0 && cnt < 2)
2788			goto tryagain;
2789	}
2790	error = NFSERR_BADOWNER;
2791
2792out:
2793	NFSEXITCODE(error);
2794	return (error);
2795}
2796
2797/*
2798 * Convert a gid to a string.
2799 * gid - the group id
2800 * cpp - points to a buffer of size NFSV4_SMALLSTR
2801 *       (malloc a larger one, as required)
2802 * retlenp - pointer to length to be returned
2803 */
2804APPLESTATIC void
2805nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
2806{
2807	int i;
2808	struct nfsusrgrp *usrp;
2809	u_char *cp = *cpp;
2810	gid_t tmp;
2811	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
2812	struct nfsrv_lughash *hp;
2813
2814	cnt = 0;
2815tryagain:
2816	if (nfsrv_dnsnamelen > 0) {
2817		/*
2818		 * Always map nfsrv_defaultgid to "nogroup".
2819		 */
2820		if (gid == nfsrv_defaultgid) {
2821			i = nfsrv_dnsnamelen + 8;
2822			if (i > len) {
2823				if (len > NFSV4_SMALLSTR)
2824					free(cp, M_NFSSTRING);
2825				cp = malloc(i, M_NFSSTRING, M_WAITOK);
2826				*cpp = cp;
2827				len = i;
2828				goto tryagain;
2829			}
2830			*retlenp = i;
2831			NFSBCOPY("nogroup@", cp, 8);
2832			cp += 8;
2833			NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2834			return;
2835		}
2836		hasampersand = 0;
2837		hp = NFSGROUPHASH(gid);
2838		mtx_lock(&hp->mtx);
2839		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
2840			if (usrp->lug_gid == gid) {
2841				if (usrp->lug_expiry < NFSD_MONOSEC)
2842					break;
2843				/*
2844				 * If the name doesn't already have an '@'
2845				 * in it, append @domainname to it.
2846				 */
2847				for (i = 0; i < usrp->lug_namelen; i++) {
2848					if (usrp->lug_name[i] == '@') {
2849						hasampersand = 1;
2850						break;
2851					}
2852				}
2853				if (hasampersand)
2854					i = usrp->lug_namelen;
2855				else
2856					i = usrp->lug_namelen +
2857					    nfsrv_dnsnamelen + 1;
2858				if (i > len) {
2859					mtx_unlock(&hp->mtx);
2860					if (len > NFSV4_SMALLSTR)
2861						free(cp, M_NFSSTRING);
2862					cp = malloc(i, M_NFSSTRING, M_WAITOK);
2863					*cpp = cp;
2864					len = i;
2865					goto tryagain;
2866				}
2867				*retlenp = i;
2868				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
2869				if (!hasampersand) {
2870					cp += usrp->lug_namelen;
2871					*cp++ = '@';
2872					NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
2873				}
2874				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
2875				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
2876				    lug_numhash);
2877				mtx_unlock(&hp->mtx);
2878				return;
2879			}
2880		}
2881		mtx_unlock(&hp->mtx);
2882		cnt++;
2883		ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
2884		    NULL, p);
2885		if (ret == 0 && cnt < 2)
2886			goto tryagain;
2887	}
2888
2889	/*
2890	 * No match, just return a string of digits.
2891	 */
2892	tmp = gid;
2893	i = 0;
2894	while (tmp || i == 0) {
2895		tmp /= 10;
2896		i++;
2897	}
2898	len = (i > len) ? len : i;
2899	*retlenp = len;
2900	cp += (len - 1);
2901	tmp = gid;
2902	for (i = 0; i < len; i++) {
2903		*cp-- = '0' + (tmp % 10);
2904		tmp /= 10;
2905	}
2906	return;
2907}
2908
2909/*
2910 * Convert a string to a gid.
2911 * If no conversion is possible return NFSERR_BADOWNER, otherwise
2912 * return 0.
2913 * If this is called from a client side mount using AUTH_SYS and the
2914 * string is made up entirely of digits, just convert the string to
2915 * a number.
2916 */
2917APPLESTATIC int
2918nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
2919    NFSPROC_T *p)
2920{
2921	int i;
2922	char *cp, *endstr, *str0;
2923	struct nfsusrgrp *usrp;
2924	int cnt, ret;
2925	int error = 0;
2926	gid_t tgid;
2927	struct nfsrv_lughash *hp, *hp2;
2928
2929	if (len == 0) {
2930		error =  NFSERR_BADOWNER;
2931		goto out;
2932	}
2933	/* If a string of digits and an AUTH_SYS mount, just convert it. */
2934	str0 = str;
2935	tgid = (gid_t)strtoul(str0, &endstr, 10);
2936	if ((endstr - str0) == len) {
2937		/* A numeric string. */
2938		if ((nd->nd_flag & ND_KERBV) == 0 &&
2939		    ((nd->nd_flag & ND_NFSCL) != 0 ||
2940		      nfsd_enable_stringtouid != 0))
2941			*gidp = tgid;
2942		else
2943			error = NFSERR_BADOWNER;
2944		goto out;
2945	}
2946	/*
2947	 * Look for an '@'.
2948	 */
2949	cp = strchr(str0, '@');
2950	if (cp != NULL)
2951		i = (int)(cp++ - str0);
2952	else
2953		i = len;
2954
2955	cnt = 0;
2956tryagain:
2957	if (nfsrv_dnsnamelen > 0) {
2958		/*
2959		 * If an '@' is found and the dns name matches, search for the
2960		 * name with the dns stripped off.
2961		 */
2962		if (cnt == 0 && i < len && i > 0 &&
2963		    (len - 1 - i) == nfsrv_dnsnamelen &&
2964		    !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
2965			len -= (nfsrv_dnsnamelen + 1);
2966			*(cp - 1) = '\0';
2967		}
2968
2969		/*
2970		 * Check for the special case of "nogroup".
2971		 */
2972		if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
2973			*gidp = nfsrv_defaultgid;
2974			error = 0;
2975			goto out;
2976		}
2977
2978		hp = NFSGROUPNAMEHASH(str, len);
2979		mtx_lock(&hp->mtx);
2980		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
2981			if (usrp->lug_namelen == len &&
2982			    !NFSBCMP(usrp->lug_name, str, len)) {
2983				if (usrp->lug_expiry < NFSD_MONOSEC)
2984					break;
2985				hp2 = NFSGROUPHASH(usrp->lug_gid);
2986				mtx_lock(&hp2->mtx);
2987				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
2988				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
2989				    lug_numhash);
2990				*gidp = usrp->lug_gid;
2991				mtx_unlock(&hp2->mtx);
2992				mtx_unlock(&hp->mtx);
2993				error = 0;
2994				goto out;
2995			}
2996		}
2997		mtx_unlock(&hp->mtx);
2998		cnt++;
2999		ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
3000		    str, p);
3001		if (ret == 0 && cnt < 2)
3002			goto tryagain;
3003	}
3004	error = NFSERR_BADOWNER;
3005
3006out:
3007	NFSEXITCODE(error);
3008	return (error);
3009}
3010
3011/*
3012 * Cmp len chars, allowing mixed case in the first argument to match lower
3013 * case in the second, but not if the first argument is all upper case.
3014 * Return 0 for a match, 1 otherwise.
3015 */
3016static int
3017nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len)
3018{
3019	int i;
3020	u_char tmp;
3021	int fndlower = 0;
3022
3023	for (i = 0; i < len; i++) {
3024		if (*cp >= 'A' && *cp <= 'Z') {
3025			tmp = *cp++ + ('a' - 'A');
3026		} else {
3027			tmp = *cp++;
3028			if (tmp >= 'a' && tmp <= 'z')
3029				fndlower = 1;
3030		}
3031		if (tmp != *cp2++)
3032			return (1);
3033	}
3034	if (fndlower)
3035		return (0);
3036	else
3037		return (1);
3038}
3039
3040/*
3041 * Set the port for the nfsuserd.
3042 */
3043APPLESTATIC int
3044nfsrv_nfsuserdport(u_short port, NFSPROC_T *p)
3045{
3046	struct nfssockreq *rp;
3047	struct sockaddr_in *ad;
3048	int error;
3049
3050	NFSLOCKNAMEID();
3051	if (nfsrv_nfsuserd) {
3052		NFSUNLOCKNAMEID();
3053		error = EPERM;
3054		goto out;
3055	}
3056	nfsrv_nfsuserd = 1;
3057	NFSUNLOCKNAMEID();
3058	/*
3059	 * Set up the socket record and connect.
3060	 */
3061	rp = &nfsrv_nfsuserdsock;
3062	rp->nr_client = NULL;
3063	rp->nr_sotype = SOCK_DGRAM;
3064	rp->nr_soproto = IPPROTO_UDP;
3065	rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
3066	rp->nr_cred = NULL;
3067	NFSSOCKADDRALLOC(rp->nr_nam);
3068	NFSSOCKADDRSIZE(rp->nr_nam, sizeof (struct sockaddr_in));
3069	ad = NFSSOCKADDR(rp->nr_nam, struct sockaddr_in *);
3070	ad->sin_family = AF_INET;
3071	ad->sin_addr.s_addr = htonl((u_int32_t)0x7f000001);	/* 127.0.0.1 */
3072	ad->sin_port = port;
3073	rp->nr_prog = RPCPROG_NFSUSERD;
3074	rp->nr_vers = RPCNFSUSERD_VERS;
3075	error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0);
3076	if (error) {
3077		NFSSOCKADDRFREE(rp->nr_nam);
3078		nfsrv_nfsuserd = 0;
3079	}
3080out:
3081	NFSEXITCODE(error);
3082	return (error);
3083}
3084
3085/*
3086 * Delete the nfsuserd port.
3087 */
3088APPLESTATIC void
3089nfsrv_nfsuserddelport(void)
3090{
3091
3092	NFSLOCKNAMEID();
3093	if (nfsrv_nfsuserd == 0) {
3094		NFSUNLOCKNAMEID();
3095		return;
3096	}
3097	nfsrv_nfsuserd = 0;
3098	NFSUNLOCKNAMEID();
3099	newnfs_disconnect(&nfsrv_nfsuserdsock);
3100	NFSSOCKADDRFREE(nfsrv_nfsuserdsock.nr_nam);
3101}
3102
3103/*
3104 * Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
3105 * name<-->id cache.
3106 * Returns 0 upon success, non-zero otherwise.
3107 */
3108static int
3109nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name, NFSPROC_T *p)
3110{
3111	u_int32_t *tl;
3112	struct nfsrv_descript *nd;
3113	int len;
3114	struct nfsrv_descript nfsd;
3115	struct ucred *cred;
3116	int error;
3117
3118	NFSLOCKNAMEID();
3119	if (nfsrv_nfsuserd == 0) {
3120		NFSUNLOCKNAMEID();
3121		error = EPERM;
3122		goto out;
3123	}
3124	NFSUNLOCKNAMEID();
3125	nd = &nfsd;
3126	cred = newnfs_getcred();
3127	nd->nd_flag = ND_GSSINITREPLY;
3128	nfsrvd_rephead(nd);
3129
3130	nd->nd_procnum = procnum;
3131	if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
3132		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3133		if (procnum == RPCNFSUSERD_GETUID)
3134			*tl = txdr_unsigned(uid);
3135		else
3136			*tl = txdr_unsigned(gid);
3137	} else {
3138		len = strlen(name);
3139		(void) nfsm_strtom(nd, name, len);
3140	}
3141	error = newnfs_request(nd, NULL, NULL, &nfsrv_nfsuserdsock, NULL, NULL,
3142		cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0, NULL, NULL);
3143	NFSFREECRED(cred);
3144	if (!error) {
3145		mbuf_freem(nd->nd_mrep);
3146		error = nd->nd_repstat;
3147	}
3148out:
3149	NFSEXITCODE(error);
3150	return (error);
3151}
3152
3153/*
3154 * This function is called from the nfssvc(2) system call, to update the
3155 * kernel user/group name list(s) for the V4 owner and ownergroup attributes.
3156 */
3157APPLESTATIC int
3158nfssvc_idname(struct nfsd_idargs *nidp)
3159{
3160	struct nfsusrgrp *nusrp, *usrp, *newusrp;
3161	struct nfsrv_lughash *hp_name, *hp_idnum, *thp;
3162	int i, group_locked, groupname_locked, user_locked, username_locked;
3163	int error = 0;
3164	u_char *cp;
3165	gid_t *grps;
3166	struct ucred *cr;
3167	static int onethread = 0;
3168	static time_t lasttime = 0;
3169
3170	if (nidp->nid_namelen <= 0 || nidp->nid_namelen > MAXHOSTNAMELEN) {
3171		error = EINVAL;
3172		goto out;
3173	}
3174	if (nidp->nid_flag & NFSID_INITIALIZE) {
3175		cp = malloc(nidp->nid_namelen + 1, M_NFSSTRING, M_WAITOK);
3176		error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
3177		    nidp->nid_namelen);
3178		if (error != 0) {
3179			free(cp, M_NFSSTRING);
3180			goto out;
3181		}
3182		if (atomic_cmpset_acq_int(&nfsrv_dnsnamelen, 0, 0) == 0) {
3183			/*
3184			 * Free up all the old stuff and reinitialize hash
3185			 * lists.  All mutexes for both lists must be locked,
3186			 * with the user/group name ones before the uid/gid
3187			 * ones, to avoid a LOR.
3188			 */
3189			for (i = 0; i < nfsrv_lughashsize; i++)
3190				mtx_lock(&nfsusernamehash[i].mtx);
3191			for (i = 0; i < nfsrv_lughashsize; i++)
3192				mtx_lock(&nfsuserhash[i].mtx);
3193			for (i = 0; i < nfsrv_lughashsize; i++)
3194				TAILQ_FOREACH_SAFE(usrp,
3195				    &nfsuserhash[i].lughead, lug_numhash, nusrp)
3196					nfsrv_removeuser(usrp, 1);
3197			for (i = 0; i < nfsrv_lughashsize; i++)
3198				mtx_unlock(&nfsuserhash[i].mtx);
3199			for (i = 0; i < nfsrv_lughashsize; i++)
3200				mtx_unlock(&nfsusernamehash[i].mtx);
3201			for (i = 0; i < nfsrv_lughashsize; i++)
3202				mtx_lock(&nfsgroupnamehash[i].mtx);
3203			for (i = 0; i < nfsrv_lughashsize; i++)
3204				mtx_lock(&nfsgrouphash[i].mtx);
3205			for (i = 0; i < nfsrv_lughashsize; i++)
3206				TAILQ_FOREACH_SAFE(usrp,
3207				    &nfsgrouphash[i].lughead, lug_numhash,
3208				    nusrp)
3209					nfsrv_removeuser(usrp, 0);
3210			for (i = 0; i < nfsrv_lughashsize; i++)
3211				mtx_unlock(&nfsgrouphash[i].mtx);
3212			for (i = 0; i < nfsrv_lughashsize; i++)
3213				mtx_unlock(&nfsgroupnamehash[i].mtx);
3214			free(nfsrv_dnsname, M_NFSSTRING);
3215			nfsrv_dnsname = NULL;
3216		}
3217		if (nfsuserhash == NULL) {
3218			/* Allocate the hash tables. */
3219			nfsuserhash = malloc(sizeof(struct nfsrv_lughash) *
3220			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3221			    M_ZERO);
3222			for (i = 0; i < nfsrv_lughashsize; i++)
3223				mtx_init(&nfsuserhash[i].mtx, "nfsuidhash",
3224				    NULL, MTX_DEF | MTX_DUPOK);
3225			nfsusernamehash = malloc(sizeof(struct nfsrv_lughash) *
3226			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3227			    M_ZERO);
3228			for (i = 0; i < nfsrv_lughashsize; i++)
3229				mtx_init(&nfsusernamehash[i].mtx,
3230				    "nfsusrhash", NULL, MTX_DEF |
3231				    MTX_DUPOK);
3232			nfsgrouphash = malloc(sizeof(struct nfsrv_lughash) *
3233			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3234			    M_ZERO);
3235			for (i = 0; i < nfsrv_lughashsize; i++)
3236				mtx_init(&nfsgrouphash[i].mtx, "nfsgidhash",
3237				    NULL, MTX_DEF | MTX_DUPOK);
3238			nfsgroupnamehash = malloc(sizeof(struct nfsrv_lughash) *
3239			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3240			    M_ZERO);
3241			for (i = 0; i < nfsrv_lughashsize; i++)
3242			    mtx_init(&nfsgroupnamehash[i].mtx,
3243			    "nfsgrphash", NULL, MTX_DEF | MTX_DUPOK);
3244		}
3245		/* (Re)initialize the list heads. */
3246		for (i = 0; i < nfsrv_lughashsize; i++)
3247			TAILQ_INIT(&nfsuserhash[i].lughead);
3248		for (i = 0; i < nfsrv_lughashsize; i++)
3249			TAILQ_INIT(&nfsusernamehash[i].lughead);
3250		for (i = 0; i < nfsrv_lughashsize; i++)
3251			TAILQ_INIT(&nfsgrouphash[i].lughead);
3252		for (i = 0; i < nfsrv_lughashsize; i++)
3253			TAILQ_INIT(&nfsgroupnamehash[i].lughead);
3254
3255		/*
3256		 * Put name in "DNS" string.
3257		 */
3258		nfsrv_dnsname = cp;
3259		nfsrv_defaultuid = nidp->nid_uid;
3260		nfsrv_defaultgid = nidp->nid_gid;
3261		nfsrv_usercnt = 0;
3262		nfsrv_usermax = nidp->nid_usermax;
3263		atomic_store_rel_int(&nfsrv_dnsnamelen, nidp->nid_namelen);
3264		goto out;
3265	}
3266
3267	/*
3268	 * malloc the new one now, so any potential sleep occurs before
3269	 * manipulation of the lists.
3270	 */
3271	newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
3272	    M_NFSUSERGROUP, M_WAITOK | M_ZERO);
3273	error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
3274	    nidp->nid_namelen);
3275	if (error == 0 && nidp->nid_ngroup > 0 &&
3276	    (nidp->nid_flag & NFSID_ADDUID) != 0) {
3277		grps = malloc(sizeof(gid_t) * nidp->nid_ngroup, M_TEMP,
3278		    M_WAITOK);
3279		error = copyin(CAST_USER_ADDR_T(nidp->nid_grps), grps,
3280		    sizeof(gid_t) * nidp->nid_ngroup);
3281		if (error == 0) {
3282			/*
3283			 * Create a credential just like svc_getcred(),
3284			 * but using the group list provided.
3285			 */
3286			cr = crget();
3287			cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid;
3288			crsetgroups(cr, nidp->nid_ngroup, grps);
3289			cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
3290			cr->cr_prison = &prison0;
3291			prison_hold(cr->cr_prison);
3292#ifdef MAC
3293			mac_cred_associate_nfsd(cr);
3294#endif
3295			newusrp->lug_cred = cr;
3296		}
3297		free(grps, M_TEMP);
3298	}
3299	if (error) {
3300		free(newusrp, M_NFSUSERGROUP);
3301		goto out;
3302	}
3303	newusrp->lug_namelen = nidp->nid_namelen;
3304
3305	/*
3306	 * The lock order is username[0]->[nfsrv_lughashsize - 1] followed
3307	 * by uid[0]->[nfsrv_lughashsize - 1], with the same for group.
3308	 * The flags user_locked, username_locked, group_locked and
3309	 * groupname_locked are set to indicate all of those hash lists are
3310	 * locked. hp_name != NULL  and hp_idnum != NULL indicates that
3311	 * the respective one mutex is locked.
3312	 */
3313	user_locked = username_locked = group_locked = groupname_locked = 0;
3314	hp_name = hp_idnum = NULL;
3315
3316	/*
3317	 * Delete old entries, as required.
3318	 */
3319	if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
3320		/* Must lock all username hash lists first, to avoid a LOR. */
3321		for (i = 0; i < nfsrv_lughashsize; i++)
3322			mtx_lock(&nfsusernamehash[i].mtx);
3323		username_locked = 1;
3324		hp_idnum = NFSUSERHASH(nidp->nid_uid);
3325		mtx_lock(&hp_idnum->mtx);
3326		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3327		    nusrp) {
3328			if (usrp->lug_uid == nidp->nid_uid)
3329				nfsrv_removeuser(usrp, 1);
3330		}
3331	} else if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
3332		hp_name = NFSUSERNAMEHASH(newusrp->lug_name,
3333		    newusrp->lug_namelen);
3334		mtx_lock(&hp_name->mtx);
3335		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3336		    nusrp) {
3337			if (usrp->lug_namelen == newusrp->lug_namelen &&
3338			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3339			    usrp->lug_namelen)) {
3340				thp = NFSUSERHASH(usrp->lug_uid);
3341				mtx_lock(&thp->mtx);
3342				nfsrv_removeuser(usrp, 1);
3343				mtx_unlock(&thp->mtx);
3344			}
3345		}
3346		hp_idnum = NFSUSERHASH(nidp->nid_uid);
3347		mtx_lock(&hp_idnum->mtx);
3348	} else if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
3349		/* Must lock all groupname hash lists first, to avoid a LOR. */
3350		for (i = 0; i < nfsrv_lughashsize; i++)
3351			mtx_lock(&nfsgroupnamehash[i].mtx);
3352		groupname_locked = 1;
3353		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3354		mtx_lock(&hp_idnum->mtx);
3355		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3356		    nusrp) {
3357			if (usrp->lug_gid == nidp->nid_gid)
3358				nfsrv_removeuser(usrp, 0);
3359		}
3360	} else if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
3361		hp_name = NFSGROUPNAMEHASH(newusrp->lug_name,
3362		    newusrp->lug_namelen);
3363		mtx_lock(&hp_name->mtx);
3364		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3365		    nusrp) {
3366			if (usrp->lug_namelen == newusrp->lug_namelen &&
3367			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3368			    usrp->lug_namelen)) {
3369				thp = NFSGROUPHASH(usrp->lug_gid);
3370				mtx_lock(&thp->mtx);
3371				nfsrv_removeuser(usrp, 0);
3372				mtx_unlock(&thp->mtx);
3373			}
3374		}
3375		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3376		mtx_lock(&hp_idnum->mtx);
3377	}
3378
3379	/*
3380	 * Now, we can add the new one.
3381	 */
3382	if (nidp->nid_usertimeout)
3383		newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
3384	else
3385		newusrp->lug_expiry = NFSD_MONOSEC + 5;
3386	if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
3387		newusrp->lug_uid = nidp->nid_uid;
3388		thp = NFSUSERHASH(newusrp->lug_uid);
3389		mtx_assert(&thp->mtx, MA_OWNED);
3390		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3391		thp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3392		mtx_assert(&thp->mtx, MA_OWNED);
3393		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3394		atomic_add_int(&nfsrv_usercnt, 1);
3395	} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
3396		newusrp->lug_gid = nidp->nid_gid;
3397		thp = NFSGROUPHASH(newusrp->lug_gid);
3398		mtx_assert(&thp->mtx, MA_OWNED);
3399		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3400		thp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3401		mtx_assert(&thp->mtx, MA_OWNED);
3402		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3403		atomic_add_int(&nfsrv_usercnt, 1);
3404	} else {
3405		if (newusrp->lug_cred != NULL)
3406			crfree(newusrp->lug_cred);
3407		free(newusrp, M_NFSUSERGROUP);
3408	}
3409
3410	/*
3411	 * Once per second, allow one thread to trim the cache.
3412	 */
3413	if (lasttime < NFSD_MONOSEC &&
3414	    atomic_cmpset_acq_int(&onethread, 0, 1) != 0) {
3415		/*
3416		 * First, unlock the single mutexes, so that all entries
3417		 * can be locked and any LOR is avoided.
3418		 */
3419		if (hp_name != NULL) {
3420			mtx_unlock(&hp_name->mtx);
3421			hp_name = NULL;
3422		}
3423		if (hp_idnum != NULL) {
3424			mtx_unlock(&hp_idnum->mtx);
3425			hp_idnum = NULL;
3426		}
3427
3428		if ((nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID |
3429		    NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) != 0) {
3430			if (username_locked == 0) {
3431				for (i = 0; i < nfsrv_lughashsize; i++)
3432					mtx_lock(&nfsusernamehash[i].mtx);
3433				username_locked = 1;
3434			}
3435			KASSERT(user_locked == 0,
3436			    ("nfssvc_idname: user_locked"));
3437			for (i = 0; i < nfsrv_lughashsize; i++)
3438				mtx_lock(&nfsuserhash[i].mtx);
3439			user_locked = 1;
3440			for (i = 0; i < nfsrv_lughashsize; i++) {
3441				TAILQ_FOREACH_SAFE(usrp,
3442				    &nfsuserhash[i].lughead, lug_numhash,
3443				    nusrp)
3444					if (usrp->lug_expiry < NFSD_MONOSEC)
3445						nfsrv_removeuser(usrp, 1);
3446			}
3447			for (i = 0; i < nfsrv_lughashsize; i++) {
3448				/*
3449				 * Trim the cache using an approximate LRU
3450				 * algorithm.  This code deletes the least
3451				 * recently used entry on each hash list.
3452				 */
3453				if (nfsrv_usercnt <= nfsrv_usermax)
3454					break;
3455				usrp = TAILQ_FIRST(&nfsuserhash[i].lughead);
3456				if (usrp != NULL)
3457					nfsrv_removeuser(usrp, 1);
3458			}
3459		} else {
3460			if (groupname_locked == 0) {
3461				for (i = 0; i < nfsrv_lughashsize; i++)
3462					mtx_lock(&nfsgroupnamehash[i].mtx);
3463				groupname_locked = 1;
3464			}
3465			KASSERT(group_locked == 0,
3466			    ("nfssvc_idname: group_locked"));
3467			for (i = 0; i < nfsrv_lughashsize; i++)
3468				mtx_lock(&nfsgrouphash[i].mtx);
3469			group_locked = 1;
3470			for (i = 0; i < nfsrv_lughashsize; i++) {
3471				TAILQ_FOREACH_SAFE(usrp,
3472				    &nfsgrouphash[i].lughead, lug_numhash,
3473				    nusrp)
3474					if (usrp->lug_expiry < NFSD_MONOSEC)
3475						nfsrv_removeuser(usrp, 0);
3476			}
3477			for (i = 0; i < nfsrv_lughashsize; i++) {
3478				/*
3479				 * Trim the cache using an approximate LRU
3480				 * algorithm.  This code deletes the least
3481				 * recently user entry on each hash list.
3482				 */
3483				if (nfsrv_usercnt <= nfsrv_usermax)
3484					break;
3485				usrp = TAILQ_FIRST(&nfsgrouphash[i].lughead);
3486				if (usrp != NULL)
3487					nfsrv_removeuser(usrp, 0);
3488			}
3489		}
3490		lasttime = NFSD_MONOSEC;
3491		atomic_store_rel_int(&onethread, 0);
3492	}
3493
3494	/* Now, unlock all locked mutexes. */
3495	if (hp_idnum != NULL)
3496		mtx_unlock(&hp_idnum->mtx);
3497	if (hp_name != NULL)
3498		mtx_unlock(&hp_name->mtx);
3499	if (user_locked != 0)
3500		for (i = 0; i < nfsrv_lughashsize; i++)
3501			mtx_unlock(&nfsuserhash[i].mtx);
3502	if (username_locked != 0)
3503		for (i = 0; i < nfsrv_lughashsize; i++)
3504			mtx_unlock(&nfsusernamehash[i].mtx);
3505	if (group_locked != 0)
3506		for (i = 0; i < nfsrv_lughashsize; i++)
3507			mtx_unlock(&nfsgrouphash[i].mtx);
3508	if (groupname_locked != 0)
3509		for (i = 0; i < nfsrv_lughashsize; i++)
3510			mtx_unlock(&nfsgroupnamehash[i].mtx);
3511out:
3512	NFSEXITCODE(error);
3513	return (error);
3514}
3515
3516/*
3517 * Remove a user/group name element.
3518 */
3519static void
3520nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
3521{
3522	struct nfsrv_lughash *hp;
3523
3524	if (isuser != 0) {
3525		hp = NFSUSERHASH(usrp->lug_uid);
3526		mtx_assert(&hp->mtx, MA_OWNED);
3527		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3528		hp = NFSUSERNAMEHASH(usrp->lug_name, usrp->lug_namelen);
3529		mtx_assert(&hp->mtx, MA_OWNED);
3530		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
3531	} else {
3532		hp = NFSGROUPHASH(usrp->lug_gid);
3533		mtx_assert(&hp->mtx, MA_OWNED);
3534		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3535		hp = NFSGROUPNAMEHASH(usrp->lug_name, usrp->lug_namelen);
3536		mtx_assert(&hp->mtx, MA_OWNED);
3537		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
3538	}
3539	atomic_add_int(&nfsrv_usercnt, -1);
3540	if (usrp->lug_cred != NULL)
3541		crfree(usrp->lug_cred);
3542	free(usrp, M_NFSUSERGROUP);
3543}
3544
3545/*
3546 * Free up all the allocations related to the name<-->id cache.
3547 * This function should only be called when the nfsuserd daemon isn't
3548 * running, since it doesn't do any locking.
3549 * This function is meant to be used when the nfscommon module is unloaded.
3550 */
3551APPLESTATIC void
3552nfsrv_cleanusergroup(void)
3553{
3554	struct nfsrv_lughash *hp, *hp2;
3555	struct nfsusrgrp *nusrp, *usrp;
3556	int i;
3557
3558	if (nfsuserhash == NULL)
3559		return;
3560
3561	for (i = 0; i < nfsrv_lughashsize; i++) {
3562		hp = &nfsuserhash[i];
3563		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
3564			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3565			hp2 = NFSUSERNAMEHASH(usrp->lug_name,
3566			    usrp->lug_namelen);
3567			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
3568			if (usrp->lug_cred != NULL)
3569				crfree(usrp->lug_cred);
3570			free(usrp, M_NFSUSERGROUP);
3571		}
3572		hp = &nfsgrouphash[i];
3573		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
3574			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
3575			hp2 = NFSGROUPNAMEHASH(usrp->lug_name,
3576			    usrp->lug_namelen);
3577			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
3578			if (usrp->lug_cred != NULL)
3579				crfree(usrp->lug_cred);
3580			free(usrp, M_NFSUSERGROUP);
3581		}
3582		mtx_destroy(&nfsuserhash[i].mtx);
3583		mtx_destroy(&nfsusernamehash[i].mtx);
3584		mtx_destroy(&nfsgroupnamehash[i].mtx);
3585		mtx_destroy(&nfsgrouphash[i].mtx);
3586	}
3587	free(nfsuserhash, M_NFSUSERGROUP);
3588	free(nfsusernamehash, M_NFSUSERGROUP);
3589	free(nfsgrouphash, M_NFSUSERGROUP);
3590	free(nfsgroupnamehash, M_NFSUSERGROUP);
3591	free(nfsrv_dnsname, M_NFSSTRING);
3592}
3593
3594/*
3595 * This function scans a byte string and checks for UTF-8 compliance.
3596 * It returns 0 if it conforms and NFSERR_INVAL if not.
3597 */
3598APPLESTATIC int
3599nfsrv_checkutf8(u_int8_t *cp, int len)
3600{
3601	u_int32_t val = 0x0;
3602	int cnt = 0, gotd = 0, shift = 0;
3603	u_int8_t byte;
3604	static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
3605	int error = 0;
3606
3607	/*
3608	 * Here are what the variables are used for:
3609	 * val - the calculated value of a multibyte char, used to check
3610	 *       that it was coded with the correct range
3611	 * cnt - the number of 10xxxxxx bytes to follow
3612	 * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
3613	 * shift - lower order bits of range (ie. "val >> shift" should
3614	 *       not be 0, in other words, dividing by the lower bound
3615	 *       of the range should get a non-zero value)
3616	 * byte - used to calculate cnt
3617	 */
3618	while (len > 0) {
3619		if (cnt > 0) {
3620			/* This handles the 10xxxxxx bytes */
3621			if ((*cp & 0xc0) != 0x80 ||
3622			    (gotd && (*cp & 0x20))) {
3623				error = NFSERR_INVAL;
3624				goto out;
3625			}
3626			gotd = 0;
3627			val <<= 6;
3628			val |= (*cp & 0x3f);
3629			cnt--;
3630			if (cnt == 0 && (val >> shift) == 0x0) {
3631				error = NFSERR_INVAL;
3632				goto out;
3633			}
3634		} else if (*cp & 0x80) {
3635			/* first byte of multi byte char */
3636			byte = *cp;
3637			while ((byte & 0x40) && cnt < 6) {
3638				cnt++;
3639				byte <<= 1;
3640			}
3641			if (cnt == 0 || cnt == 6) {
3642				error = NFSERR_INVAL;
3643				goto out;
3644			}
3645			val = (*cp & (0x3f >> cnt));
3646			shift = utf8_shift[cnt - 1];
3647			if (cnt == 2 && val == 0xd)
3648				/* Check for the 0xd800-0xdfff case */
3649				gotd = 1;
3650		}
3651		cp++;
3652		len--;
3653	}
3654	if (cnt > 0)
3655		error = NFSERR_INVAL;
3656
3657out:
3658	NFSEXITCODE(error);
3659	return (error);
3660}
3661
3662/*
3663 * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
3664 * strings, one with the root path in it and the other with the list of
3665 * locations. The list is in the same format as is found in nfr_refs.
3666 * It is a "," separated list of entries, where each of them is of the
3667 * form <server>:<rootpath>. For example
3668 * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
3669 * The nilp argument is set to 1 for the special case of a null fs_root
3670 * and an empty server list.
3671 * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
3672 * number of xdr bytes parsed in sump.
3673 */
3674static int
3675nfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
3676    int *sump, int *nilp)
3677{
3678	u_int32_t *tl;
3679	u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
3680	int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
3681	struct list {
3682		SLIST_ENTRY(list) next;
3683		int len;
3684		u_char host[1];
3685	} *lsp, *nlsp;
3686	SLIST_HEAD(, list) head;
3687
3688	*fsrootp = NULL;
3689	*srvp = NULL;
3690	*nilp = 0;
3691
3692	/*
3693	 * Get the fs_root path and check for the special case of null path
3694	 * and 0 length server list.
3695	 */
3696	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3697	len = fxdr_unsigned(int, *tl);
3698	if (len < 0 || len > 10240) {
3699		error = NFSERR_BADXDR;
3700		goto nfsmout;
3701	}
3702	if (len == 0) {
3703		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3704		if (*tl != 0) {
3705			error = NFSERR_BADXDR;
3706			goto nfsmout;
3707		}
3708		*nilp = 1;
3709		*sump = 2 * NFSX_UNSIGNED;
3710		error = 0;
3711		goto nfsmout;
3712	}
3713	cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
3714	error = nfsrv_mtostr(nd, cp, len);
3715	if (!error) {
3716		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3717		cnt = fxdr_unsigned(int, *tl);
3718		if (cnt <= 0)
3719			error = NFSERR_BADXDR;
3720	}
3721	if (error)
3722		goto nfsmout;
3723
3724	/*
3725	 * Now, loop through the location list and make up the srvlist.
3726	 */
3727	xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3728	cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
3729	slen = 1024;
3730	siz = 0;
3731	for (i = 0; i < cnt; i++) {
3732		SLIST_INIT(&head);
3733		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3734		nsrv = fxdr_unsigned(int, *tl);
3735		if (nsrv <= 0) {
3736			error = NFSERR_BADXDR;
3737			goto nfsmout;
3738		}
3739
3740		/*
3741		 * Handle the first server by putting it in the srvstr.
3742		 */
3743		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3744		len = fxdr_unsigned(int, *tl);
3745		if (len <= 0 || len > 1024) {
3746			error = NFSERR_BADXDR;
3747			goto nfsmout;
3748		}
3749		nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
3750		if (cp3 != cp2) {
3751			*cp3++ = ',';
3752			siz++;
3753		}
3754		error = nfsrv_mtostr(nd, cp3, len);
3755		if (error)
3756			goto nfsmout;
3757		cp3 += len;
3758		*cp3++ = ':';
3759		siz += (len + 1);
3760		xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
3761		for (j = 1; j < nsrv; j++) {
3762			/*
3763			 * Yuck, put them in an slist and process them later.
3764			 */
3765			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3766			len = fxdr_unsigned(int, *tl);
3767			if (len <= 0 || len > 1024) {
3768				error = NFSERR_BADXDR;
3769				goto nfsmout;
3770			}
3771			lsp = (struct list *)malloc(sizeof (struct list)
3772			    + len, M_TEMP, M_WAITOK);
3773			error = nfsrv_mtostr(nd, lsp->host, len);
3774			if (error)
3775				goto nfsmout;
3776			xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3777			lsp->len = len;
3778			SLIST_INSERT_HEAD(&head, lsp, next);
3779		}
3780
3781		/*
3782		 * Finally, we can get the path.
3783		 */
3784		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3785		len = fxdr_unsigned(int, *tl);
3786		if (len <= 0 || len > 1024) {
3787			error = NFSERR_BADXDR;
3788			goto nfsmout;
3789		}
3790		nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
3791		error = nfsrv_mtostr(nd, cp3, len);
3792		if (error)
3793			goto nfsmout;
3794		xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
3795		str = cp3;
3796		stringlen = len;
3797		cp3 += len;
3798		siz += len;
3799		SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
3800			nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
3801			    &cp2, &cp3, &slen);
3802			*cp3++ = ',';
3803			NFSBCOPY(lsp->host, cp3, lsp->len);
3804			cp3 += lsp->len;
3805			*cp3++ = ':';
3806			NFSBCOPY(str, cp3, stringlen);
3807			cp3 += stringlen;
3808			*cp3 = '\0';
3809			siz += (lsp->len + stringlen + 2);
3810			free((caddr_t)lsp, M_TEMP);
3811		}
3812	}
3813	*fsrootp = cp;
3814	*srvp = cp2;
3815	*sump = xdrsum;
3816	NFSEXITCODE2(0, nd);
3817	return (0);
3818nfsmout:
3819	if (cp != NULL)
3820		free(cp, M_NFSSTRING);
3821	if (cp2 != NULL)
3822		free(cp2, M_NFSSTRING);
3823	NFSEXITCODE2(error, nd);
3824	return (error);
3825}
3826
3827/*
3828 * Make the malloc'd space large enough. This is a pain, but the xdr
3829 * doesn't set an upper bound on the side, so...
3830 */
3831static void
3832nfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
3833{
3834	u_char *cp;
3835	int i;
3836
3837	if (siz <= *slenp)
3838		return;
3839	cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
3840	NFSBCOPY(*cpp, cp, *slenp);
3841	free(*cpp, M_NFSSTRING);
3842	i = *cpp2 - *cpp;
3843	*cpp = cp;
3844	*cpp2 = cp + i;
3845	*slenp = siz + 1024;
3846}
3847
3848/*
3849 * Initialize the reply header data structures.
3850 */
3851APPLESTATIC void
3852nfsrvd_rephead(struct nfsrv_descript *nd)
3853{
3854	mbuf_t mreq;
3855
3856	/*
3857	 * If this is a big reply, use a cluster.
3858	 */
3859	if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
3860	    nfs_bigreply[nd->nd_procnum]) {
3861		NFSMCLGET(mreq, M_WAITOK);
3862		nd->nd_mreq = mreq;
3863		nd->nd_mb = mreq;
3864	} else {
3865		NFSMGET(mreq);
3866		nd->nd_mreq = mreq;
3867		nd->nd_mb = mreq;
3868	}
3869	nd->nd_bpos = NFSMTOD(mreq, caddr_t);
3870	mbuf_setlen(mreq, 0);
3871
3872	if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
3873		NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
3874}
3875
3876/*
3877 * Lock a socket against others.
3878 * Currently used to serialize connect/disconnect attempts.
3879 */
3880int
3881newnfs_sndlock(int *flagp)
3882{
3883	struct timespec ts;
3884
3885	NFSLOCKSOCK();
3886	while (*flagp & NFSR_SNDLOCK) {
3887		*flagp |= NFSR_WANTSND;
3888		ts.tv_sec = 0;
3889		ts.tv_nsec = 0;
3890		(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
3891		    PZERO - 1, "nfsndlck", &ts);
3892	}
3893	*flagp |= NFSR_SNDLOCK;
3894	NFSUNLOCKSOCK();
3895	return (0);
3896}
3897
3898/*
3899 * Unlock the stream socket for others.
3900 */
3901void
3902newnfs_sndunlock(int *flagp)
3903{
3904
3905	NFSLOCKSOCK();
3906	if ((*flagp & NFSR_SNDLOCK) == 0)
3907		panic("nfs sndunlock");
3908	*flagp &= ~NFSR_SNDLOCK;
3909	if (*flagp & NFSR_WANTSND) {
3910		*flagp &= ~NFSR_WANTSND;
3911		wakeup((caddr_t)flagp);
3912	}
3913	NFSUNLOCKSOCK();
3914}
3915
3916APPLESTATIC int
3917nfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_storage *sa,
3918    int *isudp)
3919{
3920	struct sockaddr_in *sad;
3921	struct sockaddr_in6 *sad6;
3922	struct in_addr saddr;
3923	uint32_t portnum, *tl;
3924	int af = 0, i, j, k;
3925	char addr[64], protocol[5], *cp;
3926	int cantparse = 0, error = 0;
3927	uint16_t portv;
3928
3929	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3930	i = fxdr_unsigned(int, *tl);
3931	if (i >= 3 && i <= 4) {
3932		error = nfsrv_mtostr(nd, protocol, i);
3933		if (error)
3934			goto nfsmout;
3935		if (strcmp(protocol, "tcp") == 0) {
3936			af = AF_INET;
3937			*isudp = 0;
3938		} else if (strcmp(protocol, "udp") == 0) {
3939			af = AF_INET;
3940			*isudp = 1;
3941		} else if (strcmp(protocol, "tcp6") == 0) {
3942			af = AF_INET6;
3943			*isudp = 0;
3944		} else if (strcmp(protocol, "udp6") == 0) {
3945			af = AF_INET6;
3946			*isudp = 1;
3947		} else
3948			cantparse = 1;
3949	} else {
3950		cantparse = 1;
3951		if (i > 0) {
3952			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
3953			if (error)
3954				goto nfsmout;
3955		}
3956	}
3957	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3958	i = fxdr_unsigned(int, *tl);
3959	if (i < 0) {
3960		error = NFSERR_BADXDR;
3961		goto nfsmout;
3962	} else if (cantparse == 0 && i >= 11 && i < 64) {
3963		/*
3964		 * The shortest address is 11chars and the longest is < 64.
3965		 */
3966		error = nfsrv_mtostr(nd, addr, i);
3967		if (error)
3968			goto nfsmout;
3969
3970		/* Find the port# at the end and extract that. */
3971		i = strlen(addr);
3972		k = 0;
3973		cp = &addr[i - 1];
3974		/* Count back two '.'s from end to get port# field. */
3975		for (j = 0; j < i; j++) {
3976			if (*cp == '.') {
3977				k++;
3978				if (k == 2)
3979					break;
3980			}
3981			cp--;
3982		}
3983		if (k == 2) {
3984			/*
3985			 * The NFSv4 port# is appended as .N.N, where N is
3986			 * a decimal # in the range 0-255, just like an inet4
3987			 * address. Cheat and use inet_aton(), which will
3988			 * return a Class A address and then shift the high
3989			 * order 8bits over to convert it to the port#.
3990			 */
3991			*cp++ = '\0';
3992			if (inet_aton(cp, &saddr) == 1) {
3993				portnum = ntohl(saddr.s_addr);
3994				portv = (uint16_t)((portnum >> 16) |
3995				    (portnum & 0xff));
3996			} else
3997				cantparse = 1;
3998		} else
3999			cantparse = 1;
4000		if (cantparse == 0) {
4001			if (af == AF_INET) {
4002				sad = (struct sockaddr_in *)sa;
4003				if (inet_pton(af, addr, &sad->sin_addr) == 1) {
4004					sad->sin_len = sizeof(*sad);
4005					sad->sin_family = AF_INET;
4006					sad->sin_port = htons(portv);
4007					return (0);
4008				}
4009			} else {
4010				sad6 = (struct sockaddr_in6 *)sa;
4011				if (inet_pton(af, addr, &sad6->sin6_addr)
4012				    == 1) {
4013					sad6->sin6_len = sizeof(*sad6);
4014					sad6->sin6_family = AF_INET6;
4015					sad6->sin6_port = htons(portv);
4016					return (0);
4017				}
4018			}
4019		}
4020	} else {
4021		if (i > 0) {
4022			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
4023			if (error)
4024				goto nfsmout;
4025		}
4026	}
4027	error = EPERM;
4028nfsmout:
4029	return (error);
4030}
4031
4032/*
4033 * Handle an NFSv4.1 Sequence request for the session.
4034 * If reply != NULL, use it to return the cached reply, as required.
4035 * The client gets a cached reply via this call for callbacks, however the
4036 * server gets a cached reply via the nfsv4_seqsess_cachereply() call.
4037 */
4038int
4039nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot,
4040    struct nfsslot *slots, struct mbuf **reply, uint16_t maxslot)
4041{
4042	int error;
4043
4044	error = 0;
4045	if (reply != NULL)
4046		*reply = NULL;
4047	if (slotid > maxslot)
4048		return (NFSERR_BADSLOT);
4049	if (seqid == slots[slotid].nfssl_seq) {
4050		/* A retry. */
4051		if (slots[slotid].nfssl_inprog != 0)
4052			error = NFSERR_DELAY;
4053		else if (slots[slotid].nfssl_reply != NULL) {
4054			if (reply != NULL) {
4055				*reply = slots[slotid].nfssl_reply;
4056				slots[slotid].nfssl_reply = NULL;
4057			}
4058			slots[slotid].nfssl_inprog = 1;
4059			error = NFSERR_REPLYFROMCACHE;
4060		} else
4061			/* No reply cached, so just do it. */
4062			slots[slotid].nfssl_inprog = 1;
4063	} else if ((slots[slotid].nfssl_seq + 1) == seqid) {
4064		if (slots[slotid].nfssl_reply != NULL)
4065			m_freem(slots[slotid].nfssl_reply);
4066		slots[slotid].nfssl_reply = NULL;
4067		slots[slotid].nfssl_inprog = 1;
4068		slots[slotid].nfssl_seq++;
4069	} else
4070		error = NFSERR_SEQMISORDERED;
4071	return (error);
4072}
4073
4074/*
4075 * Cache this reply for the slot.
4076 * Use the "rep" argument to return the cached reply if repstat is set to
4077 * NFSERR_REPLYFROMCACHE. The client never sets repstat to this value.
4078 */
4079void
4080nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, int repstat,
4081   struct mbuf **rep)
4082{
4083
4084	if (repstat == NFSERR_REPLYFROMCACHE) {
4085		*rep = slots[slotid].nfssl_reply;
4086		slots[slotid].nfssl_reply = NULL;
4087	} else {
4088		if (slots[slotid].nfssl_reply != NULL)
4089			m_freem(slots[slotid].nfssl_reply);
4090		slots[slotid].nfssl_reply = *rep;
4091	}
4092	slots[slotid].nfssl_inprog = 0;
4093}
4094
4095/*
4096 * Generate the xdr for an NFSv4.1 Sequence Operation.
4097 */
4098APPLESTATIC void
4099nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd,
4100    struct nfsclsession *sep, int dont_replycache)
4101{
4102	uint32_t *tl, slotseq = 0;
4103	int error, maxslot, slotpos;
4104	uint8_t sessionid[NFSX_V4SESSIONID];
4105
4106	error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot, &slotseq,
4107	    sessionid);
4108	if (error != 0)
4109		return;
4110	KASSERT(maxslot >= 0, ("nfscl_setsequence neg maxslot"));
4111
4112	/* Build the Sequence arguments. */
4113	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
4114	bcopy(sessionid, tl, NFSX_V4SESSIONID);
4115	tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
4116	nd->nd_slotseq = tl;
4117	*tl++ = txdr_unsigned(slotseq);
4118	*tl++ = txdr_unsigned(slotpos);
4119	*tl++ = txdr_unsigned(maxslot);
4120	if (dont_replycache == 0)
4121		*tl = newnfs_true;
4122	else
4123		*tl = newnfs_false;
4124	nd->nd_flag |= ND_HASSEQUENCE;
4125}
4126
4127int
4128nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep,
4129    int *slotposp, int *maxslotp, uint32_t *slotseqp, uint8_t *sessionid)
4130{
4131	int i, maxslot, slotpos;
4132	uint64_t bitval;
4133
4134	/* Find an unused slot. */
4135	slotpos = -1;
4136	maxslot = -1;
4137	mtx_lock(&sep->nfsess_mtx);
4138	do {
4139		bitval = 1;
4140		for (i = 0; i < sep->nfsess_foreslots; i++) {
4141			if ((bitval & sep->nfsess_slots) == 0) {
4142				slotpos = i;
4143				sep->nfsess_slots |= bitval;
4144				sep->nfsess_slotseq[i]++;
4145				*slotseqp = sep->nfsess_slotseq[i];
4146				break;
4147			}
4148			bitval <<= 1;
4149		}
4150		if (slotpos == -1) {
4151			/*
4152			 * If a forced dismount is in progress, just return.
4153			 * This RPC attempt will fail when it calls
4154			 * newnfs_request().
4155			 */
4156			if (nmp != NULL &&
4157			    (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)
4158			    != 0) {
4159				mtx_unlock(&sep->nfsess_mtx);
4160				return (ESTALE);
4161			}
4162			/* Wake up once/sec, to check for a forced dismount. */
4163			(void)mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx,
4164			    PZERO, "nfsclseq", hz);
4165		}
4166	} while (slotpos == -1);
4167	/* Now, find the highest slot in use. (nfsc_slots is 64bits) */
4168	bitval = 1;
4169	for (i = 0; i < 64; i++) {
4170		if ((bitval & sep->nfsess_slots) != 0)
4171			maxslot = i;
4172		bitval <<= 1;
4173	}
4174	bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID);
4175	mtx_unlock(&sep->nfsess_mtx);
4176	*slotposp = slotpos;
4177	*maxslotp = maxslot;
4178	return (0);
4179}
4180
4181/*
4182 * Free a session slot.
4183 */
4184APPLESTATIC void
4185nfsv4_freeslot(struct nfsclsession *sep, int slot)
4186{
4187	uint64_t bitval;
4188
4189	bitval = 1;
4190	if (slot > 0)
4191		bitval <<= slot;
4192	mtx_lock(&sep->nfsess_mtx);
4193	if ((bitval & sep->nfsess_slots) == 0)
4194		printf("freeing free slot!!\n");
4195	sep->nfsess_slots &= ~bitval;
4196	wakeup(&sep->nfsess_slots);
4197	mtx_unlock(&sep->nfsess_mtx);
4198}
4199
4200