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