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