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