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