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