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