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