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