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