]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/nfs/nfs_commonsubs.c
Merge ^/vendor/llvm-openmp/dist up to its last change, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / fs / nfs / nfs_commonsubs.c
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         mbuf_setlen(mb, 0);
364         nd->nd_mreq = nd->nd_mb = mb;
365         nd->nd_bpos = NFSMTOD(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         mbuf_t mp;
615         long uiosiz, rem;
616         int error = 0;
617
618         mp = nd->nd_md;
619         mbufcp = nd->nd_dpos;
620         len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - 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 = mbuf_next(mp);
635                                 if (mp == NULL) {
636                                         error = EBADRPC;
637                                         goto out;
638                                 }
639                                 mbufcp = NFSMTOD(mp, caddr_t);
640                                 len = mbuf_len(mp);
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, CAST_USER_ADDR_T(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         mbuf_t mp2;
698         int siz2, xfer;
699         caddr_t p;
700         int left;
701         caddr_t retp;
702
703         retp = NULL;
704         left = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) - nd->nd_dpos;
705         while (left == 0) {
706                 nd->nd_md = mbuf_next(nd->nd_md);
707                 if (nd->nd_md == NULL)
708                         return (retp);
709                 left = mbuf_len(nd->nd_md);
710                 nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
711         }
712         if (left >= siz) {
713                 retp = nd->nd_dpos;
714                 nd->nd_dpos += siz;
715         } else if (mbuf_next(nd->nd_md) == 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                 mbuf_setnext(mp2, mbuf_next(nd->nd_md));
724                 mbuf_setnext(nd->nd_md, mp2);
725                 mbuf_setlen(nd->nd_md, mbuf_len(nd->nd_md) - left);
726                 nd->nd_md = mp2;
727                 retp = p = NFSMTOD(mp2, caddr_t);
728                 NFSBCOPY(nd->nd_dpos, p, left); /* Copy what was left */
729                 siz2 = siz - left;
730                 p += left;
731                 mp2 = mbuf_next(mp2);
732                 /* Loop around copying up the siz2 bytes */
733                 while (siz2 > 0) {
734                         if (mp2 == NULL)
735                                 return (NULL);
736                         xfer = (siz2 > mbuf_len(mp2)) ? mbuf_len(mp2) : siz2;
737                         if (xfer > 0) {
738                                 NFSBCOPY(NFSMTOD(mp2, caddr_t), p, xfer);
739                                 NFSM_DATAP(mp2, xfer);
740                                 mbuf_setlen(mp2, mbuf_len(mp2) - xfer);
741                                 p += xfer;
742                                 siz2 -= xfer;
743                         }
744                         if (siz2 > 0)
745                                 mp2 = mbuf_next(mp2);
746                 }
747                 mbuf_setlen(nd->nd_md, siz);
748                 nd->nd_md = mp2;
749                 nd->nd_dpos = NFSMTOD(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 = NFSMTOD(nd->nd_md, caddr_t) + mbuf_len(nd->nd_md) -
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 = mbuf_next(nd->nd_md);
790                 if (nd->nd_md == NULL) {
791                         error = EBADRPC;
792                         goto out;
793                 }
794                 left = mbuf_len(nd->nd_md);
795                 nd->nd_dpos = NFSMTOD(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         mbuf_t m2;
812         int xfer, left;
813         mbuf_t 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                         mbuf_setlen(m1, 0);
836                         mbuf_setnext(m2, m1);
837                         m2 = m1;
838                         cp2 = NFSMTOD(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                 mbuf_setlen(m2, mbuf_len(m2) + 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                         mbuf_setlen(m2, mbuf_len(m2) + rem);
855                 }
856         }
857         nd->nd_mb = m2;
858         nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
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         mbuf_t 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 (mbuf_next(m) != nd->nd_md) {
1026                         if (mbuf_next(m) == NULL)
1027                                 panic("nfsm trim leading");
1028                         m = mbuf_next(m);
1029                 }
1030                 mbuf_setnext(m, NULL);
1031                 mbuf_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 - NFSMTOD(m, caddr_t);
1039         if (offs == mbuf_len(m)) {
1040                 n = m;
1041                 m = mbuf_next(m);
1042                 if (m == NULL)
1043                         panic("nfsm trim leading2");
1044                 mbuf_setnext(n, NULL);
1045                 mbuf_freem(n);
1046         } else if (offs > 0) {
1047                 mbuf_setlen(m, mbuf_len(m) - offs);
1048                 NFSM_DATAP(m, 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 = NFSMTOD(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         mbuf_t mb;
1063         caddr_t bpos;
1064 {
1065
1066         if (mbuf_next(mb)) {
1067                 mbuf_freem(mbuf_next(mb));
1068                 mbuf_setnext(mb, NULL);
1069         }
1070         mbuf_setlen(mb, bpos - NFSMTOD(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         mbuf_t mp;
2427         int rem, error = 0;
2428
2429         mp = nd->nd_md;
2430         cp = nd->nd_dpos;
2431         len = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - 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 = mbuf_next(mp);
2443                         if (mp == NULL) {
2444                                 error = EBADRPC;
2445                                 goto out;
2446                         }
2447                         cp = NFSMTOD(mp, caddr_t);
2448                         len = mbuf_len(mp);
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                 mbuf_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(CAST_USER_ADDR_T(nidp->nid_name), cp,
3763                     nidp->nid_namelen);
3764                 if (error != 0) {
3765                         free(cp, M_NFSSTRING);
3766                         goto out;
3767                 }
3768                 if (atomic_cmpset_acq_int(&nfsrv_dnsnamelen, 0, 0) == 0) {
3769                         /*
3770                          * Free up all the old stuff and reinitialize hash
3771                          * lists.  All mutexes for both lists must be locked,
3772                          * with the user/group name ones before the uid/gid
3773                          * ones, to avoid a LOR.
3774                          */
3775                         for (i = 0; i < nfsrv_lughashsize; i++)
3776                                 mtx_lock(&nfsusernamehash[i].mtx);
3777                         for (i = 0; i < nfsrv_lughashsize; i++)
3778                                 mtx_lock(&nfsuserhash[i].mtx);
3779                         for (i = 0; i < nfsrv_lughashsize; i++)
3780                                 TAILQ_FOREACH_SAFE(usrp,
3781                                     &nfsuserhash[i].lughead, lug_numhash, nusrp)
3782                                         nfsrv_removeuser(usrp, 1);
3783                         for (i = 0; i < nfsrv_lughashsize; i++)
3784                                 mtx_unlock(&nfsuserhash[i].mtx);
3785                         for (i = 0; i < nfsrv_lughashsize; i++)
3786                                 mtx_unlock(&nfsusernamehash[i].mtx);
3787                         for (i = 0; i < nfsrv_lughashsize; i++)
3788                                 mtx_lock(&nfsgroupnamehash[i].mtx);
3789                         for (i = 0; i < nfsrv_lughashsize; i++)
3790                                 mtx_lock(&nfsgrouphash[i].mtx);
3791                         for (i = 0; i < nfsrv_lughashsize; i++)
3792                                 TAILQ_FOREACH_SAFE(usrp,
3793                                     &nfsgrouphash[i].lughead, lug_numhash,
3794                                     nusrp)
3795                                         nfsrv_removeuser(usrp, 0);
3796                         for (i = 0; i < nfsrv_lughashsize; i++)
3797                                 mtx_unlock(&nfsgrouphash[i].mtx);
3798                         for (i = 0; i < nfsrv_lughashsize; i++)
3799                                 mtx_unlock(&nfsgroupnamehash[i].mtx);
3800                         free(nfsrv_dnsname, M_NFSSTRING);
3801                         nfsrv_dnsname = NULL;
3802                 }
3803                 if (nfsuserhash == NULL) {
3804                         /* Allocate the hash tables. */
3805                         nfsuserhash = malloc(sizeof(struct nfsrv_lughash) *
3806                             nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3807                             M_ZERO);
3808                         for (i = 0; i < nfsrv_lughashsize; i++)
3809                                 mtx_init(&nfsuserhash[i].mtx, "nfsuidhash",
3810                                     NULL, MTX_DEF | MTX_DUPOK);
3811                         nfsusernamehash = malloc(sizeof(struct nfsrv_lughash) *
3812                             nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3813                             M_ZERO);
3814                         for (i = 0; i < nfsrv_lughashsize; i++)
3815                                 mtx_init(&nfsusernamehash[i].mtx,
3816                                     "nfsusrhash", NULL, MTX_DEF |
3817                                     MTX_DUPOK);
3818                         nfsgrouphash = malloc(sizeof(struct nfsrv_lughash) *
3819                             nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3820                             M_ZERO);
3821                         for (i = 0; i < nfsrv_lughashsize; i++)
3822                                 mtx_init(&nfsgrouphash[i].mtx, "nfsgidhash",
3823                                     NULL, MTX_DEF | MTX_DUPOK);
3824                         nfsgroupnamehash = malloc(sizeof(struct nfsrv_lughash) *
3825                             nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
3826                             M_ZERO);
3827                         for (i = 0; i < nfsrv_lughashsize; i++)
3828                             mtx_init(&nfsgroupnamehash[i].mtx,
3829                             "nfsgrphash", NULL, MTX_DEF | MTX_DUPOK);
3830                 }
3831                 /* (Re)initialize the list heads. */
3832                 for (i = 0; i < nfsrv_lughashsize; i++)
3833                         TAILQ_INIT(&nfsuserhash[i].lughead);
3834                 for (i = 0; i < nfsrv_lughashsize; i++)
3835                         TAILQ_INIT(&nfsusernamehash[i].lughead);
3836                 for (i = 0; i < nfsrv_lughashsize; i++)
3837                         TAILQ_INIT(&nfsgrouphash[i].lughead);
3838                 for (i = 0; i < nfsrv_lughashsize; i++)
3839                         TAILQ_INIT(&nfsgroupnamehash[i].lughead);
3840
3841                 /*
3842                  * Put name in "DNS" string.
3843                  */
3844                 nfsrv_dnsname = cp;
3845                 nfsrv_defaultuid = nidp->nid_uid;
3846                 nfsrv_defaultgid = nidp->nid_gid;
3847                 nfsrv_usercnt = 0;
3848                 nfsrv_usermax = nidp->nid_usermax;
3849                 atomic_store_rel_int(&nfsrv_dnsnamelen, nidp->nid_namelen);
3850                 goto out;
3851         }
3852
3853         /*
3854          * malloc the new one now, so any potential sleep occurs before
3855          * manipulation of the lists.
3856          */
3857         newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
3858             M_NFSUSERGROUP, M_WAITOK | M_ZERO);
3859         error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
3860             nidp->nid_namelen);
3861         if (error == 0 && nidp->nid_ngroup > 0 &&
3862             (nidp->nid_flag & NFSID_ADDUID) != 0) {
3863                 grps = malloc(sizeof(gid_t) * nidp->nid_ngroup, M_TEMP,
3864                     M_WAITOK);
3865                 error = copyin(CAST_USER_ADDR_T(nidp->nid_grps), grps,
3866                     sizeof(gid_t) * nidp->nid_ngroup);
3867                 if (error == 0) {
3868                         /*
3869                          * Create a credential just like svc_getcred(),
3870                          * but using the group list provided.
3871                          */
3872                         cr = crget();
3873                         cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid;
3874                         crsetgroups(cr, nidp->nid_ngroup, grps);
3875                         cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
3876                         cr->cr_prison = &prison0;
3877                         prison_hold(cr->cr_prison);
3878 #ifdef MAC
3879                         mac_cred_associate_nfsd(cr);
3880 #endif
3881                         newusrp->lug_cred = cr;
3882                 }
3883                 free(grps, M_TEMP);
3884         }
3885         if (error) {
3886                 free(newusrp, M_NFSUSERGROUP);
3887                 goto out;
3888         }
3889         newusrp->lug_namelen = nidp->nid_namelen;
3890
3891         /*
3892          * The lock order is username[0]->[nfsrv_lughashsize - 1] followed
3893          * by uid[0]->[nfsrv_lughashsize - 1], with the same for group.
3894          * The flags user_locked, username_locked, group_locked and
3895          * groupname_locked are set to indicate all of those hash lists are
3896          * locked. hp_name != NULL  and hp_idnum != NULL indicates that
3897          * the respective one mutex is locked.
3898          */
3899         user_locked = username_locked = group_locked = groupname_locked = 0;
3900         hp_name = hp_idnum = NULL;
3901
3902         /*
3903          * Delete old entries, as required.
3904          */
3905         if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
3906                 /* Must lock all username hash lists first, to avoid a LOR. */
3907                 for (i = 0; i < nfsrv_lughashsize; i++)
3908                         mtx_lock(&nfsusernamehash[i].mtx);
3909                 username_locked = 1;
3910                 hp_idnum = NFSUSERHASH(nidp->nid_uid);
3911                 mtx_lock(&hp_idnum->mtx);
3912                 TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3913                     nusrp) {
3914                         if (usrp->lug_uid == nidp->nid_uid)
3915                                 nfsrv_removeuser(usrp, 1);
3916                 }
3917         } else if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
3918                 hp_name = NFSUSERNAMEHASH(newusrp->lug_name,
3919                     newusrp->lug_namelen);
3920                 mtx_lock(&hp_name->mtx);
3921                 TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3922                     nusrp) {
3923                         if (usrp->lug_namelen == newusrp->lug_namelen &&
3924                             !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3925                             usrp->lug_namelen)) {
3926                                 thp = NFSUSERHASH(usrp->lug_uid);
3927                                 mtx_lock(&thp->mtx);
3928                                 nfsrv_removeuser(usrp, 1);
3929                                 mtx_unlock(&thp->mtx);
3930                         }
3931                 }
3932                 hp_idnum = NFSUSERHASH(nidp->nid_uid);
3933                 mtx_lock(&hp_idnum->mtx);
3934         } else if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
3935                 /* Must lock all groupname hash lists first, to avoid a LOR. */
3936                 for (i = 0; i < nfsrv_lughashsize; i++)
3937                         mtx_lock(&nfsgroupnamehash[i].mtx);
3938                 groupname_locked = 1;
3939                 hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3940                 mtx_lock(&hp_idnum->mtx);
3941                 TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
3942                     nusrp) {
3943                         if (usrp->lug_gid == nidp->nid_gid)
3944                                 nfsrv_removeuser(usrp, 0);
3945                 }
3946         } else if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
3947                 hp_name = NFSGROUPNAMEHASH(newusrp->lug_name,
3948                     newusrp->lug_namelen);
3949                 mtx_lock(&hp_name->mtx);
3950                 TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
3951                     nusrp) {
3952                         if (usrp->lug_namelen == newusrp->lug_namelen &&
3953                             !NFSBCMP(usrp->lug_name, newusrp->lug_name,
3954                             usrp->lug_namelen)) {
3955                                 thp = NFSGROUPHASH(usrp->lug_gid);
3956                                 mtx_lock(&thp->mtx);
3957                                 nfsrv_removeuser(usrp, 0);
3958                                 mtx_unlock(&thp->mtx);
3959                         }
3960                 }
3961                 hp_idnum = NFSGROUPHASH(nidp->nid_gid);
3962                 mtx_lock(&hp_idnum->mtx);
3963         }
3964
3965         /*
3966          * Now, we can add the new one.
3967          */
3968         if (nidp->nid_usertimeout)
3969                 newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
3970         else
3971                 newusrp->lug_expiry = NFSD_MONOSEC + 5;
3972         if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
3973                 newusrp->lug_uid = nidp->nid_uid;
3974                 thp = NFSUSERHASH(newusrp->lug_uid);
3975                 mtx_assert(&thp->mtx, MA_OWNED);
3976                 TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3977                 thp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3978                 mtx_assert(&thp->mtx, MA_OWNED);
3979                 TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3980                 atomic_add_int(&nfsrv_usercnt, 1);
3981         } else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
3982                 newusrp->lug_gid = nidp->nid_gid;
3983                 thp = NFSGROUPHASH(newusrp->lug_gid);
3984                 mtx_assert(&thp->mtx, MA_OWNED);
3985                 TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
3986                 thp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
3987                 mtx_assert(&thp->mtx, MA_OWNED);
3988                 TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
3989                 atomic_add_int(&nfsrv_usercnt, 1);
3990         } else {
3991                 if (newusrp->lug_cred != NULL)
3992                         crfree(newusrp->lug_cred);
3993                 free(newusrp, M_NFSUSERGROUP);
3994         }
3995
3996         /*
3997          * Once per second, allow one thread to trim the cache.
3998          */
3999         if (lasttime < NFSD_MONOSEC &&
4000             atomic_cmpset_acq_int(&onethread, 0, 1) != 0) {
4001                 /*
4002                  * First, unlock the single mutexes, so that all entries
4003                  * can be locked and any LOR is avoided.
4004                  */
4005                 if (hp_name != NULL) {
4006                         mtx_unlock(&hp_name->mtx);
4007                         hp_name = NULL;
4008                 }
4009                 if (hp_idnum != NULL) {
4010                         mtx_unlock(&hp_idnum->mtx);
4011                         hp_idnum = NULL;
4012                 }
4013
4014                 if ((nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID |
4015                     NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) != 0) {
4016                         if (username_locked == 0) {
4017                                 for (i = 0; i < nfsrv_lughashsize; i++)
4018                                         mtx_lock(&nfsusernamehash[i].mtx);
4019                                 username_locked = 1;
4020                         }
4021                         KASSERT(user_locked == 0,
4022                             ("nfssvc_idname: user_locked"));
4023                         for (i = 0; i < nfsrv_lughashsize; i++)
4024                                 mtx_lock(&nfsuserhash[i].mtx);
4025                         user_locked = 1;
4026                         for (i = 0; i < nfsrv_lughashsize; i++) {
4027                                 TAILQ_FOREACH_SAFE(usrp,
4028                                     &nfsuserhash[i].lughead, lug_numhash,
4029                                     nusrp)
4030                                         if (usrp->lug_expiry < NFSD_MONOSEC)
4031                                                 nfsrv_removeuser(usrp, 1);
4032                         }
4033                         for (i = 0; i < nfsrv_lughashsize; i++) {
4034                                 /*
4035                                  * Trim the cache using an approximate LRU
4036                                  * algorithm.  This code deletes the least
4037                                  * recently used entry on each hash list.
4038                                  */
4039                                 if (nfsrv_usercnt <= nfsrv_usermax)
4040                                         break;
4041                                 usrp = TAILQ_FIRST(&nfsuserhash[i].lughead);
4042                                 if (usrp != NULL)
4043                                         nfsrv_removeuser(usrp, 1);
4044                         }
4045                 } else {
4046                         if (groupname_locked == 0) {
4047                                 for (i = 0; i < nfsrv_lughashsize; i++)
4048                                         mtx_lock(&nfsgroupnamehash[i].mtx);
4049                                 groupname_locked = 1;
4050                         }
4051                         KASSERT(group_locked == 0,
4052                             ("nfssvc_idname: group_locked"));
4053                         for (i = 0; i < nfsrv_lughashsize; i++)
4054                                 mtx_lock(&nfsgrouphash[i].mtx);
4055                         group_locked = 1;
4056                         for (i = 0; i < nfsrv_lughashsize; i++) {
4057                                 TAILQ_FOREACH_SAFE(usrp,
4058                                     &nfsgrouphash[i].lughead, lug_numhash,
4059                                     nusrp)
4060                                         if (usrp->lug_expiry < NFSD_MONOSEC)
4061                                                 nfsrv_removeuser(usrp, 0);
4062                         }
4063                         for (i = 0; i < nfsrv_lughashsize; i++) {
4064                                 /*
4065                                  * Trim the cache using an approximate LRU
4066                                  * algorithm.  This code deletes the least
4067                                  * recently user entry on each hash list.
4068                                  */
4069                                 if (nfsrv_usercnt <= nfsrv_usermax)
4070                                         break;
4071                                 usrp = TAILQ_FIRST(&nfsgrouphash[i].lughead);
4072                                 if (usrp != NULL)
4073                                         nfsrv_removeuser(usrp, 0);
4074                         }
4075                 }
4076                 lasttime = NFSD_MONOSEC;
4077                 atomic_store_rel_int(&onethread, 0);
4078         }
4079
4080         /* Now, unlock all locked mutexes. */
4081         if (hp_idnum != NULL)
4082                 mtx_unlock(&hp_idnum->mtx);
4083         if (hp_name != NULL)
4084                 mtx_unlock(&hp_name->mtx);
4085         if (user_locked != 0)
4086                 for (i = 0; i < nfsrv_lughashsize; i++)
4087                         mtx_unlock(&nfsuserhash[i].mtx);
4088         if (username_locked != 0)
4089                 for (i = 0; i < nfsrv_lughashsize; i++)
4090                         mtx_unlock(&nfsusernamehash[i].mtx);
4091         if (group_locked != 0)
4092                 for (i = 0; i < nfsrv_lughashsize; i++)
4093                         mtx_unlock(&nfsgrouphash[i].mtx);
4094         if (groupname_locked != 0)
4095                 for (i = 0; i < nfsrv_lughashsize; i++)
4096                         mtx_unlock(&nfsgroupnamehash[i].mtx);
4097 out:
4098         NFSEXITCODE(error);
4099         return (error);
4100 }
4101
4102 /*
4103  * Remove a user/group name element.
4104  */
4105 static void
4106 nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
4107 {
4108         struct nfsrv_lughash *hp;
4109
4110         if (isuser != 0) {
4111                 hp = NFSUSERHASH(usrp->lug_uid);
4112                 mtx_assert(&hp->mtx, MA_OWNED);
4113                 TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
4114                 hp = NFSUSERNAMEHASH(usrp->lug_name, usrp->lug_namelen);
4115                 mtx_assert(&hp->mtx, MA_OWNED);
4116                 TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
4117         } else {
4118                 hp = NFSGROUPHASH(usrp->lug_gid);
4119                 mtx_assert(&hp->mtx, MA_OWNED);
4120                 TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
4121                 hp = NFSGROUPNAMEHASH(usrp->lug_name, usrp->lug_namelen);
4122                 mtx_assert(&hp->mtx, MA_OWNED);
4123                 TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
4124         }
4125         atomic_add_int(&nfsrv_usercnt, -1);
4126         if (usrp->lug_cred != NULL)
4127                 crfree(usrp->lug_cred);
4128         free(usrp, M_NFSUSERGROUP);
4129 }
4130
4131 /*
4132  * Free up all the allocations related to the name<-->id cache.
4133  * This function should only be called when the nfsuserd daemon isn't
4134  * running, since it doesn't do any locking.
4135  * This function is meant to be used when the nfscommon module is unloaded.
4136  */
4137 APPLESTATIC void
4138 nfsrv_cleanusergroup(void)
4139 {
4140         struct nfsrv_lughash *hp, *hp2;
4141         struct nfsusrgrp *nusrp, *usrp;
4142         int i;
4143
4144         if (nfsuserhash == NULL)
4145                 return;
4146
4147         for (i = 0; i < nfsrv_lughashsize; i++) {
4148                 hp = &nfsuserhash[i];
4149                 TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
4150                         TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
4151                         hp2 = NFSUSERNAMEHASH(usrp->lug_name,
4152                             usrp->lug_namelen);
4153                         TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
4154                         if (usrp->lug_cred != NULL)
4155                                 crfree(usrp->lug_cred);
4156                         free(usrp, M_NFSUSERGROUP);
4157                 }
4158                 hp = &nfsgrouphash[i];
4159                 TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
4160                         TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
4161                         hp2 = NFSGROUPNAMEHASH(usrp->lug_name,
4162                             usrp->lug_namelen);
4163                         TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
4164                         if (usrp->lug_cred != NULL)
4165                                 crfree(usrp->lug_cred);
4166                         free(usrp, M_NFSUSERGROUP);
4167                 }
4168                 mtx_destroy(&nfsuserhash[i].mtx);
4169                 mtx_destroy(&nfsusernamehash[i].mtx);
4170                 mtx_destroy(&nfsgroupnamehash[i].mtx);
4171                 mtx_destroy(&nfsgrouphash[i].mtx);
4172         }
4173         free(nfsuserhash, M_NFSUSERGROUP);
4174         free(nfsusernamehash, M_NFSUSERGROUP);
4175         free(nfsgrouphash, M_NFSUSERGROUP);
4176         free(nfsgroupnamehash, M_NFSUSERGROUP);
4177         free(nfsrv_dnsname, M_NFSSTRING);
4178 }
4179
4180 /*
4181  * This function scans a byte string and checks for UTF-8 compliance.
4182  * It returns 0 if it conforms and NFSERR_INVAL if not.
4183  */
4184 APPLESTATIC int
4185 nfsrv_checkutf8(u_int8_t *cp, int len)
4186 {
4187         u_int32_t val = 0x0;
4188         int cnt = 0, gotd = 0, shift = 0;
4189         u_int8_t byte;
4190         static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
4191         int error = 0;
4192
4193         /*
4194          * Here are what the variables are used for:
4195          * val - the calculated value of a multibyte char, used to check
4196          *       that it was coded with the correct range
4197          * cnt - the number of 10xxxxxx bytes to follow
4198          * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
4199          * shift - lower order bits of range (ie. "val >> shift" should
4200          *       not be 0, in other words, dividing by the lower bound
4201          *       of the range should get a non-zero value)
4202          * byte - used to calculate cnt
4203          */
4204         while (len > 0) {
4205                 if (cnt > 0) {
4206                         /* This handles the 10xxxxxx bytes */
4207                         if ((*cp & 0xc0) != 0x80 ||
4208                             (gotd && (*cp & 0x20))) {
4209                                 error = NFSERR_INVAL;
4210                                 goto out;
4211                         }
4212                         gotd = 0;
4213                         val <<= 6;
4214                         val |= (*cp & 0x3f);
4215                         cnt--;
4216                         if (cnt == 0 && (val >> shift) == 0x0) {
4217                                 error = NFSERR_INVAL;
4218                                 goto out;
4219                         }
4220                 } else if (*cp & 0x80) {
4221                         /* first byte of multi byte char */
4222                         byte = *cp;
4223                         while ((byte & 0x40) && cnt < 6) {
4224                                 cnt++;
4225                                 byte <<= 1;
4226                         }
4227                         if (cnt == 0 || cnt == 6) {
4228                                 error = NFSERR_INVAL;
4229                                 goto out;
4230                         }
4231                         val = (*cp & (0x3f >> cnt));
4232                         shift = utf8_shift[cnt - 1];
4233                         if (cnt == 2 && val == 0xd)
4234                                 /* Check for the 0xd800-0xdfff case */
4235                                 gotd = 1;
4236                 }
4237                 cp++;
4238                 len--;
4239         }
4240         if (cnt > 0)
4241                 error = NFSERR_INVAL;
4242
4243 out:
4244         NFSEXITCODE(error);
4245         return (error);
4246 }
4247
4248 /*
4249  * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
4250  * strings, one with the root path in it and the other with the list of
4251  * locations. The list is in the same format as is found in nfr_refs.
4252  * It is a "," separated list of entries, where each of them is of the
4253  * form <server>:<rootpath>. For example
4254  * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
4255  * The nilp argument is set to 1 for the special case of a null fs_root
4256  * and an empty server list.
4257  * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
4258  * number of xdr bytes parsed in sump.
4259  */
4260 static int
4261 nfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
4262     int *sump, int *nilp)
4263 {
4264         u_int32_t *tl;
4265         u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
4266         int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
4267         struct list {
4268                 SLIST_ENTRY(list) next;
4269                 int len;
4270                 u_char host[1];
4271         } *lsp, *nlsp;
4272         SLIST_HEAD(, list) head;
4273
4274         *fsrootp = NULL;
4275         *srvp = NULL;
4276         *nilp = 0;
4277
4278         /*
4279          * Get the fs_root path and check for the special case of null path
4280          * and 0 length server list.
4281          */
4282         NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4283         len = fxdr_unsigned(int, *tl);
4284         if (len < 0 || len > 10240) {
4285                 error = NFSERR_BADXDR;
4286                 goto nfsmout;
4287         }
4288         if (len == 0) {
4289                 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4290                 if (*tl != 0) {
4291                         error = NFSERR_BADXDR;
4292                         goto nfsmout;
4293                 }
4294                 *nilp = 1;
4295                 *sump = 2 * NFSX_UNSIGNED;
4296                 error = 0;
4297                 goto nfsmout;
4298         }
4299         cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
4300         error = nfsrv_mtostr(nd, cp, len);
4301         if (!error) {
4302                 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4303                 cnt = fxdr_unsigned(int, *tl);
4304                 if (cnt <= 0)
4305                         error = NFSERR_BADXDR;
4306         }
4307         if (error)
4308                 goto nfsmout;
4309
4310         /*
4311          * Now, loop through the location list and make up the srvlist.
4312          */
4313         xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
4314         cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
4315         slen = 1024;
4316         siz = 0;
4317         for (i = 0; i < cnt; i++) {
4318                 SLIST_INIT(&head);
4319                 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4320                 nsrv = fxdr_unsigned(int, *tl);
4321                 if (nsrv <= 0) {
4322                         error = NFSERR_BADXDR;
4323                         goto nfsmout;
4324                 }
4325
4326                 /*
4327                  * Handle the first server by putting it in the srvstr.
4328                  */
4329                 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4330                 len = fxdr_unsigned(int, *tl);
4331                 if (len <= 0 || len > 1024) {
4332                         error = NFSERR_BADXDR;
4333                         goto nfsmout;
4334                 }
4335                 nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
4336                 if (cp3 != cp2) {
4337                         *cp3++ = ',';
4338                         siz++;
4339                 }
4340                 error = nfsrv_mtostr(nd, cp3, len);
4341                 if (error)
4342                         goto nfsmout;
4343                 cp3 += len;
4344                 *cp3++ = ':';
4345                 siz += (len + 1);
4346                 xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
4347                 for (j = 1; j < nsrv; j++) {
4348                         /*
4349                          * Yuck, put them in an slist and process them later.
4350                          */
4351                         NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4352                         len = fxdr_unsigned(int, *tl);
4353                         if (len <= 0 || len > 1024) {
4354                                 error = NFSERR_BADXDR;
4355                                 goto nfsmout;
4356                         }
4357                         lsp = (struct list *)malloc(sizeof (struct list)
4358                             + len, M_TEMP, M_WAITOK);
4359                         error = nfsrv_mtostr(nd, lsp->host, len);
4360                         if (error)
4361                                 goto nfsmout;
4362                         xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
4363                         lsp->len = len;
4364                         SLIST_INSERT_HEAD(&head, lsp, next);
4365                 }
4366
4367                 /*
4368                  * Finally, we can get the path.
4369                  */
4370                 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4371                 len = fxdr_unsigned(int, *tl);
4372                 if (len <= 0 || len > 1024) {
4373                         error = NFSERR_BADXDR;
4374                         goto nfsmout;
4375                 }
4376                 nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
4377                 error = nfsrv_mtostr(nd, cp3, len);
4378                 if (error)
4379                         goto nfsmout;
4380                 xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
4381                 str = cp3;
4382                 stringlen = len;
4383                 cp3 += len;
4384                 siz += len;
4385                 SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
4386                         nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
4387                             &cp2, &cp3, &slen);
4388                         *cp3++ = ',';
4389                         NFSBCOPY(lsp->host, cp3, lsp->len);
4390                         cp3 += lsp->len;
4391                         *cp3++ = ':';
4392                         NFSBCOPY(str, cp3, stringlen);
4393                         cp3 += stringlen;
4394                         *cp3 = '\0';
4395                         siz += (lsp->len + stringlen + 2);
4396                         free(lsp, M_TEMP);
4397                 }
4398         }
4399         *fsrootp = cp;
4400         *srvp = cp2;
4401         *sump = xdrsum;
4402         NFSEXITCODE2(0, nd);
4403         return (0);
4404 nfsmout:
4405         if (cp != NULL)
4406                 free(cp, M_NFSSTRING);
4407         if (cp2 != NULL)
4408                 free(cp2, M_NFSSTRING);
4409         NFSEXITCODE2(error, nd);
4410         return (error);
4411 }
4412
4413 /*
4414  * Make the malloc'd space large enough. This is a pain, but the xdr
4415  * doesn't set an upper bound on the side, so...
4416  */
4417 static void
4418 nfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
4419 {
4420         u_char *cp;
4421         int i;
4422
4423         if (siz <= *slenp)
4424                 return;
4425         cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
4426         NFSBCOPY(*cpp, cp, *slenp);
4427         free(*cpp, M_NFSSTRING);
4428         i = *cpp2 - *cpp;
4429         *cpp = cp;
4430         *cpp2 = cp + i;
4431         *slenp = siz + 1024;
4432 }
4433
4434 /*
4435  * Initialize the reply header data structures.
4436  */
4437 APPLESTATIC void
4438 nfsrvd_rephead(struct nfsrv_descript *nd)
4439 {
4440         mbuf_t mreq;
4441
4442         /*
4443          * If this is a big reply, use a cluster.
4444          */
4445         if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
4446             nfs_bigreply[nd->nd_procnum]) {
4447                 NFSMCLGET(mreq, M_WAITOK);
4448                 nd->nd_mreq = mreq;
4449                 nd->nd_mb = mreq;
4450         } else {
4451                 NFSMGET(mreq);
4452                 nd->nd_mreq = mreq;
4453                 nd->nd_mb = mreq;
4454         }
4455         nd->nd_bpos = NFSMTOD(mreq, caddr_t);
4456         mbuf_setlen(mreq, 0);
4457
4458         if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
4459                 NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
4460 }
4461
4462 /*
4463  * Lock a socket against others.
4464  * Currently used to serialize connect/disconnect attempts.
4465  */
4466 int
4467 newnfs_sndlock(int *flagp)
4468 {
4469         struct timespec ts;
4470
4471         NFSLOCKSOCK();
4472         while (*flagp & NFSR_SNDLOCK) {
4473                 *flagp |= NFSR_WANTSND;
4474                 ts.tv_sec = 0;
4475                 ts.tv_nsec = 0;
4476                 (void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
4477                     PZERO - 1, "nfsndlck", &ts);
4478         }
4479         *flagp |= NFSR_SNDLOCK;
4480         NFSUNLOCKSOCK();
4481         return (0);
4482 }
4483
4484 /*
4485  * Unlock the stream socket for others.
4486  */
4487 void
4488 newnfs_sndunlock(int *flagp)
4489 {
4490
4491         NFSLOCKSOCK();
4492         if ((*flagp & NFSR_SNDLOCK) == 0)
4493                 panic("nfs sndunlock");
4494         *flagp &= ~NFSR_SNDLOCK;
4495         if (*flagp & NFSR_WANTSND) {
4496                 *flagp &= ~NFSR_WANTSND;
4497                 wakeup((caddr_t)flagp);
4498         }
4499         NFSUNLOCKSOCK();
4500 }
4501
4502 APPLESTATIC int
4503 nfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_in *sin,
4504     struct sockaddr_in6 *sin6, sa_family_t *saf, int *isudp)
4505 {
4506         struct in_addr saddr;
4507         uint32_t portnum, *tl;
4508         int i, j, k;
4509         sa_family_t af = AF_UNSPEC;
4510         char addr[64], protocol[5], *cp;
4511         int cantparse = 0, error = 0;
4512         uint16_t portv;
4513
4514         NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4515         i = fxdr_unsigned(int, *tl);
4516         if (i >= 3 && i <= 4) {
4517                 error = nfsrv_mtostr(nd, protocol, i);
4518                 if (error)
4519                         goto nfsmout;
4520                 if (strcmp(protocol, "tcp") == 0) {
4521                         af = AF_INET;
4522                         *isudp = 0;
4523                 } else if (strcmp(protocol, "udp") == 0) {
4524                         af = AF_INET;
4525                         *isudp = 1;
4526                 } else if (strcmp(protocol, "tcp6") == 0) {
4527                         af = AF_INET6;
4528                         *isudp = 0;
4529                 } else if (strcmp(protocol, "udp6") == 0) {
4530                         af = AF_INET6;
4531                         *isudp = 1;
4532                 } else
4533                         cantparse = 1;
4534         } else {
4535                 cantparse = 1;
4536                 if (i > 0) {
4537                         error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
4538                         if (error)
4539                                 goto nfsmout;
4540                 }
4541         }
4542         NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4543         i = fxdr_unsigned(int, *tl);
4544         if (i < 0) {
4545                 error = NFSERR_BADXDR;
4546                 goto nfsmout;
4547         } else if (cantparse == 0 && i >= 11 && i < 64) {
4548                 /*
4549                  * The shortest address is 11chars and the longest is < 64.
4550                  */
4551                 error = nfsrv_mtostr(nd, addr, i);
4552                 if (error)
4553                         goto nfsmout;
4554
4555                 /* Find the port# at the end and extract that. */
4556                 i = strlen(addr);
4557                 k = 0;
4558                 cp = &addr[i - 1];
4559                 /* Count back two '.'s from end to get port# field. */
4560                 for (j = 0; j < i; j++) {
4561                         if (*cp == '.') {
4562                                 k++;
4563                                 if (k == 2)
4564                                         break;
4565                         }
4566                         cp--;
4567                 }
4568                 if (k == 2) {
4569                         /*
4570                          * The NFSv4 port# is appended as .N.N, where N is
4571                          * a decimal # in the range 0-255, just like an inet4
4572                          * address. Cheat and use inet_aton(), which will
4573                          * return a Class A address and then shift the high
4574                          * order 8bits over to convert it to the port#.
4575                          */
4576                         *cp++ = '\0';
4577                         if (inet_aton(cp, &saddr) == 1) {
4578                                 portnum = ntohl(saddr.s_addr);
4579                                 portv = (uint16_t)((portnum >> 16) |
4580                                     (portnum & 0xff));
4581                         } else
4582                                 cantparse = 1;
4583                 } else
4584                         cantparse = 1;
4585                 if (cantparse == 0) {
4586                         if (af == AF_INET) {
4587                                 if (inet_pton(af, addr, &sin->sin_addr) == 1) {
4588                                         sin->sin_len = sizeof(*sin);
4589                                         sin->sin_family = AF_INET;
4590                                         sin->sin_port = htons(portv);
4591                                         *saf = af;
4592                                         return (0);
4593                                 }
4594                         } else {
4595                                 if (inet_pton(af, addr, &sin6->sin6_addr)
4596                                     == 1) {
4597                                         sin6->sin6_len = sizeof(*sin6);
4598                                         sin6->sin6_family = AF_INET6;
4599                                         sin6->sin6_port = htons(portv);
4600                                         *saf = af;
4601                                         return (0);
4602                                 }
4603                         }
4604                 }
4605         } else {
4606                 if (i > 0) {
4607                         error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
4608                         if (error)
4609                                 goto nfsmout;
4610                 }
4611         }
4612         error = EPERM;
4613 nfsmout:
4614         return (error);
4615 }
4616
4617 /*
4618  * Handle an NFSv4.1 Sequence request for the session.
4619  * If reply != NULL, use it to return the cached reply, as required.
4620  * The client gets a cached reply via this call for callbacks, however the
4621  * server gets a cached reply via the nfsv4_seqsess_cachereply() call.
4622  */
4623 int
4624 nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot,
4625     struct nfsslot *slots, struct mbuf **reply, uint16_t maxslot)
4626 {
4627         int error;
4628
4629         error = 0;
4630         if (reply != NULL)
4631                 *reply = NULL;
4632         if (slotid > maxslot)
4633                 return (NFSERR_BADSLOT);
4634         if (seqid == slots[slotid].nfssl_seq) {
4635                 /* A retry. */
4636                 if (slots[slotid].nfssl_inprog != 0)
4637                         error = NFSERR_DELAY;
4638                 else if (slots[slotid].nfssl_reply != NULL) {
4639                         if (reply != NULL) {
4640                                 *reply = slots[slotid].nfssl_reply;
4641                                 slots[slotid].nfssl_reply = NULL;
4642                         }
4643                         slots[slotid].nfssl_inprog = 1;
4644                         error = NFSERR_REPLYFROMCACHE;
4645                 } else
4646                         /* No reply cached, so just do it. */
4647                         slots[slotid].nfssl_inprog = 1;
4648         } else if ((slots[slotid].nfssl_seq + 1) == seqid) {
4649                 if (slots[slotid].nfssl_reply != NULL)
4650                         m_freem(slots[slotid].nfssl_reply);
4651                 slots[slotid].nfssl_reply = NULL;
4652                 slots[slotid].nfssl_inprog = 1;
4653                 slots[slotid].nfssl_seq++;
4654         } else
4655                 error = NFSERR_SEQMISORDERED;
4656         return (error);
4657 }
4658
4659 /*
4660  * Cache this reply for the slot.
4661  * Use the "rep" argument to return the cached reply if repstat is set to
4662  * NFSERR_REPLYFROMCACHE. The client never sets repstat to this value.
4663  */
4664 void
4665 nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, int repstat,
4666    struct mbuf **rep)
4667 {
4668
4669         if (repstat == NFSERR_REPLYFROMCACHE) {
4670                 *rep = slots[slotid].nfssl_reply;
4671                 slots[slotid].nfssl_reply = NULL;
4672         } else {
4673                 if (slots[slotid].nfssl_reply != NULL)
4674                         m_freem(slots[slotid].nfssl_reply);
4675                 slots[slotid].nfssl_reply = *rep;
4676         }
4677         slots[slotid].nfssl_inprog = 0;
4678 }
4679
4680 /*
4681  * Generate the xdr for an NFSv4.1 Sequence Operation.
4682  */
4683 APPLESTATIC void
4684 nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd,
4685     struct nfsclsession *sep, int dont_replycache)
4686 {
4687         uint32_t *tl, slotseq = 0;
4688         int error, maxslot, slotpos;
4689         uint8_t sessionid[NFSX_V4SESSIONID];
4690
4691         error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot, &slotseq,
4692             sessionid);
4693         nd->nd_maxreq = sep->nfsess_maxreq;
4694         nd->nd_maxresp = sep->nfsess_maxresp;
4695
4696         /* Build the Sequence arguments. */
4697         NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
4698         nd->nd_sequence = tl;
4699         bcopy(sessionid, tl, NFSX_V4SESSIONID);
4700         tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
4701         nd->nd_slotseq = tl;
4702         if (error == 0) {
4703                 nd->nd_flag |= ND_HASSLOTID;
4704                 nd->nd_slotid = slotpos;
4705                 *tl++ = txdr_unsigned(slotseq);
4706                 *tl++ = txdr_unsigned(slotpos);
4707                 *tl++ = txdr_unsigned(maxslot);
4708                 if (dont_replycache == 0)
4709                         *tl = newnfs_true;
4710                 else
4711                         *tl = newnfs_false;
4712         } else {
4713                 /*
4714                  * There are two errors and the rest of the session can
4715                  * just be zeros.
4716                  * NFSERR_BADSESSION: This bad session should just generate
4717                  *    the same error again when the RPC is retried.
4718                  * ESTALE: A forced dismount is in progress and will cause the
4719                  *    RPC to fail later.
4720                  */
4721                 *tl++ = 0;
4722                 *tl++ = 0;
4723                 *tl++ = 0;
4724                 *tl = 0;
4725         }
4726         nd->nd_flag |= ND_HASSEQUENCE;
4727 }
4728
4729 int
4730 nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep,
4731     int *slotposp, int *maxslotp, uint32_t *slotseqp, uint8_t *sessionid)
4732 {
4733         int i, maxslot, slotpos;
4734         uint64_t bitval;
4735
4736         /* Find an unused slot. */
4737         slotpos = -1;
4738         maxslot = -1;
4739         mtx_lock(&sep->nfsess_mtx);
4740         do {
4741                 if (nmp != NULL && sep->nfsess_defunct != 0) {
4742                         /* Just return the bad session. */
4743                         bcopy(sep->nfsess_sessionid, sessionid,
4744                             NFSX_V4SESSIONID);
4745                         mtx_unlock(&sep->nfsess_mtx);
4746                         return (NFSERR_BADSESSION);
4747                 }
4748                 bitval = 1;
4749                 for (i = 0; i < sep->nfsess_foreslots; i++) {
4750                         if ((bitval & sep->nfsess_slots) == 0) {
4751                                 slotpos = i;
4752                                 sep->nfsess_slots |= bitval;
4753                                 sep->nfsess_slotseq[i]++;
4754                                 *slotseqp = sep->nfsess_slotseq[i];
4755                                 break;
4756                         }
4757                         bitval <<= 1;
4758                 }
4759                 if (slotpos == -1) {
4760                         /*
4761                          * If a forced dismount is in progress, just return.
4762                          * This RPC attempt will fail when it calls
4763                          * newnfs_request().
4764                          */
4765                         if (nmp != NULL && NFSCL_FORCEDISM(nmp->nm_mountp)) {
4766                                 mtx_unlock(&sep->nfsess_mtx);
4767                                 return (ESTALE);
4768                         }
4769                         /* Wake up once/sec, to check for a forced dismount. */
4770                         (void)mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx,
4771                             PZERO, "nfsclseq", hz);
4772                 }
4773         } while (slotpos == -1);
4774         /* Now, find the highest slot in use. (nfsc_slots is 64bits) */
4775         bitval = 1;
4776         for (i = 0; i < 64; i++) {
4777                 if ((bitval & sep->nfsess_slots) != 0)
4778                         maxslot = i;
4779                 bitval <<= 1;
4780         }
4781         bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID);
4782         mtx_unlock(&sep->nfsess_mtx);
4783         *slotposp = slotpos;
4784         *maxslotp = maxslot;
4785         return (0);
4786 }
4787
4788 /*
4789  * Free a session slot.
4790  */
4791 APPLESTATIC void
4792 nfsv4_freeslot(struct nfsclsession *sep, int slot)
4793 {
4794         uint64_t bitval;
4795
4796         bitval = 1;
4797         if (slot > 0)
4798                 bitval <<= slot;
4799         mtx_lock(&sep->nfsess_mtx);
4800         if ((bitval & sep->nfsess_slots) == 0)
4801                 printf("freeing free slot!!\n");
4802         sep->nfsess_slots &= ~bitval;
4803         wakeup(&sep->nfsess_slots);
4804         mtx_unlock(&sep->nfsess_mtx);
4805 }
4806
4807 /*
4808  * Search for a matching pnfsd DS, based on the nmp arg.
4809  * Return one if found, NULL otherwise.
4810  */
4811 struct nfsdevice *
4812 nfsv4_findmirror(struct nfsmount *nmp)
4813 {
4814         struct nfsdevice *ds;
4815
4816         mtx_assert(NFSDDSMUTEXPTR, MA_OWNED);
4817         /*
4818          * Search the DS server list for a match with nmp.
4819          */
4820         if (nfsrv_devidcnt == 0)
4821                 return (NULL);
4822         TAILQ_FOREACH(ds, &nfsrv_devidhead, nfsdev_list) {
4823                 if (ds->nfsdev_nmp == nmp) {
4824                         NFSCL_DEBUG(4, "nfsv4_findmirror: fnd main ds\n");
4825                         break;
4826                 }
4827         }
4828         return (ds);
4829 }
4830