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