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