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