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