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