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