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