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