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