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