]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_subs.c
xnu-792.24.17.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_subs.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23 /*
24 * Copyright (c) 1989, 1993
25 * The Regents of the University of California. All rights reserved.
26 *
27 * This code is derived from software contributed to Berkeley by
28 * Rick Macklem at The University of Guelph.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 *
58 * @(#)nfs_subs.c 8.8 (Berkeley) 5/22/95
59 * FreeBSD-Id: nfs_subs.c,v 1.47 1997/11/07 08:53:24 phk Exp $
60 */
61
62 /*
63 * These functions support the macros and help fiddle mbuf chains for
64 * the nfs op functions. They do things like create the rpc header and
65 * copy data between mbuf chains and uio lists.
66 */
67 #include <sys/param.h>
68 #include <sys/proc.h>
69 #include <sys/kauth.h>
70 #include <sys/systm.h>
71 #include <sys/kernel.h>
72 #include <sys/mount_internal.h>
73 #include <sys/vnode_internal.h>
74 #include <sys/kpi_mbuf.h>
75 #include <sys/socket.h>
76 #include <sys/stat.h>
77 #include <sys/malloc.h>
78 #include <sys/syscall.h>
79 #include <sys/sysctl.h>
80 #include <sys/ubc_internal.h>
81 #include <sys/fcntl.h>
82 #include <sys/uio_internal.h>
83 #include <sys/domain.h>
84 #include <libkern/OSAtomic.h>
85
86 #include <sys/vm.h>
87 #include <sys/vmparam.h>
88
89 #include <sys/time.h>
90 #include <kern/clock.h>
91
92 #include <nfs/rpcv2.h>
93 #include <nfs/nfsproto.h>
94 #include <nfs/nfs.h>
95 #include <nfs/nfsnode.h>
96 #include <nfs/xdr_subs.h>
97 #include <nfs/nfsm_subs.h>
98 #include <nfs/nfsmount.h>
99 #include <nfs/nfsrtt.h>
100 #include <nfs/nfs_lock.h>
101
102 #include <miscfs/specfs/specdev.h>
103
104 #include <netinet/in.h>
105 #if ISO
106 #include <netiso/iso.h>
107 #endif
108
109 #include <sys/kdebug.h>
110
111 SYSCTL_DECL(_vfs_generic);
112 SYSCTL_NODE(_vfs_generic, OID_AUTO, nfs, CTLFLAG_RW, 0, "nfs hinge");
113
114 #define FSDBG(A, B, C, D, E) \
115 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_NONE, \
116 (int)(B), (int)(C), (int)(D), (int)(E), 0)
117 #define FSDBG_TOP(A, B, C, D, E) \
118 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_START, \
119 (int)(B), (int)(C), (int)(D), (int)(E), 0)
120 #define FSDBG_BOT(A, B, C, D, E) \
121 KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, (A))) | DBG_FUNC_END, \
122 (int)(B), (int)(C), (int)(D), (int)(E), 0)
123 /*
124 * Data items converted to xdr at startup, since they are constant
125 * This is kinda hokey, but may save a little time doing byte swaps
126 */
127 u_long nfs_xdrneg1;
128 u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
129 rpc_mismatch, rpc_auth_unix, rpc_msgaccepted,
130 rpc_auth_kerb;
131 u_long nfs_prog, nfs_true, nfs_false;
132 __private_extern__ int nfs_mbuf_mlen = 0, nfs_mbuf_mhlen = 0,
133 nfs_mbuf_minclsize = 0, nfs_mbuf_mclbytes = 0;
134
135 /* And other global data */
136 static u_long nfs_xid = 0;
137 u_long nfs_xidwrap = 0; /* to build a (non-wwrapping) 64 bit xid */
138 static enum vtype nv2tov_type[8]= {
139 VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON
140 };
141 enum vtype nv3tov_type[8]= {
142 VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
143 };
144
145 int nfs_mount_type;
146 int nfs_ticks;
147
148 lck_grp_t *nfsd_lck_grp;
149 lck_grp_attr_t *nfsd_lck_grp_attr;
150 lck_attr_t *nfsd_lck_attr;
151 lck_mtx_t *nfsd_mutex;
152
153 lck_grp_attr_t *nfs_slp_group_attr;
154 lck_attr_t *nfs_slp_lock_attr;
155 lck_grp_t *nfs_slp_rwlock_group;
156 lck_grp_t *nfs_slp_mutex_group;
157
158 struct nfs_reqq nfs_reqq;
159 struct nfssvc_sockhead nfssvc_sockhead, nfssvc_deadsockhead;
160 struct nfsd_head nfsd_head;
161 int nfsd_head_flag;
162
163 struct nfsexpfslist nfs_exports;
164 struct nfsexphashhead *nfsexphashtbl;
165 u_long nfsexphash;
166 lck_grp_attr_t *nfs_export_group_attr;
167 lck_attr_t *nfs_export_lock_attr;
168 lck_grp_t *nfs_export_rwlock_group;
169 lck_rw_t nfs_export_rwlock;
170
171 #ifndef NFS_NOSERVER
172 /*
173 * Mapping of old NFS Version 2 RPC numbers to generic numbers.
174 */
175 int nfsv3_procid[NFS_NPROCS] = {
176 NFSPROC_NULL,
177 NFSPROC_GETATTR,
178 NFSPROC_SETATTR,
179 NFSPROC_NOOP,
180 NFSPROC_LOOKUP,
181 NFSPROC_READLINK,
182 NFSPROC_READ,
183 NFSPROC_NOOP,
184 NFSPROC_WRITE,
185 NFSPROC_CREATE,
186 NFSPROC_REMOVE,
187 NFSPROC_RENAME,
188 NFSPROC_LINK,
189 NFSPROC_SYMLINK,
190 NFSPROC_MKDIR,
191 NFSPROC_RMDIR,
192 NFSPROC_READDIR,
193 NFSPROC_FSSTAT,
194 NFSPROC_NOOP,
195 NFSPROC_NOOP,
196 NFSPROC_NOOP,
197 NFSPROC_NOOP,
198 NFSPROC_NOOP
199 };
200
201 #endif /* NFS_NOSERVER */
202 /*
203 * and the reverse mapping from generic to Version 2 procedure numbers
204 */
205 int nfsv2_procid[NFS_NPROCS] = {
206 NFSV2PROC_NULL,
207 NFSV2PROC_GETATTR,
208 NFSV2PROC_SETATTR,
209 NFSV2PROC_LOOKUP,
210 NFSV2PROC_NOOP,
211 NFSV2PROC_READLINK,
212 NFSV2PROC_READ,
213 NFSV2PROC_WRITE,
214 NFSV2PROC_CREATE,
215 NFSV2PROC_MKDIR,
216 NFSV2PROC_SYMLINK,
217 NFSV2PROC_CREATE,
218 NFSV2PROC_REMOVE,
219 NFSV2PROC_RMDIR,
220 NFSV2PROC_RENAME,
221 NFSV2PROC_LINK,
222 NFSV2PROC_READDIR,
223 NFSV2PROC_NOOP,
224 NFSV2PROC_STATFS,
225 NFSV2PROC_NOOP,
226 NFSV2PROC_NOOP,
227 NFSV2PROC_NOOP,
228 NFSV2PROC_NOOP
229 };
230
231 #ifndef NFS_NOSERVER
232 /*
233 * Maps errno values to nfs error numbers.
234 * Use NFSERR_IO as the catch all for ones not specifically defined in
235 * RFC 1094.
236 */
237 static u_char nfsrv_v2errmap[ELAST] = {
238 NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
239 NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
240 NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
241 NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
242 NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
243 NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
244 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
245 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
246 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
247 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
248 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
249 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
250 NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
251 NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
252 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
253 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
254 NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
255 };
256
257 /*
258 * Maps errno values to nfs error numbers.
259 * Although it is not obvious whether or not NFS clients really care if
260 * a returned error value is in the specified list for the procedure, the
261 * safest thing to do is filter them appropriately. For Version 2, the
262 * X/Open XNFS document is the only specification that defines error values
263 * for each RPC (The RFC simply lists all possible error values for all RPCs),
264 * so I have decided to not do this for Version 2.
265 * The first entry is the default error return and the rest are the valid
266 * errors for that RPC in increasing numeric order.
267 */
268 static short nfsv3err_null[] = {
269 0,
270 0,
271 };
272
273 static short nfsv3err_getattr[] = {
274 NFSERR_IO,
275 NFSERR_IO,
276 NFSERR_STALE,
277 NFSERR_BADHANDLE,
278 NFSERR_SERVERFAULT,
279 0,
280 };
281
282 static short nfsv3err_setattr[] = {
283 NFSERR_IO,
284 NFSERR_PERM,
285 NFSERR_IO,
286 NFSERR_ACCES,
287 NFSERR_INVAL,
288 NFSERR_NOSPC,
289 NFSERR_ROFS,
290 NFSERR_DQUOT,
291 NFSERR_STALE,
292 NFSERR_BADHANDLE,
293 NFSERR_NOT_SYNC,
294 NFSERR_SERVERFAULT,
295 0,
296 };
297
298 static short nfsv3err_lookup[] = {
299 NFSERR_IO,
300 NFSERR_NOENT,
301 NFSERR_IO,
302 NFSERR_ACCES,
303 NFSERR_NOTDIR,
304 NFSERR_NAMETOL,
305 NFSERR_STALE,
306 NFSERR_BADHANDLE,
307 NFSERR_SERVERFAULT,
308 0,
309 };
310
311 static short nfsv3err_access[] = {
312 NFSERR_IO,
313 NFSERR_IO,
314 NFSERR_STALE,
315 NFSERR_BADHANDLE,
316 NFSERR_SERVERFAULT,
317 0,
318 };
319
320 static short nfsv3err_readlink[] = {
321 NFSERR_IO,
322 NFSERR_IO,
323 NFSERR_ACCES,
324 NFSERR_INVAL,
325 NFSERR_STALE,
326 NFSERR_BADHANDLE,
327 NFSERR_NOTSUPP,
328 NFSERR_SERVERFAULT,
329 0,
330 };
331
332 static short nfsv3err_read[] = {
333 NFSERR_IO,
334 NFSERR_IO,
335 NFSERR_NXIO,
336 NFSERR_ACCES,
337 NFSERR_INVAL,
338 NFSERR_STALE,
339 NFSERR_BADHANDLE,
340 NFSERR_SERVERFAULT,
341 0,
342 };
343
344 static short nfsv3err_write[] = {
345 NFSERR_IO,
346 NFSERR_IO,
347 NFSERR_ACCES,
348 NFSERR_INVAL,
349 NFSERR_FBIG,
350 NFSERR_NOSPC,
351 NFSERR_ROFS,
352 NFSERR_DQUOT,
353 NFSERR_STALE,
354 NFSERR_BADHANDLE,
355 NFSERR_SERVERFAULT,
356 0,
357 };
358
359 static short nfsv3err_create[] = {
360 NFSERR_IO,
361 NFSERR_IO,
362 NFSERR_ACCES,
363 NFSERR_EXIST,
364 NFSERR_NOTDIR,
365 NFSERR_NOSPC,
366 NFSERR_ROFS,
367 NFSERR_NAMETOL,
368 NFSERR_DQUOT,
369 NFSERR_STALE,
370 NFSERR_BADHANDLE,
371 NFSERR_NOTSUPP,
372 NFSERR_SERVERFAULT,
373 0,
374 };
375
376 static short nfsv3err_mkdir[] = {
377 NFSERR_IO,
378 NFSERR_IO,
379 NFSERR_ACCES,
380 NFSERR_EXIST,
381 NFSERR_NOTDIR,
382 NFSERR_NOSPC,
383 NFSERR_ROFS,
384 NFSERR_NAMETOL,
385 NFSERR_DQUOT,
386 NFSERR_STALE,
387 NFSERR_BADHANDLE,
388 NFSERR_NOTSUPP,
389 NFSERR_SERVERFAULT,
390 0,
391 };
392
393 static short nfsv3err_symlink[] = {
394 NFSERR_IO,
395 NFSERR_IO,
396 NFSERR_ACCES,
397 NFSERR_EXIST,
398 NFSERR_NOTDIR,
399 NFSERR_NOSPC,
400 NFSERR_ROFS,
401 NFSERR_NAMETOL,
402 NFSERR_DQUOT,
403 NFSERR_STALE,
404 NFSERR_BADHANDLE,
405 NFSERR_NOTSUPP,
406 NFSERR_SERVERFAULT,
407 0,
408 };
409
410 static short nfsv3err_mknod[] = {
411 NFSERR_IO,
412 NFSERR_IO,
413 NFSERR_ACCES,
414 NFSERR_EXIST,
415 NFSERR_NOTDIR,
416 NFSERR_NOSPC,
417 NFSERR_ROFS,
418 NFSERR_NAMETOL,
419 NFSERR_DQUOT,
420 NFSERR_STALE,
421 NFSERR_BADHANDLE,
422 NFSERR_NOTSUPP,
423 NFSERR_SERVERFAULT,
424 NFSERR_BADTYPE,
425 0,
426 };
427
428 static short nfsv3err_remove[] = {
429 NFSERR_IO,
430 NFSERR_NOENT,
431 NFSERR_IO,
432 NFSERR_ACCES,
433 NFSERR_NOTDIR,
434 NFSERR_ROFS,
435 NFSERR_NAMETOL,
436 NFSERR_STALE,
437 NFSERR_BADHANDLE,
438 NFSERR_SERVERFAULT,
439 0,
440 };
441
442 static short nfsv3err_rmdir[] = {
443 NFSERR_IO,
444 NFSERR_NOENT,
445 NFSERR_IO,
446 NFSERR_ACCES,
447 NFSERR_EXIST,
448 NFSERR_NOTDIR,
449 NFSERR_INVAL,
450 NFSERR_ROFS,
451 NFSERR_NAMETOL,
452 NFSERR_NOTEMPTY,
453 NFSERR_STALE,
454 NFSERR_BADHANDLE,
455 NFSERR_NOTSUPP,
456 NFSERR_SERVERFAULT,
457 0,
458 };
459
460 static short nfsv3err_rename[] = {
461 NFSERR_IO,
462 NFSERR_NOENT,
463 NFSERR_IO,
464 NFSERR_ACCES,
465 NFSERR_EXIST,
466 NFSERR_XDEV,
467 NFSERR_NOTDIR,
468 NFSERR_ISDIR,
469 NFSERR_INVAL,
470 NFSERR_NOSPC,
471 NFSERR_ROFS,
472 NFSERR_MLINK,
473 NFSERR_NAMETOL,
474 NFSERR_NOTEMPTY,
475 NFSERR_DQUOT,
476 NFSERR_STALE,
477 NFSERR_BADHANDLE,
478 NFSERR_NOTSUPP,
479 NFSERR_SERVERFAULT,
480 0,
481 };
482
483 static short nfsv3err_link[] = {
484 NFSERR_IO,
485 NFSERR_IO,
486 NFSERR_ACCES,
487 NFSERR_EXIST,
488 NFSERR_XDEV,
489 NFSERR_NOTDIR,
490 NFSERR_INVAL,
491 NFSERR_NOSPC,
492 NFSERR_ROFS,
493 NFSERR_MLINK,
494 NFSERR_NAMETOL,
495 NFSERR_DQUOT,
496 NFSERR_STALE,
497 NFSERR_BADHANDLE,
498 NFSERR_NOTSUPP,
499 NFSERR_SERVERFAULT,
500 0,
501 };
502
503 static short nfsv3err_readdir[] = {
504 NFSERR_IO,
505 NFSERR_IO,
506 NFSERR_ACCES,
507 NFSERR_NOTDIR,
508 NFSERR_STALE,
509 NFSERR_BADHANDLE,
510 NFSERR_BAD_COOKIE,
511 NFSERR_TOOSMALL,
512 NFSERR_SERVERFAULT,
513 0,
514 };
515
516 static short nfsv3err_readdirplus[] = {
517 NFSERR_IO,
518 NFSERR_IO,
519 NFSERR_ACCES,
520 NFSERR_NOTDIR,
521 NFSERR_STALE,
522 NFSERR_BADHANDLE,
523 NFSERR_BAD_COOKIE,
524 NFSERR_NOTSUPP,
525 NFSERR_TOOSMALL,
526 NFSERR_SERVERFAULT,
527 0,
528 };
529
530 static short nfsv3err_fsstat[] = {
531 NFSERR_IO,
532 NFSERR_IO,
533 NFSERR_STALE,
534 NFSERR_BADHANDLE,
535 NFSERR_SERVERFAULT,
536 0,
537 };
538
539 static short nfsv3err_fsinfo[] = {
540 NFSERR_STALE,
541 NFSERR_STALE,
542 NFSERR_BADHANDLE,
543 NFSERR_SERVERFAULT,
544 0,
545 };
546
547 static short nfsv3err_pathconf[] = {
548 NFSERR_STALE,
549 NFSERR_STALE,
550 NFSERR_BADHANDLE,
551 NFSERR_SERVERFAULT,
552 0,
553 };
554
555 static short nfsv3err_commit[] = {
556 NFSERR_IO,
557 NFSERR_IO,
558 NFSERR_STALE,
559 NFSERR_BADHANDLE,
560 NFSERR_SERVERFAULT,
561 0,
562 };
563
564 static short *nfsrv_v3errmap[] = {
565 nfsv3err_null,
566 nfsv3err_getattr,
567 nfsv3err_setattr,
568 nfsv3err_lookup,
569 nfsv3err_access,
570 nfsv3err_readlink,
571 nfsv3err_read,
572 nfsv3err_write,
573 nfsv3err_create,
574 nfsv3err_mkdir,
575 nfsv3err_symlink,
576 nfsv3err_mknod,
577 nfsv3err_remove,
578 nfsv3err_rmdir,
579 nfsv3err_rename,
580 nfsv3err_link,
581 nfsv3err_readdir,
582 nfsv3err_readdirplus,
583 nfsv3err_fsstat,
584 nfsv3err_fsinfo,
585 nfsv3err_pathconf,
586 nfsv3err_commit,
587 };
588
589 #endif /* NFS_NOSERVER */
590
591 extern struct nfsrtt nfsrtt;
592 extern struct nfsstats nfsstats;
593 extern nfstype nfsv2_type[9];
594 extern nfstype nfsv3_type[9];
595 extern struct nfsnodehashhead *nfsnodehashtbl;
596 extern u_long nfsnodehash;
597
598
599 LIST_HEAD(nfsnodehashhead, nfsnode);
600
601 /*
602 * Create the header for an rpc request packet
603 * The hsiz is the size of the rest of the nfs request header.
604 * (just used to decide if a cluster is a good idea)
605 */
606 int
607 nfsm_reqh(int hsiz, caddr_t *bposp, mbuf_t *mbp)
608 {
609 int error;
610
611 *mbp = NULL;
612 if (hsiz >= nfs_mbuf_minclsize)
613 error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, mbp);
614 else
615 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, mbp);
616 if (error)
617 return (error);
618 *bposp = mbuf_data(*mbp);
619 return (0);
620 }
621
622 /*
623 * Build the RPC header and fill in the authorization info.
624 * The authorization string argument is only used when the credentials
625 * come from outside of the kernel.
626 * Returns the head of the mbuf list.
627 */
628 int
629 nfsm_rpchead(cr, nmflag, procid, auth_type, auth_len, auth_str, verf_len,
630 verf_str, mrest, mrest_len, mbp, xidp, mreqp)
631 kauth_cred_t cr;
632 int nmflag;
633 int procid;
634 int auth_type;
635 int auth_len;
636 char *auth_str;
637 int verf_len;
638 char *verf_str;
639 mbuf_t mrest;
640 int mrest_len;
641 mbuf_t *mbp;
642 u_long *xidp;
643 mbuf_t *mreqp;
644 {
645 mbuf_t mb;
646 u_long *tl;
647 caddr_t bpos;
648 int i, error, len;
649 mbuf_t mreq, mb2;
650 int siz, grpsiz, authsiz, mlen;
651 struct timeval tv;
652
653 authsiz = nfsm_rndup(auth_len);
654 len = authsiz + 10 * NFSX_UNSIGNED;
655 if (len >= nfs_mbuf_minclsize) {
656 error = mbuf_getpacket(MBUF_WAITOK, &mb);
657 } else {
658 error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &mb);
659 if (!error) {
660 if (len < nfs_mbuf_mhlen)
661 mbuf_align_32(mb, len);
662 else
663 mbuf_align_32(mb, 8 * NFSX_UNSIGNED);
664 }
665 }
666 if (error) {
667 /* unable to allocate packet */
668 /* XXX nfsstat? */
669 return (error);
670 }
671 mreq = mb;
672 bpos = mbuf_data(mb);
673
674 /*
675 * First the RPC header.
676 */
677 nfsm_build(tl, u_long *, 8 * NFSX_UNSIGNED);
678
679 /*
680 * derive initial xid from system time
681 */
682 if (!nfs_xid) {
683 /*
684 * Note: it's OK if this code inits nfs_xid to 0 (for example,
685 * due to a broken clock) because we immediately increment it
686 * and we guarantee to never use xid 0. So, nfs_xid should only
687 * ever be 0 the first time this function is called.
688 */
689 microtime(&tv);
690 nfs_xid = tv.tv_sec << 12;
691 }
692 /*
693 * Skip zero xid if it should ever happen.
694 */
695 if (++nfs_xid == 0) {
696 nfs_xidwrap++;
697 nfs_xid++;
698 }
699
700 *tl++ = *xidp = txdr_unsigned(nfs_xid);
701 *tl++ = rpc_call;
702 *tl++ = rpc_vers;
703 *tl++ = txdr_unsigned(NFS_PROG);
704 if (nmflag & NFSMNT_NFSV3)
705 *tl++ = txdr_unsigned(NFS_VER3);
706 else
707 *tl++ = txdr_unsigned(NFS_VER2);
708 if (nmflag & NFSMNT_NFSV3)
709 *tl++ = txdr_unsigned(procid);
710 else
711 *tl++ = txdr_unsigned(nfsv2_procid[procid]);
712
713 /*
714 * And then the authorization cred.
715 */
716 *tl++ = txdr_unsigned(auth_type);
717 *tl = txdr_unsigned(authsiz);
718 switch (auth_type) {
719 case RPCAUTH_UNIX:
720 nfsm_build(tl, u_long *, auth_len);
721 *tl++ = 0; /* stamp ?? */
722 *tl++ = 0; /* NULL hostname */
723 *tl++ = txdr_unsigned(kauth_cred_getuid(cr));
724 *tl++ = txdr_unsigned(cr->cr_groups[0]);
725 grpsiz = (auth_len >> 2) - 5;
726 *tl++ = txdr_unsigned(grpsiz);
727 for (i = 1; i <= grpsiz; i++)
728 *tl++ = txdr_unsigned(cr->cr_groups[i]);
729 break;
730 case RPCAUTH_KERB4:
731 siz = auth_len;
732 mlen = mbuf_len(mb);
733 while (siz > 0) {
734 if (mbuf_trailingspace(mb) == 0) {
735 mb2 = NULL;
736 if (siz >= nfs_mbuf_minclsize)
737 error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mb2);
738 else
739 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mb2);
740 if (!error)
741 error = mbuf_setnext(mb, mb2);
742 if (error) {
743 mbuf_freem(mreq);
744 return (error);
745 }
746 mb = mb2;
747 mlen = 0;
748 bpos = mbuf_data(mb);
749 }
750 i = min(siz, mbuf_trailingspace(mb));
751 bcopy(auth_str, bpos, i);
752 mlen += i;
753 mbuf_setlen(mb, mlen);
754 auth_str += i;
755 bpos += i;
756 siz -= i;
757 }
758 if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
759 for (i = 0; i < siz; i++)
760 *bpos++ = '\0';
761 mlen += siz;
762 mbuf_setlen(mb, mlen);
763 }
764 break;
765 };
766
767 /*
768 * And the verifier...
769 */
770 nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
771 if (verf_str) {
772 mlen = mbuf_len(mb);
773 *tl++ = txdr_unsigned(RPCAUTH_KERB4);
774 *tl = txdr_unsigned(verf_len);
775 siz = verf_len;
776 while (siz > 0) {
777 if (mbuf_trailingspace(mb) == 0) {
778 mb2 = NULL;
779 if (siz >= nfs_mbuf_minclsize)
780 error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mb2);
781 else
782 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mb2);
783 if (!error)
784 error = mbuf_setnext(mb, mb2);
785 if (error) {
786 mbuf_freem(mreq);
787 return (error);
788 }
789 mb = mb2;
790 mlen = 0;
791 bpos = mbuf_data(mb);
792 }
793 i = min(siz, mbuf_trailingspace(mb));
794 bcopy(verf_str, bpos, i);
795 mlen += i;
796 mbuf_setlen(mb, mlen);
797 verf_str += i;
798 bpos += i;
799 siz -= i;
800 }
801 if ((siz = (nfsm_rndup(verf_len) - verf_len)) > 0) {
802 for (i = 0; i < siz; i++)
803 *bpos++ = '\0';
804 mlen += siz;
805 mbuf_setlen(mb, mlen);
806 }
807 } else {
808 *tl++ = txdr_unsigned(RPCAUTH_NULL);
809 *tl = 0;
810 }
811 error = mbuf_pkthdr_setrcvif(mreq, 0);
812 if (!error)
813 error = mbuf_setnext(mb, mrest);
814 if (error) {
815 mbuf_freem(mreq);
816 return (error);
817 }
818 mbuf_pkthdr_setlen(mreq, authsiz + 10 * NFSX_UNSIGNED + mrest_len);
819 *mbp = mb;
820 *mreqp = mreq;
821 return (0);
822 }
823
824 /*
825 * copies mbuf chain to the uio scatter/gather list
826 */
827 int
828 nfsm_mbuftouio(mrep, uiop, siz, dpos)
829 mbuf_t *mrep;
830 struct uio *uiop;
831 int siz;
832 caddr_t *dpos;
833 {
834 char *mbufcp, *uiocp;
835 int xfer, left, len;
836 mbuf_t mp;
837 long uiosiz, rem;
838 int error = 0;
839
840 mp = *mrep;
841 mbufcp = *dpos;
842 len = (caddr_t)mbuf_data(mp) + mbuf_len(mp) - mbufcp;
843 rem = nfsm_rndup(siz)-siz;
844 while (siz > 0) {
845 if (uiop->uio_iovcnt <= 0 || uiop->uio_iovs.iov32p == NULL)
846 return (EFBIG);
847 // LP64todo - fix this!
848 left = uio_iov_len(uiop);
849 uiocp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
850 if (left > siz)
851 left = siz;
852 uiosiz = left;
853 while (left > 0) {
854 while (len == 0) {
855 mp = mbuf_next(mp);
856 if (mp == NULL)
857 return (EBADRPC);
858 mbufcp = mbuf_data(mp);
859 len = mbuf_len(mp);
860 }
861 xfer = (left > len) ? len : left;
862 if (UIO_SEG_IS_USER_SPACE(uiop->uio_segflg))
863 copyout(mbufcp, CAST_USER_ADDR_T(uiocp), xfer);
864 else
865 bcopy(mbufcp, uiocp, xfer);
866 left -= xfer;
867 len -= xfer;
868 mbufcp += xfer;
869 uiocp += xfer;
870 uiop->uio_offset += xfer;
871 uio_uio_resid_add(uiop, -xfer);
872 }
873 if (uio_iov_len(uiop) <= (size_t)siz) {
874 uiop->uio_iovcnt--;
875 uio_next_iov(uiop);
876 } else {
877 uio_iov_base_add(uiop, uiosiz);
878 uio_iov_len_add(uiop, -uiosiz);
879 }
880 siz -= uiosiz;
881 }
882 *dpos = mbufcp;
883 *mrep = mp;
884 if (rem > 0) {
885 if (len < rem)
886 error = nfs_adv(mrep, dpos, rem, len);
887 else
888 *dpos += rem;
889 }
890 return (error);
891 }
892
893 /*
894 * copies a uio scatter/gather list to an mbuf chain.
895 * NOTE: can ony handle iovcnt == 1
896 */
897 int
898 nfsm_uiotombuf(uiop, mq, siz, bpos)
899 struct uio *uiop;
900 mbuf_t *mq;
901 int siz;
902 caddr_t *bpos;
903 {
904 char *uiocp;
905 mbuf_t mp, mp2;
906 int xfer, left, mlen, mplen;
907 int uiosiz, clflg, rem, error;
908 char *cp;
909
910 if (uiop->uio_iovcnt != 1)
911 panic("nfsm_uiotombuf: iovcnt != 1");
912
913 if (siz > nfs_mbuf_mlen) /* or should it >= MCLBYTES ?? */
914 clflg = 1;
915 else
916 clflg = 0;
917 rem = nfsm_rndup(siz)-siz;
918 mp = mp2 = *mq;
919 mplen = mbuf_len(mp);
920 while (siz > 0) {
921 // LP64todo - fix this!
922 left = uio_iov_len(uiop);
923 uiocp = CAST_DOWN(caddr_t, uio_iov_base(uiop));
924 if (left > siz)
925 left = siz;
926 uiosiz = left;
927 while (left > 0) {
928 mlen = mbuf_trailingspace(mp);
929 if (mlen == 0) {
930 mp = NULL;
931 if (clflg)
932 error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mp);
933 else
934 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mp);
935 if (!error)
936 error = mbuf_setnext(mp2, mp);
937 if (error)
938 return (error);
939 mplen = 0;
940 mp2 = mp;
941 mlen = mbuf_trailingspace(mp);
942 }
943 xfer = (left > mlen) ? mlen : left;
944 if (UIO_SEG_IS_USER_SPACE(uiop->uio_segflg))
945 copyin(CAST_USER_ADDR_T(uiocp), (caddr_t)mbuf_data(mp) + mplen, xfer);
946 else
947 bcopy(uiocp, (caddr_t)mbuf_data(mp) + mplen, xfer);
948 mplen += xfer;
949 mbuf_setlen(mp, mplen);
950 left -= xfer;
951 uiocp += xfer;
952 uiop->uio_offset += xfer;
953 uio_uio_resid_add(uiop, -xfer);
954 }
955 uio_iov_base_add(uiop, uiosiz);
956 uio_iov_len_add(uiop, -uiosiz);
957 siz -= uiosiz;
958 }
959 if (rem > 0) {
960 if (rem > mbuf_trailingspace(mp)) {
961 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mp);
962 if (!error)
963 error = mbuf_setnext(mp2, mp);
964 if (error)
965 return (error);
966 mplen = 0;
967 }
968 cp = (caddr_t)mbuf_data(mp) + mplen;
969 for (left = 0; left < rem; left++)
970 *cp++ = '\0';
971 mplen += rem;
972 mbuf_setlen(mp, mplen);
973 *bpos = cp;
974 } else {
975 *bpos = (caddr_t)mbuf_data(mp) + mplen;
976 }
977 *mq = mp;
978 return (0);
979 }
980
981 /*
982 * Help break down an mbuf chain by setting the first siz bytes contiguous
983 * pointed to by returned val.
984 * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
985 * cases. (The macros use the vars. dpos and dpos2)
986 */
987 int
988 nfsm_disct(mdp, dposp, siz, left, cp2)
989 mbuf_t *mdp;
990 caddr_t *dposp;
991 int siz;
992 int left;
993 caddr_t *cp2;
994 {
995 mbuf_t mp, mp2;
996 int siz2, xfer, error, mp2len;
997 caddr_t p, mp2data;
998
999 mp = *mdp;
1000 while (left == 0) {
1001 *mdp = mp = mbuf_next(mp);
1002 if (mp == NULL)
1003 return (EBADRPC);
1004 left = mbuf_len(mp);
1005 *dposp = mbuf_data(mp);
1006 }
1007 if (left >= siz) {
1008 *cp2 = *dposp;
1009 *dposp += siz;
1010 } else if (mbuf_next(mp) == NULL) {
1011 return (EBADRPC);
1012 } else if (siz > nfs_mbuf_mhlen) {
1013 panic("nfs S too big");
1014 } else {
1015 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &mp2);
1016 if (error)
1017 return (error);
1018 error = mbuf_setnext(mp2, mbuf_next(mp));
1019 if (!error)
1020 error = mbuf_setnext(mp, mp2);
1021 if (error) {
1022 mbuf_free(mp2);
1023 return (error);
1024 }
1025 mbuf_setlen(mp, mbuf_len(mp) - left);
1026 mp = mp2;
1027 *cp2 = p = mbuf_data(mp);
1028 bcopy(*dposp, p, left); /* Copy what was left */
1029 siz2 = siz-left;
1030 p += left;
1031 mp2 = mbuf_next(mp);
1032 mp2data = mbuf_data(mp2);
1033 mp2len = mbuf_len(mp2);
1034 /* Loop around copying up the siz2 bytes */
1035 while (siz2 > 0) {
1036 if (mp2 == NULL)
1037 return (EBADRPC);
1038 xfer = (siz2 > mp2len) ? mp2len : siz2;
1039 if (xfer > 0) {
1040 bcopy(mp2data, p, xfer);
1041 mp2data += xfer;
1042 mp2len -= xfer;
1043 mbuf_setdata(mp2, mp2data, mp2len);
1044 p += xfer;
1045 siz2 -= xfer;
1046 }
1047 if (siz2 > 0) {
1048 mp2 = mbuf_next(mp2);
1049 mp2data = mbuf_data(mp2);
1050 mp2len = mbuf_len(mp2);
1051 }
1052 }
1053 mbuf_setlen(mp, siz);
1054 *mdp = mp2;
1055 *dposp = mp2data;
1056 }
1057 return (0);
1058 }
1059
1060 /*
1061 * Advance the position in the mbuf chain.
1062 */
1063 int
1064 nfs_adv(mdp, dposp, offs, left)
1065 mbuf_t *mdp;
1066 caddr_t *dposp;
1067 int offs;
1068 int left;
1069 {
1070 mbuf_t m;
1071 int s;
1072
1073 m = *mdp;
1074 s = left;
1075 while (s < offs) {
1076 offs -= s;
1077 m = mbuf_next(m);
1078 if (m == NULL)
1079 return (EBADRPC);
1080 s = mbuf_len(m);
1081 }
1082 *mdp = m;
1083 *dposp = (caddr_t)mbuf_data(m) + offs;
1084 return (0);
1085 }
1086
1087 /*
1088 * Copy a string into mbufs for the hard cases...
1089 */
1090 int
1091 nfsm_strtmbuf(mb, bpos, cp, siz)
1092 mbuf_t *mb;
1093 char **bpos;
1094 char *cp;
1095 long siz;
1096 {
1097 mbuf_t m1 = NULL, m2;
1098 long left, xfer, len, tlen, mlen;
1099 u_long *tl;
1100 int putsize, error;
1101
1102 putsize = 1;
1103 m2 = *mb;
1104 left = mbuf_trailingspace(m2);
1105 if (left >= NFSX_UNSIGNED) {
1106 tl = ((u_long *)(*bpos));
1107 *tl++ = txdr_unsigned(siz);
1108 putsize = 0;
1109 left -= NFSX_UNSIGNED;
1110 len = mbuf_len(m2);
1111 len += NFSX_UNSIGNED;
1112 mbuf_setlen(m2, len);
1113 if (left > 0) {
1114 bcopy(cp, (caddr_t) tl, left);
1115 siz -= left;
1116 cp += left;
1117 len += left;
1118 mbuf_setlen(m2, len);
1119 left = 0;
1120 }
1121 }
1122 /* Loop around adding mbufs */
1123 while (siz > 0) {
1124 m1 = NULL;
1125 if (siz > nfs_mbuf_mlen)
1126 error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &m1);
1127 else
1128 error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_DATA, &m1);
1129 if (!error)
1130 error = mbuf_setnext(m2, m1);
1131 if (error)
1132 return (error);
1133 mlen = mbuf_maxlen(m1);
1134 mbuf_setlen(m1, mlen);
1135 m2 = m1;
1136 tl = mbuf_data(m1);
1137 tlen = 0;
1138 if (putsize) {
1139 *tl++ = txdr_unsigned(siz);
1140 mlen -= NFSX_UNSIGNED;
1141 mbuf_setlen(m1, mlen);
1142 tlen = NFSX_UNSIGNED;
1143 putsize = 0;
1144 }
1145 if (siz < mlen) {
1146 len = nfsm_rndup(siz);
1147 xfer = siz;
1148 if (xfer < len)
1149 *(tl+(xfer>>2)) = 0;
1150 } else {
1151 xfer = len = mlen;
1152 }
1153 bcopy(cp, (caddr_t) tl, xfer);
1154 mbuf_setlen(m1, len + tlen);
1155 siz -= xfer;
1156 cp += xfer;
1157 }
1158 *mb = m1;
1159 *bpos = (caddr_t)mbuf_data(m1) + mbuf_len(m1);
1160 return (0);
1161 }
1162
1163 /*
1164 * Called once to initialize data structures...
1165 */
1166 int
1167 nfs_init(struct vfsconf *vfsp)
1168 {
1169 int i;
1170
1171 /*
1172 * Check to see if major data structures haven't bloated.
1173 */
1174 if (sizeof (struct nfsnode) > NFS_NODEALLOC) {
1175 printf("struct nfsnode bloated (> %dbytes)\n", NFS_NODEALLOC);
1176 printf("Try reducing NFS_SMALLFH\n");
1177 }
1178 if (sizeof (struct nfsmount) > NFS_MNTALLOC) {
1179 printf("struct nfsmount bloated (> %dbytes)\n", NFS_MNTALLOC);
1180 printf("Try reducing NFS_MUIDHASHSIZ\n");
1181 }
1182 if (sizeof (struct nfssvc_sock) > NFS_SVCALLOC) {
1183 printf("struct nfssvc_sock bloated (> %dbytes)\n",NFS_SVCALLOC);
1184 printf("Try reducing NFS_UIDHASHSIZ\n");
1185 }
1186 if (sizeof (struct nfsuid) > NFS_UIDALLOC) {
1187 printf("struct nfsuid bloated (> %dbytes)\n",NFS_UIDALLOC);
1188 printf("Try unionizing the nu_nickname and nu_flag fields\n");
1189 }
1190
1191 nfs_mount_type = vfsp->vfc_typenum;
1192 nfsrtt.pos = 0;
1193 rpc_vers = txdr_unsigned(RPC_VER2);
1194 rpc_call = txdr_unsigned(RPC_CALL);
1195 rpc_reply = txdr_unsigned(RPC_REPLY);
1196 rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
1197 rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
1198 rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
1199 rpc_autherr = txdr_unsigned(RPC_AUTHERR);
1200 rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
1201 rpc_auth_kerb = txdr_unsigned(RPCAUTH_KERB4);
1202 nfs_prog = txdr_unsigned(NFS_PROG);
1203 nfs_true = txdr_unsigned(TRUE);
1204 nfs_false = txdr_unsigned(FALSE);
1205 nfs_xdrneg1 = txdr_unsigned(-1);
1206
1207 nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
1208 if (nfs_ticks < 1)
1209 nfs_ticks = 1;
1210 /* Ensure async daemons disabled */
1211 for (i = 0; i < NFS_MAXASYNCDAEMON; i++) {
1212 nfs_iodwant[i] = NULL;
1213 nfs_iodmount[i] = (struct nfsmount *)0;
1214 }
1215 /* init nfsiod mutex */
1216 nfs_iod_lck_grp_attr = lck_grp_attr_alloc_init();
1217 lck_grp_attr_setstat(nfs_iod_lck_grp_attr);
1218 nfs_iod_lck_grp = lck_grp_alloc_init("nfs_iod", nfs_iod_lck_grp_attr);
1219 nfs_iod_lck_attr = lck_attr_alloc_init();
1220 nfs_iod_mutex = lck_mtx_alloc_init(nfs_iod_lck_grp, nfs_iod_lck_attr);
1221
1222 nfs_nbinit(); /* Init the nfsbuf table */
1223 nfs_nhinit(); /* Init the nfsnode table */
1224 nfs_lockinit(); /* Init the nfs lock state */
1225
1226 #ifndef NFS_NOSERVER
1227 /* init nfsd mutex */
1228 nfsd_lck_grp_attr = lck_grp_attr_alloc_init();
1229 lck_grp_attr_setstat(nfsd_lck_grp_attr);
1230 nfsd_lck_grp = lck_grp_alloc_init("nfsd", nfsd_lck_grp_attr);
1231 nfsd_lck_attr = lck_attr_alloc_init();
1232 nfsd_mutex = lck_mtx_alloc_init(nfsd_lck_grp, nfsd_lck_attr);
1233
1234 /* init slp rwlock */
1235 nfs_slp_lock_attr = lck_attr_alloc_init();
1236 nfs_slp_group_attr = lck_grp_attr_alloc_init();
1237 nfs_slp_rwlock_group = lck_grp_alloc_init("nfs-slp-rwlock", nfs_slp_group_attr);
1238 nfs_slp_mutex_group = lck_grp_alloc_init("nfs-slp-mutex", nfs_slp_group_attr);
1239
1240 /* init export data structures */
1241 nfsexphashtbl = hashinit(8, M_TEMP, &nfsexphash);
1242 LIST_INIT(&nfs_exports);
1243 nfs_export_lock_attr = lck_attr_alloc_init();
1244 nfs_export_group_attr = lck_grp_attr_alloc_init();
1245 nfs_export_rwlock_group = lck_grp_alloc_init("nfs-export-rwlock", nfs_export_group_attr);
1246 lck_rw_init(&nfs_export_rwlock, nfs_export_rwlock_group, nfs_export_lock_attr);
1247
1248 lck_mtx_lock(nfsd_mutex);
1249 nfsrv_init(0); /* Init server data structures */
1250 nfsrv_initcache(); /* Init the server request cache */
1251 lck_mtx_unlock(nfsd_mutex);
1252 #endif
1253
1254 /*
1255 * Initialize reply list and start timer
1256 */
1257 TAILQ_INIT(&nfs_reqq);
1258
1259 nfs_timer(0);
1260
1261 vfsp->vfc_refcount++; /* make us non-unloadable */
1262 return (0);
1263 }
1264
1265 /*
1266 * initialize NFS's cache of mbuf constants
1267 */
1268 void
1269 nfs_mbuf_init(void)
1270 {
1271 struct mbuf_stat ms;
1272
1273 mbuf_stats(&ms);
1274 nfs_mbuf_mlen = ms.mlen;
1275 nfs_mbuf_mhlen = ms.mhlen;
1276 nfs_mbuf_minclsize = ms.minclsize;
1277 nfs_mbuf_mclbytes = ms.mclbytes;
1278 }
1279
1280 /*
1281 * Parse the attributes that are in the mbuf list and store them in *nvap.
1282 */
1283 int
1284 nfs_parsefattr(mbuf_t *mdp, caddr_t *dposp, int v3, struct nfs_vattr *nvap)
1285 {
1286 struct nfs_fattr *fp;
1287 long t1;
1288 caddr_t cp2;
1289 int error = 0, rdev;
1290 mbuf_t md;
1291 enum vtype vtype;
1292 u_short vmode;
1293
1294 md = *mdp;
1295 t1 = ((caddr_t)mbuf_data(md) + mbuf_len(md)) - *dposp;
1296 if ((error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2))) {
1297 return (error);
1298 }
1299 fp = (struct nfs_fattr *)cp2;
1300 if (v3) {
1301 vtype = nfsv3tov_type(fp->fa_type);
1302 vmode = fxdr_unsigned(u_short, fp->fa_mode);
1303 rdev = makedev(fxdr_unsigned(int, fp->fa3_rdev.specdata1),
1304 fxdr_unsigned(int, fp->fa3_rdev.specdata2));
1305 } else {
1306 vtype = nfsv2tov_type(fp->fa_type);
1307 vmode = fxdr_unsigned(u_short, fp->fa_mode);
1308 /*
1309 * XXX
1310 *
1311 * The duplicate information returned in fa_type and fa_mode
1312 * is an ambiguity in the NFS version 2 protocol.
1313 *
1314 * VREG should be taken literally as a regular file. If a
1315 * server intents to return some type information differently
1316 * in the upper bits of the mode field (e.g. for sockets, or
1317 * FIFOs), NFSv2 mandates fa_type to be VNON. Anyway, we
1318 * leave the examination of the mode bits even in the VREG
1319 * case to avoid breakage for bogus servers, but we make sure
1320 * that there are actually type bits set in the upper part of
1321 * fa_mode (and failing that, trust the va_type field).
1322 *
1323 * NFSv3 cleared the issue, and requires fa_mode to not
1324 * contain any type information (while also introduing sockets
1325 * and FIFOs for fa_type).
1326 */
1327 if (vtype == VNON || (vtype == VREG && (vmode & S_IFMT) != 0))
1328 vtype = IFTOVT(vmode);
1329 rdev = fxdr_unsigned(long, fp->fa2_rdev);
1330 /*
1331 * Really ugly NFSv2 kludge.
1332 */
1333 if (vtype == VCHR && rdev == (int)0xffffffff)
1334 vtype = VFIFO;
1335 }
1336
1337 nvap->nva_type = vtype;
1338 nvap->nva_mode = (vmode & 07777);
1339 nvap->nva_rdev = (dev_t)rdev;
1340 nvap->nva_nlink = (uint64_t)fxdr_unsigned(u_long, fp->fa_nlink);
1341 nvap->nva_uid = fxdr_unsigned(uid_t, fp->fa_uid);
1342 nvap->nva_gid = fxdr_unsigned(gid_t, fp->fa_gid);
1343 if (v3) {
1344 fxdr_hyper(&fp->fa3_size, &nvap->nva_size);
1345 nvap->nva_blocksize = 16*1024;
1346 fxdr_hyper(&fp->fa3_used, &nvap->nva_bytes);
1347 fxdr_hyper(&fp->fa3_fileid, &nvap->nva_fileid);
1348 fxdr_nfsv3time(&fp->fa3_atime, &nvap->nva_atime);
1349 fxdr_nfsv3time(&fp->fa3_mtime, &nvap->nva_mtime);
1350 fxdr_nfsv3time(&fp->fa3_ctime, &nvap->nva_ctime);
1351 } else {
1352 nvap->nva_size = fxdr_unsigned(u_long, fp->fa2_size);
1353 nvap->nva_blocksize = fxdr_unsigned(long, fp->fa2_blocksize);
1354 nvap->nva_bytes = fxdr_unsigned(long, fp->fa2_blocks) * NFS_FABLKSIZE;
1355 nvap->nva_fileid = (uint64_t)fxdr_unsigned(u_long, fp->fa2_fileid);
1356 fxdr_nfsv2time(&fp->fa2_atime, &nvap->nva_atime);
1357 fxdr_nfsv2time(&fp->fa2_mtime, &nvap->nva_mtime);
1358 fxdr_nfsv2time(&fp->fa2_ctime, &nvap->nva_ctime);
1359 }
1360
1361 return (0);
1362 }
1363
1364 /*
1365 * Load the attribute cache (that lives in the nfsnode entry) with
1366 * the value pointed to by nvap, unless the file type in the attribute
1367 * cache doesn't match the file type in the nvap, in which case log a
1368 * warning and return ESTALE.
1369 *
1370 * If the dontshrink flag is set, then it's not safe to call ubc_setsize()
1371 * to shrink the size of the file.
1372 */
1373 int
1374 nfs_loadattrcache(
1375 struct nfsnode *np,
1376 struct nfs_vattr *nvap,
1377 u_int64_t *xidp,
1378 int dontshrink)
1379 {
1380 mount_t mp;
1381 vnode_t vp;
1382 struct timeval now;
1383 struct nfs_vattr *npnvap;
1384
1385 if (np->n_flag & NINIT) {
1386 vp = NULL;
1387 mp = np->n_mount;
1388 } else {
1389 vp = NFSTOV(np);
1390 mp = vnode_mount(vp);
1391 }
1392
1393 FSDBG_TOP(527, vp, np, *xidp >> 32, *xidp);
1394
1395 if (!VFSTONFS(mp)) {
1396 FSDBG_BOT(527, ENXIO, 1, 0, *xidp);
1397 return (ENXIO);
1398 }
1399
1400 if (*xidp < np->n_xid) {
1401 /*
1402 * We have already updated attributes with a response from
1403 * a later request. The attributes we have here are probably
1404 * stale so we drop them (just return). However, our
1405 * out-of-order receipt could be correct - if the requests were
1406 * processed out of order at the server. Given the uncertainty
1407 * we invalidate our cached attributes. *xidp is zeroed here
1408 * to indicate the attributes were dropped - only getattr
1409 * cares - it needs to retry the rpc.
1410 */
1411 NATTRINVALIDATE(np);
1412 FSDBG_BOT(527, 0, np, np->n_xid, *xidp);
1413 *xidp = 0;
1414 return (0);
1415 }
1416
1417 if (vp && (nvap->nva_type != vnode_vtype(vp))) {
1418 /*
1419 * The filehandle has changed type on us. This can be
1420 * caused by either the server not having unique filehandles
1421 * or because another client has removed the previous
1422 * filehandle and a new object (of a different type)
1423 * has been created with the same filehandle.
1424 *
1425 * We can't simply switch the type on the vnode because
1426 * there may be type-specific fields that need to be
1427 * cleaned up or set up.
1428 *
1429 * So, what should we do with this vnode?
1430 *
1431 * About the best we can do is log a warning and return
1432 * an error. ESTALE is about the closest error, but it
1433 * is a little strange that we come up with this error
1434 * internally instead of simply passing it through from
1435 * the server. Hopefully, the vnode will be reclaimed
1436 * soon so the filehandle can be reincarnated as the new
1437 * object type.
1438 */
1439 printf("nfs loadattrcache vnode changed type, was %d now %d\n",
1440 vnode_vtype(vp), nvap->nva_type);
1441 FSDBG_BOT(527, ESTALE, 3, 0, *xidp);
1442 return (ESTALE);
1443 }
1444
1445 microuptime(&now);
1446 np->n_attrstamp = now.tv_sec;
1447 np->n_xid = *xidp;
1448
1449 npnvap = &np->n_vattr;
1450 nvap->nva_fsid = vfs_statfs(mp)->f_fsid.val[0];
1451 bcopy((caddr_t)nvap, (caddr_t)npnvap, sizeof(*nvap));
1452
1453 if (vp) {
1454 if (nvap->nva_size != np->n_size) {
1455 FSDBG(527, vp, nvap->nva_size, np->n_size,
1456 (nvap->nva_type == VREG) |
1457 (np->n_flag & NMODIFIED ? 6 : 4));
1458 if (nvap->nva_type == VREG) {
1459 u_quad_t orig_size = np->n_size;
1460 if (np->n_flag & NMODIFIED) {
1461 if (nvap->nva_size < np->n_size)
1462 nvap->nva_size = np->n_size;
1463 else
1464 np->n_size = nvap->nva_size;
1465 } else
1466 np->n_size = nvap->nva_size;
1467 if (!UBCINFOEXISTS(vp) ||
1468 (dontshrink && np->n_size < (u_quad_t)ubc_getsize(vp))) {
1469 nvap->nva_size = np->n_size = orig_size;
1470 NATTRINVALIDATE(np);
1471 } else {
1472 ubc_setsize(vp, (off_t)np->n_size); /* XXX */
1473 }
1474 } else
1475 np->n_size = nvap->nva_size;
1476 }
1477 } else {
1478 np->n_size = nvap->nva_size;
1479 }
1480
1481 if (np->n_flag & NCHG) {
1482 if (np->n_flag & NACC)
1483 nvap->nva_atime = np->n_atim;
1484 if (np->n_flag & NUPD)
1485 nvap->nva_mtime = np->n_mtim;
1486 }
1487
1488 FSDBG_BOT(527, 0, np, 0, *xidp);
1489 return (0);
1490 }
1491
1492 /*
1493 * Calculate the attribute timeout based on
1494 * how recently the file has been modified.
1495 */
1496 int
1497 nfs_attrcachetimeout(vnode_t vp)
1498 {
1499 struct nfsnode *np = VTONFS(vp);
1500 struct nfsmount *nmp;
1501 struct timeval now;
1502 int isdir, timeo;
1503
1504 if (!(nmp = VFSTONFS(vnode_mount(vp))))
1505 return (0);
1506
1507 isdir = vnode_isdir(vp);
1508
1509 if ((np)->n_flag & NMODIFIED)
1510 timeo = isdir ? nmp->nm_acdirmin : nmp->nm_acregmin;
1511 else {
1512 /* Note that if the client and server clocks are way out of sync, */
1513 /* timeout will probably get clamped to a min or max value */
1514 microtime(&now);
1515 timeo = (now.tv_sec - (np)->n_mtime.tv_sec) / 10;
1516 if (isdir) {
1517 if (timeo < nmp->nm_acdirmin)
1518 timeo = nmp->nm_acdirmin;
1519 else if (timeo > nmp->nm_acdirmax)
1520 timeo = nmp->nm_acdirmax;
1521 } else {
1522 if (timeo < nmp->nm_acregmin)
1523 timeo = nmp->nm_acregmin;
1524 else if (timeo > nmp->nm_acregmax)
1525 timeo = nmp->nm_acregmax;
1526 }
1527 }
1528
1529 return (timeo);
1530 }
1531
1532 /*
1533 * Check the time stamp
1534 * If the cache is valid, copy contents to *nvaper and return 0
1535 * otherwise return an error
1536 */
1537 int
1538 nfs_getattrcache(vp, nvaper)
1539 vnode_t vp;
1540 struct nfs_vattr *nvaper;
1541 {
1542 struct nfsnode *np = VTONFS(vp);
1543 struct nfs_vattr *nvap;
1544 struct timeval nowup;
1545 int32_t timeo;
1546
1547 if (!NATTRVALID(np)) {
1548 FSDBG(528, vp, 0, 0, 0);
1549 OSAddAtomic(1, (SInt32*)&nfsstats.attrcache_misses);
1550 return (ENOENT);
1551 }
1552
1553 timeo = nfs_attrcachetimeout(vp);
1554
1555 microuptime(&nowup);
1556 if ((nowup.tv_sec - np->n_attrstamp) >= timeo) {
1557 FSDBG(528, vp, 0, 0, 1);
1558 OSAddAtomic(1, (SInt32*)&nfsstats.attrcache_misses);
1559 return (ENOENT);
1560 }
1561 FSDBG(528, vp, 0, 0, 2);
1562 OSAddAtomic(1, (SInt32*)&nfsstats.attrcache_hits);
1563 nvap = &np->n_vattr;
1564
1565 if (nvap->nva_size != np->n_size) {
1566 FSDBG(528, vp, nvap->nva_size, np->n_size,
1567 (nvap->nva_type == VREG) |
1568 (np->n_flag & NMODIFIED ? 6 : 4));
1569 if (nvap->nva_type == VREG) {
1570 if (np->n_flag & NMODIFIED) {
1571 if (nvap->nva_size < np->n_size)
1572 nvap->nva_size = np->n_size;
1573 else
1574 np->n_size = nvap->nva_size;
1575 } else
1576 np->n_size = nvap->nva_size;
1577 ubc_setsize(vp, (off_t)np->n_size); /* XXX */
1578 } else
1579 np->n_size = nvap->nva_size;
1580 }
1581
1582 bcopy((caddr_t)nvap, (caddr_t)nvaper, sizeof(struct nfs_vattr));
1583 if (np->n_flag & NCHG) {
1584 if (np->n_flag & NACC)
1585 nvaper->nva_atime = np->n_atim;
1586 if (np->n_flag & NUPD)
1587 nvaper->nva_mtime = np->n_mtim;
1588 }
1589 return (0);
1590 }
1591
1592 #ifndef NFS_NOSERVER
1593 /*
1594 * Extract a lookup path from the given mbufs and store it in
1595 * a newly allocated buffer saved in the given nameidata structure.
1596 * exptected string length given as *lenp and final string length
1597 * (after any WebNFS processing) is returned in *lenp.
1598 */
1599 int
1600 nfsm_path_mbuftond(
1601 mbuf_t *mdp,
1602 caddr_t *dposp,
1603 __unused int v3,
1604 __unused int pubflag,
1605 int* lenp,
1606 struct nameidata *ndp)
1607 {
1608 int i, len, len2, rem, error = 0;
1609 mbuf_t md;
1610 char *fromcp, *tocp;
1611 struct componentname *cnp = &ndp->ni_cnd;
1612 /* XXX Revisit when enabling WebNFS */
1613 #ifdef WEBNFS_ENABLED
1614 int webcnt = 0, digitcnt = 0;
1615 char hexdigits[2];
1616 #endif
1617
1618 len = *lenp;
1619 if (len > (MAXPATHLEN - 1))
1620 return (ENAMETOOLONG);
1621
1622 /*
1623 * Get a buffer for the name to be translated, and copy the
1624 * name into the buffer.
1625 */
1626 MALLOC_ZONE(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
1627 if (!cnp->cn_pnbuf)
1628 return (ENOMEM);
1629 cnp->cn_pnlen = MAXPATHLEN;
1630 cnp->cn_flags |= HASBUF;
1631
1632 /*
1633 * Copy the name from the mbuf list to the string
1634 *
1635 * Along the way, take note of any WebNFS characters
1636 * and convert any % escapes.
1637 */
1638 fromcp = *dposp;
1639 tocp = cnp->cn_pnbuf;
1640 md = *mdp;
1641 rem = (caddr_t)mbuf_data(md) + mbuf_len(md) - fromcp;
1642 for (i = 1; i <= len; i++) {
1643 while (rem == 0) {
1644 md = mbuf_next(md);
1645 if (md == NULL) {
1646 error = EBADRPC;
1647 goto out;
1648 }
1649 fromcp = mbuf_data(md);
1650 rem = mbuf_len(md);
1651 }
1652 /* XXX Revisit when enabling WebNFS */
1653 #ifdef WEBNFS_ENABLED
1654 if (pubflag) {
1655 if ((i == 1) && ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START)) {
1656 switch ((unsigned char)*fromcp) {
1657 case WEBNFS_NATIVE_CHAR:
1658 /*
1659 * 'Native' path for us is the same
1660 * as a path according to the NFS spec,
1661 * just skip the escape char.
1662 */
1663 webcnt++;
1664 fromcp++;
1665 rem--;
1666 /* next iteration of for loop */
1667 continue;
1668 /*
1669 * More may be added in the future, range 0x80-0xff.
1670 * Don't currently support security query lookup (0x81).
1671 */
1672 default:
1673 error = EIO;
1674 goto out;
1675 }
1676 }
1677 if (digitcnt) {
1678 /* We're expecting hex digits */
1679 if (!ISHEX(*fromcp)) {
1680 error = ENOENT;
1681 goto out;
1682 }
1683 digitcnt--;
1684 hexdigits[digitcnt ? 0 : 1] = *fromcp++;
1685 if (!digitcnt)
1686 *tocp++ = HEXSTRTOI(hexdigits);
1687 rem--;
1688 /* next iteration of for loop */
1689 continue;
1690 } else if (*fromcp == WEBNFS_ESC_CHAR) {
1691 /*
1692 * We can't really look at the next couple
1693 * bytes here safely/easily, so we note that
1694 * the next two characters should be hex
1695 * digits and later save them in hexdigits[].
1696 * When we've got both, we'll convert it.
1697 */
1698 digitcnt = 2;
1699 webcnt += 2;
1700 fromcp++;
1701 rem--;
1702 /* next iteration of for loop */
1703 continue;
1704 }
1705 }
1706 if (*fromcp == '\0' || (!pubflag && *fromcp == '/'))
1707 #else
1708 if (*fromcp == '\0' || *fromcp == '/')
1709 #endif
1710 {
1711 error = EACCES;
1712 goto out;
1713 }
1714 *tocp++ = *fromcp++;
1715 rem--;
1716 }
1717 *tocp = '\0';
1718 *mdp = md;
1719 *dposp = fromcp;
1720 len2 = nfsm_rndup(len)-len;
1721 if (len2 > 0) {
1722 if (rem >= len2)
1723 *dposp += len2;
1724 else if ((error = nfs_adv(mdp, dposp, len2, rem)) != 0)
1725 goto out;
1726 }
1727
1728 /* XXX Revisit when enabling WebNFS */
1729 #ifdef WEBNFS_ENABLED
1730 if (pubflag) {
1731 if (digitcnt) {
1732 /* The string ended in the middle of an escape! */
1733 error = ENOENT;
1734 goto out;
1735 }
1736 len -= webcnt;
1737 }
1738 #endif
1739
1740 out:
1741 if (error) {
1742 if (cnp->cn_pnbuf)
1743 FREE_ZONE(cnp->cn_pnbuf, MAXPATHLEN, M_NAMEI);
1744 cnp->cn_flags &= ~HASBUF;
1745 } else {
1746 ndp->ni_pathlen = len;
1747 *lenp = len;
1748 }
1749 return (error);
1750 }
1751
1752 /*
1753 * Set up nameidata for a lookup() call and do it.
1754 *
1755 * If pubflag is set, this call is done for a lookup operation on the
1756 * public filehandle. In that case we allow crossing mountpoints and
1757 * absolute pathnames. However, the caller is expected to check that
1758 * the lookup result is within the public fs, and deny access if
1759 * it is not.
1760 */
1761 int
1762 nfs_namei(
1763 struct nfsrv_descript *nfsd,
1764 struct vfs_context *ctx,
1765 struct nameidata *ndp,
1766 struct nfs_filehandle *nfhp,
1767 mbuf_t nam,
1768 int pubflag,
1769 vnode_t *retdirp,
1770 struct nfs_export **nxp,
1771 struct nfs_export_options **nxop)
1772 {
1773 /* XXX Revisit when enabling WebNFS */
1774 #ifdef WEBNFS_ENABLED
1775 char *cp;
1776 uio_t auio;
1777 char uio_buf[ UIO_SIZEOF(1) ];
1778 int linklen, olen = ndp->ni_pathlen;
1779 #endif
1780 vnode_t dp;
1781 int error;
1782 struct componentname *cnp = &ndp->ni_cnd;
1783 char *tmppn;
1784
1785 *retdirp = NULL;
1786
1787 /*
1788 * Extract and set starting directory.
1789 */
1790 error = nfsrv_fhtovp(nfhp, nam, pubflag, &dp, nxp, nxop);
1791 if (error)
1792 goto out;
1793 error = nfsrv_credcheck(nfsd, *nxp, *nxop);
1794 if (error || (vnode_vtype(dp) != VDIR)) {
1795 vnode_put(dp);
1796 error = ENOTDIR;
1797 goto out;
1798 }
1799
1800 ctx->vc_ucred = nfsd->nd_cr;
1801 ndp->ni_cnd.cn_context = ctx;
1802
1803 if (*nxop && ((*nxop)->nxo_flags & NX_READONLY))
1804 cnp->cn_flags |= RDONLY;
1805
1806 *retdirp = dp;
1807
1808 /* XXX Revisit when enabling WebNFS */
1809 #ifdef WEBNFS_ENABLED
1810 if (pubflag) {
1811 ndp->ni_rootdir = rootvnode;
1812 ndp->ni_loopcnt = 0;
1813 if (cnp->cn_pnbuf[0] == '/') {
1814 vnode_put(dp);
1815 dp = rootvnode;
1816 error = vnode_get(dp);
1817 if (error) {
1818 *retdirp = NULL;
1819 goto out;
1820 }
1821 }
1822 } else {
1823 cnp->cn_flags |= NOCROSSMOUNT;
1824 }
1825 #else
1826 cnp->cn_flags |= NOCROSSMOUNT;
1827 #endif
1828
1829 ndp->ni_usedvp = dp;
1830
1831 for (;;) {
1832 cnp->cn_nameptr = cnp->cn_pnbuf;
1833 ndp->ni_startdir = dp;
1834 /*
1835 * And call lookup() to do the real work
1836 */
1837 error = lookup(ndp);
1838 if (error)
1839 break;
1840 /*
1841 * Check for encountering a symbolic link
1842 */
1843 if ((cnp->cn_flags & ISSYMLINK) == 0) {
1844 return (0);
1845 } else {
1846 if ((cnp->cn_flags & FSNODELOCKHELD)) {
1847 cnp->cn_flags &= ~FSNODELOCKHELD;
1848 unlock_fsnode(ndp->ni_dvp, NULL);
1849 }
1850 /* XXX Revisit when enabling WebNFS */
1851 #ifdef WEBNFS_ENABLED
1852 if (!pubflag) {
1853 #endif
1854 if (cnp->cn_flags & (LOCKPARENT | WANTPARENT))
1855 vnode_put(ndp->ni_dvp);
1856 if (ndp->ni_vp) {
1857 vnode_put(ndp->ni_vp);
1858 ndp->ni_vp = NULL;
1859 }
1860 error = EINVAL;
1861 break;
1862 /* XXX Revisit when enabling WebNFS */
1863 #ifdef WEBNFS_ENABLED
1864 }
1865
1866 if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
1867 vnode_put(ndp->ni_vp);
1868 ndp->ni_vp = NULL;
1869 error = ELOOP;
1870 break;
1871 }
1872 /* XXX assert(olen <= MAXPATHLEN - 1); */
1873 if (ndp->ni_pathlen > 1) {
1874 MALLOC_ZONE(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
1875 if (!cp) {
1876 vnode_put(ndp->ni_vp);
1877 ndp->ni_vp = NULL;
1878 error = ENOMEM;
1879 break;
1880 }
1881 } else {
1882 cp = cnp->cn_pnbuf;
1883 }
1884 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
1885 &uio_buf[0], sizeof(uio_buf));
1886 if (!auio) {
1887 vnode_put(ndp->ni_vp);
1888 ndp->ni_vp = NULL;
1889 if (ndp->ni_pathlen > 1)
1890 FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
1891 error = ENOMEM;
1892 break;
1893 }
1894 uio_addiov(auio, CAST_USER_ADDR_T(cp), MAXPATHLEN);
1895 error = VNOP_READLINK(ndp->ni_vp, auio, cnp->cn_context);
1896 if (error) {
1897 badlink:
1898 vnode_put(ndp->ni_vp);
1899 ndp->ni_vp = NULL;
1900 if (ndp->ni_pathlen > 1)
1901 FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
1902 break;
1903 }
1904 linklen = MAXPATHLEN - uio_resid(auio);
1905 if (linklen == 0) {
1906 error = ENOENT;
1907 goto badlink;
1908 }
1909 if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
1910 error = ENAMETOOLONG;
1911 goto badlink;
1912 }
1913 if (ndp->ni_pathlen > 1) {
1914 long len = cnp->cn_pnlen;
1915 tmppn = cnp->cn_pnbuf;
1916 cnp->cn_pnbuf = cp;
1917 cnp->cn_pnlen = olen + 1;
1918 bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
1919 FREE_ZONE(tmppn, len, M_NAMEI);
1920 } else
1921 cnp->cn_pnbuf[linklen] = '\0';
1922 ndp->ni_pathlen += linklen;
1923
1924 vnode_put(ndp->ni_vp);
1925 dp = ndp->ni_dvp;
1926 ndp->ni_dvp = NULL;
1927
1928 /*
1929 * Check if root directory should replace current directory.
1930 */
1931 if (cnp->cn_pnbuf[0] == '/') {
1932 vnode_put(dp);
1933 dp = ndp->ni_rootdir;
1934 error = vnode_get(dp);
1935 if (error)
1936 break;
1937 }
1938 #endif
1939 }
1940 }
1941 out:
1942 tmppn = cnp->cn_pnbuf;
1943 cnp->cn_pnbuf = NULL;
1944 cnp->cn_flags &= ~HASBUF;
1945 FREE_ZONE(tmppn, cnp->cn_pnlen, M_NAMEI);
1946
1947 return (error);
1948 }
1949
1950 /*
1951 * A fiddled version of m_adj() that ensures null fill to a long
1952 * boundary and only trims off the back end
1953 */
1954 void
1955 nfsm_adj(mp, len, nul)
1956 mbuf_t mp;
1957 int len;
1958 int nul;
1959 {
1960 mbuf_t m, mnext;
1961 int count, i, mlen;
1962 char *cp;
1963
1964 /*
1965 * Trim from tail. Scan the mbuf chain,
1966 * calculating its length and finding the last mbuf.
1967 * If the adjustment only affects this mbuf, then just
1968 * adjust and return. Otherwise, rescan and truncate
1969 * after the remaining size.
1970 */
1971 count = 0;
1972 m = mp;
1973 for (;;) {
1974 mlen = mbuf_len(m);
1975 count += mlen;
1976 mnext = mbuf_next(m);
1977 if (mnext == NULL)
1978 break;
1979 m = mnext;
1980 }
1981 if (mlen > len) {
1982 mlen -= len;
1983 mbuf_setlen(m, mlen);
1984 if (nul > 0) {
1985 cp = (caddr_t)mbuf_data(m) + mlen - nul;
1986 for (i = 0; i < nul; i++)
1987 *cp++ = '\0';
1988 }
1989 return;
1990 }
1991 count -= len;
1992 if (count < 0)
1993 count = 0;
1994 /*
1995 * Correct length for chain is "count".
1996 * Find the mbuf with last data, adjust its length,
1997 * and toss data from remaining mbufs on chain.
1998 */
1999 for (m = mp; m; m = mbuf_next(m)) {
2000 mlen = mbuf_len(m);
2001 if (mlen >= count) {
2002 mlen = count;
2003 mbuf_setlen(m, count);
2004 if (nul > 0) {
2005 cp = (caddr_t)mbuf_data(m) + mlen - nul;
2006 for (i = 0; i < nul; i++)
2007 *cp++ = '\0';
2008 }
2009 break;
2010 }
2011 count -= mlen;
2012 }
2013 for (m = mbuf_next(m); m; m = mbuf_next(m))
2014 mbuf_setlen(m, 0);
2015 }
2016
2017 /*
2018 * Make these functions instead of macros, so that the kernel text size
2019 * doesn't get too big...
2020 */
2021 void
2022 nfsm_srvwcc(nfsd, before_ret, before_vap, after_ret, after_vap, mbp, bposp)
2023 struct nfsrv_descript *nfsd;
2024 int before_ret;
2025 struct vnode_attr *before_vap;
2026 int after_ret;
2027 struct vnode_attr *after_vap;
2028 mbuf_t *mbp;
2029 char **bposp;
2030 {
2031 mbuf_t mb = *mbp, mb2;
2032 char *bpos = *bposp;
2033 u_long *tl;
2034
2035 if (before_ret) {
2036 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
2037 *tl = nfs_false;
2038 } else {
2039 nfsm_build(tl, u_long *, 7 * NFSX_UNSIGNED);
2040 *tl++ = nfs_true;
2041 txdr_hyper(&(before_vap->va_data_size), tl);
2042 tl += 2;
2043 txdr_nfsv3time(&(before_vap->va_modify_time), tl);
2044 tl += 2;
2045 txdr_nfsv3time(&(before_vap->va_change_time), tl);
2046 }
2047 *bposp = bpos;
2048 *mbp = mb;
2049 nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
2050 }
2051
2052 void
2053 nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp)
2054 struct nfsrv_descript *nfsd;
2055 int after_ret;
2056 struct vnode_attr *after_vap;
2057 mbuf_t *mbp;
2058 char **bposp;
2059 {
2060 mbuf_t mb = *mbp, mb2;
2061 char *bpos = *bposp;
2062 u_long *tl;
2063 struct nfs_fattr *fp;
2064
2065 if (after_ret) {
2066 nfsm_build(tl, u_long *, NFSX_UNSIGNED);
2067 *tl = nfs_false;
2068 } else {
2069 nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3FATTR);
2070 *tl++ = nfs_true;
2071 fp = (struct nfs_fattr *)tl;
2072 nfsm_srvfattr(nfsd, after_vap, fp);
2073 }
2074 *mbp = mb;
2075 *bposp = bpos;
2076 }
2077
2078 void
2079 nfsm_srvfattr(nfsd, vap, fp)
2080 struct nfsrv_descript *nfsd;
2081 struct vnode_attr *vap;
2082 struct nfs_fattr *fp;
2083 {
2084
2085 // XXX Should we assert here that all fields are supported?
2086
2087 fp->fa_nlink = txdr_unsigned(vap->va_nlink);
2088 fp->fa_uid = txdr_unsigned(vap->va_uid);
2089 fp->fa_gid = txdr_unsigned(vap->va_gid);
2090 if (nfsd->nd_flag & ND_NFSV3) {
2091 fp->fa_type = vtonfsv3_type(vap->va_type);
2092 fp->fa_mode = vtonfsv3_mode(vap->va_mode);
2093 txdr_hyper(&vap->va_data_size, &fp->fa3_size);
2094 txdr_hyper(&vap->va_data_alloc, &fp->fa3_used);
2095 fp->fa3_rdev.specdata1 = txdr_unsigned(major(vap->va_rdev));
2096 fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev));
2097 fp->fa3_fsid.nfsuquad[0] = 0;
2098 fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
2099 txdr_hyper(&vap->va_fileid, &fp->fa3_fileid);
2100 txdr_nfsv3time(&vap->va_access_time, &fp->fa3_atime);
2101 txdr_nfsv3time(&vap->va_modify_time, &fp->fa3_mtime);
2102 txdr_nfsv3time(&vap->va_change_time, &fp->fa3_ctime);
2103 } else {
2104 fp->fa_type = vtonfsv2_type(vap->va_type);
2105 fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
2106 fp->fa2_size = txdr_unsigned(vap->va_data_size);
2107 fp->fa2_blocksize = txdr_unsigned(vap->va_iosize);
2108 if (vap->va_type == VFIFO)
2109 fp->fa2_rdev = 0xffffffff;
2110 else
2111 fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
2112 fp->fa2_blocks = txdr_unsigned(vap->va_data_alloc / NFS_FABLKSIZE);
2113 fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
2114 fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
2115 txdr_nfsv2time(&vap->va_access_time, &fp->fa2_atime);
2116 txdr_nfsv2time(&vap->va_modify_time, &fp->fa2_mtime);
2117 txdr_nfsv2time(&vap->va_change_time, &fp->fa2_ctime);
2118 }
2119 }
2120
2121 /*
2122 * Build hash lists of net addresses and hang them off the NFS export.
2123 * Called by nfsrv_export() to set up the lists of export addresses.
2124 */
2125 static int
2126 nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
2127 {
2128 struct nfs_export_net_args nxna;
2129 struct nfs_netopt *no;
2130 struct radix_node_head *rnh;
2131 struct radix_node *rn;
2132 struct sockaddr *saddr, *smask;
2133 struct domain *dom;
2134 int i, error;
2135 unsigned int net;
2136 user_addr_t uaddr;
2137 kauth_cred_t cred;
2138 struct ucred temp_cred;
2139
2140 uaddr = unxa->nxa_nets;
2141 for (net = 0; net < unxa->nxa_netcount; net++, uaddr += sizeof(nxna)) {
2142 error = copyin(uaddr, &nxna, sizeof(nxna));
2143 if (error)
2144 return (error);
2145
2146 if (nxna.nxna_flags & (NX_MAPROOT|NX_MAPALL)) {
2147 bzero(&temp_cred, sizeof(temp_cred));
2148 temp_cred.cr_uid = nxna.nxna_cred.cr_uid;
2149 temp_cred.cr_ngroups = nxna.nxna_cred.cr_ngroups;
2150 for (i=0; i < nxna.nxna_cred.cr_ngroups && i < NGROUPS; i++)
2151 temp_cred.cr_groups[i] = nxna.nxna_cred.cr_groups[i];
2152
2153 cred = kauth_cred_create(&temp_cred);
2154 if (!cred)
2155 return (ENOMEM);
2156 } else {
2157 cred = NULL;
2158 }
2159
2160 if (nxna.nxna_addr.ss_len == 0) {
2161 /* No address means this is a default/world export */
2162 if (nx->nx_flags & NX_DEFAULTEXPORT)
2163 return (EEXIST);
2164 nx->nx_flags |= NX_DEFAULTEXPORT;
2165 nx->nx_defopt.nxo_flags = nxna.nxna_flags;
2166 nx->nx_defopt.nxo_cred = cred;
2167 nx->nx_expcnt++;
2168 continue;
2169 }
2170
2171 i = sizeof(struct nfs_netopt);
2172 i += nxna.nxna_addr.ss_len + nxna.nxna_mask.ss_len;
2173 MALLOC(no, struct nfs_netopt *, i, M_NETADDR, M_WAITOK);
2174 if (!no)
2175 return (ENOMEM);
2176 bzero(no, sizeof(struct nfs_netopt));
2177 no->no_opt.nxo_flags = nxna.nxna_flags;
2178 no->no_opt.nxo_cred = cred;
2179
2180 saddr = (struct sockaddr *)(no + 1);
2181 bcopy(&nxna.nxna_addr, saddr, nxna.nxna_addr.ss_len);
2182 if (nxna.nxna_mask.ss_len) {
2183 smask = (struct sockaddr *)((caddr_t)saddr + nxna.nxna_addr.ss_len);
2184 bcopy(&nxna.nxna_mask, smask, nxna.nxna_mask.ss_len);
2185 } else {
2186 smask = NULL;
2187 }
2188 i = saddr->sa_family;
2189 if ((rnh = nx->nx_rtable[i]) == 0) {
2190 /*
2191 * Seems silly to initialize every AF when most are not
2192 * used, do so on demand here
2193 */
2194 for (dom = domains; dom; dom = dom->dom_next)
2195 if (dom->dom_family == i && dom->dom_rtattach) {
2196 dom->dom_rtattach((void **)&nx->nx_rtable[i],
2197 dom->dom_rtoffset);
2198 break;
2199 }
2200 if ((rnh = nx->nx_rtable[i]) == 0) {
2201 kauth_cred_rele(cred);
2202 _FREE(no, M_NETADDR);
2203 return (ENOBUFS);
2204 }
2205 }
2206 rn = (*rnh->rnh_addaddr)((caddr_t)saddr, (caddr_t)smask, rnh, no->no_rnodes);
2207 if (rn == 0) {
2208 /*
2209 * One of the reasons that rnh_addaddr may fail is that
2210 * the entry already exists. To check for this case, we
2211 * look up the entry to see if it is there. If so, we
2212 * do not need to make a new entry but do continue.
2213 */
2214 int matched = 0;
2215 rn = (*rnh->rnh_matchaddr)((caddr_t)saddr, rnh);
2216 if (rn != 0 && (rn->rn_flags & RNF_ROOT) == 0 &&
2217 (((struct nfs_netopt *)rn)->no_opt.nxo_flags == nxna.nxna_flags)) {
2218 kauth_cred_t cred2 = ((struct nfs_netopt *)rn)->no_opt.nxo_cred;
2219 if (cred && cred2 && (cred->cr_uid == cred2->cr_uid) &&
2220 (cred->cr_ngroups == cred2->cr_ngroups)) {
2221 for (i=0; i < cred2->cr_ngroups && i < NGROUPS; i++)
2222 if (cred->cr_groups[i] != cred2->cr_groups[i])
2223 break;
2224 if (i >= cred2->cr_ngroups || i >= NGROUPS)
2225 matched = 1;
2226 }
2227 }
2228 kauth_cred_rele(cred);
2229 _FREE(no, M_NETADDR);
2230 if (matched)
2231 continue;
2232 return (EPERM);
2233 }
2234 nx->nx_expcnt++;
2235 }
2236
2237 return (0);
2238 }
2239
2240 /*
2241 * In order to properly track an export's netopt count, we need to pass
2242 * an additional argument to nfsrv_free_netopt() so that it can decrement
2243 * the export's netopt count.
2244 */
2245 struct nfsrv_free_netopt_arg {
2246 uint32_t *cnt;
2247 struct radix_node_head *rnh;
2248 };
2249
2250 static int
2251 nfsrv_free_netopt(struct radix_node *rn, void *w)
2252 {
2253 struct nfsrv_free_netopt_arg *fna = (struct nfsrv_free_netopt_arg *)w;
2254 struct radix_node_head *rnh = fna->rnh;
2255 uint32_t *cnt = fna->cnt;
2256 struct nfs_netopt *nno = (struct nfs_netopt *)rn;
2257
2258 (*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh);
2259 if (nno->no_opt.nxo_cred)
2260 kauth_cred_rele(nno->no_opt.nxo_cred);
2261 _FREE((caddr_t)rn, M_NETADDR);
2262 *cnt -= 1;
2263 return (0);
2264 }
2265
2266 /*
2267 * Free the net address hash lists that are hanging off the mount points.
2268 */
2269 static void
2270 nfsrv_free_addrlist(struct nfs_export *nx)
2271 {
2272 int i;
2273 struct radix_node_head *rnh;
2274 struct nfsrv_free_netopt_arg fna;
2275
2276 for (i = 0; i <= AF_MAX; i++)
2277 if ( (rnh = nx->nx_rtable[i]) ) {
2278 fna.rnh = rnh;
2279 fna.cnt = &nx->nx_expcnt;
2280 (*rnh->rnh_walktree)(rnh, nfsrv_free_netopt, (caddr_t)&fna);
2281 _FREE((caddr_t)rnh, M_RTABLE);
2282 nx->nx_rtable[i] = 0;
2283 }
2284 }
2285
2286 void enablequotas(struct mount *mp, vfs_context_t ctx); // XXX
2287
2288 int
2289 nfsrv_export(struct user_nfs_export_args *unxa, struct vfs_context *ctx)
2290 {
2291 int error = 0, pathlen;
2292 struct nfs_exportfs *nxfs, *nxfs2, *nxfs3;
2293 struct nfs_export *nx, *nx2, *nx3;
2294 struct nfs_filehandle nfh;
2295 struct nameidata mnd, xnd;
2296 vnode_t mvp = NULL, xvp = NULL;
2297 mount_t mp;
2298 char path[MAXPATHLEN];
2299 int expisroot;
2300
2301 if (unxa->nxa_flags & NXA_DELETE_ALL) {
2302 /* delete all exports on all file systems */
2303 lck_rw_lock_exclusive(&nfs_export_rwlock);
2304 while ((nxfs = LIST_FIRST(&nfs_exports))) {
2305 mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
2306 if (mp)
2307 mp->mnt_flag &= ~MNT_EXPORTED;
2308 /* delete all exports on this file system */
2309 while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
2310 LIST_REMOVE(nx, nx_next);
2311 LIST_REMOVE(nx, nx_hash);
2312 /* delete all netopts for this export */
2313 nfsrv_free_addrlist(nx);
2314 nx->nx_flags &= ~NX_DEFAULTEXPORT;
2315 if (nx->nx_defopt.nxo_cred) {
2316 kauth_cred_rele(nx->nx_defopt.nxo_cred);
2317 nx->nx_defopt.nxo_cred = NULL;
2318 }
2319 FREE(nx->nx_path, M_TEMP);
2320 FREE(nx, M_TEMP);
2321 }
2322 LIST_REMOVE(nxfs, nxfs_next);
2323 FREE(nxfs->nxfs_path, M_TEMP);
2324 FREE(nxfs, M_TEMP);
2325 }
2326 lck_rw_done(&nfs_export_rwlock);
2327 return (0);
2328 }
2329
2330 error = copyinstr(unxa->nxa_fspath, path, MAXPATHLEN, (size_t *)&pathlen);
2331 if (error)
2332 return (error);
2333
2334 lck_rw_lock_exclusive(&nfs_export_rwlock);
2335
2336 // first check if we've already got an exportfs with the given ID
2337 LIST_FOREACH(nxfs, &nfs_exports, nxfs_next) {
2338 if (nxfs->nxfs_id == unxa->nxa_fsid)
2339 break;
2340 }
2341 if (nxfs) {
2342 /* verify exported FS path matches given path */
2343 if (strcmp(path, nxfs->nxfs_path)) {
2344 error = EEXIST;
2345 goto unlock_out;
2346 }
2347 mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
2348 /* find exported FS root vnode */
2349 NDINIT(&mnd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
2350 UIO_SYSSPACE, nxfs->nxfs_path, ctx);
2351 error = namei(&mnd);
2352 if (error)
2353 goto unlock_out;
2354 mvp = mnd.ni_vp;
2355 /* make sure it's (still) the root of a file system */
2356 if ((mvp->v_flag & VROOT) == 0) {
2357 error = EINVAL;
2358 goto out;
2359 }
2360 /* sanity check: this should be same mount */
2361 if (mp != vnode_mount(mvp)) {
2362 error = EINVAL;
2363 goto out;
2364 }
2365 } else {
2366 /* no current exported file system with that ID */
2367 if (!(unxa->nxa_flags & NXA_ADD)) {
2368 error = ENOENT;
2369 goto unlock_out;
2370 }
2371
2372 /* find exported FS root vnode */
2373 NDINIT(&mnd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
2374 UIO_SYSSPACE, path, ctx);
2375 error = namei(&mnd);
2376 if (error)
2377 goto unlock_out;
2378 mvp = mnd.ni_vp;
2379 /* make sure it's the root of a file system */
2380 if ((mvp->v_flag & VROOT) == 0) {
2381 error = EINVAL;
2382 goto out;
2383 }
2384 mp = vnode_mount(mvp);
2385
2386 /* make sure the file system is NFS-exportable */
2387 nfh.nfh_len = NFS_MAX_FID_SIZE;
2388 error = VFS_VPTOFH(mvp, &nfh.nfh_len, &nfh.nfh_fid[0], NULL);
2389 if (!error && (nfh.nfh_len > (int)NFS_MAX_FID_SIZE))
2390 error = EIO;
2391 if (error)
2392 goto out;
2393
2394 /* add an exportfs for it */
2395 MALLOC(nxfs, struct nfs_exportfs *, sizeof(struct nfs_exportfs), M_TEMP, M_WAITOK);
2396 if (!nxfs) {
2397 error = ENOMEM;
2398 goto out;
2399 }
2400 bzero(nxfs, sizeof(struct nfs_exportfs));
2401 nxfs->nxfs_id = unxa->nxa_fsid;
2402 MALLOC(nxfs->nxfs_path, char*, pathlen, M_TEMP, M_WAITOK);
2403 if (!nxfs->nxfs_path) {
2404 FREE(nxfs, M_TEMP);
2405 error = ENOMEM;
2406 goto out;
2407 }
2408 bcopy(path, nxfs->nxfs_path, pathlen);
2409 /* insert into list in reverse-sorted order */
2410 nxfs3 = NULL;
2411 LIST_FOREACH(nxfs2, &nfs_exports, nxfs_next) {
2412 if (strcmp(nxfs->nxfs_path, nxfs2->nxfs_path) > 0)
2413 break;
2414 nxfs3 = nxfs2;
2415 }
2416 if (nxfs2)
2417 LIST_INSERT_BEFORE(nxfs2, nxfs, nxfs_next);
2418 else if (nxfs3)
2419 LIST_INSERT_AFTER(nxfs3, nxfs, nxfs_next);
2420 else
2421 LIST_INSERT_HEAD(&nfs_exports, nxfs, nxfs_next);
2422
2423 /* make sure any quotas are enabled before we export the file system */
2424 enablequotas(mp, ctx);
2425 }
2426
2427 if (unxa->nxa_exppath) {
2428 error = copyinstr(unxa->nxa_exppath, path, MAXPATHLEN, (size_t *)&pathlen);
2429 if (error)
2430 goto out;
2431 LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
2432 if (nx->nx_id == unxa->nxa_expid)
2433 break;
2434 }
2435 if (nx) {
2436 /* verify exported FS path matches given path */
2437 if (strcmp(path, nx->nx_path)) {
2438 error = EEXIST;
2439 goto out;
2440 }
2441 } else {
2442 /* no current export with that ID */
2443 if (!(unxa->nxa_flags & NXA_ADD)) {
2444 error = ENOENT;
2445 goto out;
2446 }
2447 /* add an export for it */
2448 MALLOC(nx, struct nfs_export *, sizeof(struct nfs_export), M_TEMP, M_WAITOK);
2449 if (!nx) {
2450 error = ENOMEM;
2451 goto out1;
2452 }
2453 bzero(nx, sizeof(struct nfs_export));
2454 nx->nx_id = unxa->nxa_expid;
2455 nx->nx_fs = nxfs;
2456 MALLOC(nx->nx_path, char*, pathlen, M_TEMP, M_WAITOK);
2457 if (!nx->nx_path) {
2458 error = ENOMEM;
2459 FREE(nx, M_TEMP);
2460 nx = NULL;
2461 goto out1;
2462 }
2463 bcopy(path, nx->nx_path, pathlen);
2464 /* insert into list in reverse-sorted order */
2465 nx3 = NULL;
2466 LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
2467 if (strcmp(nx->nx_path, nx2->nx_path) > 0)
2468 break;
2469 nx3 = nx2;
2470 }
2471 if (nx2)
2472 LIST_INSERT_BEFORE(nx2, nx, nx_next);
2473 else if (nx3)
2474 LIST_INSERT_AFTER(nx3, nx, nx_next);
2475 else
2476 LIST_INSERT_HEAD(&nxfs->nxfs_exports, nx, nx_next);
2477 /* insert into hash */
2478 LIST_INSERT_HEAD(NFSEXPHASH(nxfs->nxfs_id, nx->nx_id), nx, nx_hash);
2479
2480 /*
2481 * We don't allow nested exports. Check if the new entry
2482 * nests with the entries before and after or if there's an
2483 * entry for the file system root and subdirs.
2484 */
2485 error = 0;
2486 if ((nx3 && !strncmp(nx3->nx_path, nx->nx_path, pathlen - 1) &&
2487 (nx3->nx_path[pathlen-1] == '/')) ||
2488 (nx2 && !strncmp(nx2->nx_path, nx->nx_path, strlen(nx2->nx_path)) &&
2489 (nx->nx_path[strlen(nx2->nx_path)] == '/')))
2490 error = EINVAL;
2491 if (!error) {
2492 /* check export conflict with fs root export and vice versa */
2493 expisroot = !nx->nx_path[0] ||
2494 ((nx->nx_path[0] == '.') && !nx->nx_path[1]);
2495 LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
2496 if (expisroot) {
2497 if (nx2 != nx)
2498 break;
2499 } else if (!nx2->nx_path[0])
2500 break;
2501 else if ((nx2->nx_path[0] == '.') && !nx2->nx_path[1])
2502 break;
2503 }
2504 if (nx2)
2505 error = EINVAL;
2506 }
2507 if (error) {
2508 printf("nfsrv_export: attempt to register nested exports: %s/%s\n",
2509 nxfs->nxfs_path, nx->nx_path);
2510 goto out1;
2511 }
2512
2513 /* find export root vnode */
2514 if (!nx->nx_path[0] || ((nx->nx_path[0] == '.') && !nx->nx_path[1])) {
2515 /* exporting file system's root directory */
2516 xvp = mvp;
2517 vnode_get(xvp);
2518 } else {
2519 xnd.ni_cnd.cn_nameiop = LOOKUP;
2520 xnd.ni_cnd.cn_flags = LOCKLEAF;
2521 xnd.ni_pathlen = pathlen - 1;
2522 xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf = path;
2523 xnd.ni_startdir = mvp;
2524 xnd.ni_usedvp = mvp;
2525 xnd.ni_cnd.cn_context = ctx;
2526 error = lookup(&xnd);
2527 if (error)
2528 goto out1;
2529 xvp = xnd.ni_vp;
2530 }
2531
2532 if (vnode_vtype(xvp) != VDIR) {
2533 error = EINVAL;
2534 vnode_put(xvp);
2535 goto out1;
2536 }
2537
2538 /* grab file handle */
2539 nx->nx_fh.nfh_xh.nxh_version = NFS_FH_VERSION;
2540 nx->nx_fh.nfh_xh.nxh_fsid = nx->nx_fs->nxfs_id;
2541 nx->nx_fh.nfh_xh.nxh_expid = nx->nx_id;
2542 nx->nx_fh.nfh_xh.nxh_flags = 0;
2543 nx->nx_fh.nfh_xh.nxh_reserved = 0;
2544 nx->nx_fh.nfh_len = NFS_MAX_FID_SIZE;
2545 error = VFS_VPTOFH(xvp, &nx->nx_fh.nfh_len, &nx->nx_fh.nfh_fid[0], NULL);
2546 if (!error && (nx->nx_fh.nfh_len > (int)NFS_MAX_FID_SIZE)) {
2547 error = EIO;
2548 } else {
2549 nx->nx_fh.nfh_xh.nxh_fidlen = nx->nx_fh.nfh_len;
2550 nx->nx_fh.nfh_len += sizeof(nx->nx_fh.nfh_xh);
2551 }
2552
2553 vnode_put(xvp);
2554 if (error)
2555 goto out1;
2556 }
2557 } else {
2558 nx = NULL;
2559 }
2560
2561 /* perform the export changes */
2562 if (unxa->nxa_flags & NXA_DELETE) {
2563 if (!nx) {
2564 /* delete all exports on this file system */
2565 while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
2566 LIST_REMOVE(nx, nx_next);
2567 LIST_REMOVE(nx, nx_hash);
2568 /* delete all netopts for this export */
2569 nfsrv_free_addrlist(nx);
2570 nx->nx_flags &= ~NX_DEFAULTEXPORT;
2571 if (nx->nx_defopt.nxo_cred) {
2572 kauth_cred_rele(nx->nx_defopt.nxo_cred);
2573 nx->nx_defopt.nxo_cred = NULL;
2574 }
2575 FREE(nx->nx_path, M_TEMP);
2576 FREE(nx, M_TEMP);
2577 }
2578 goto out1;
2579 } else {
2580 /* delete all netopts for this export */
2581 nfsrv_free_addrlist(nx);
2582 nx->nx_flags &= ~NX_DEFAULTEXPORT;
2583 if (nx->nx_defopt.nxo_cred) {
2584 kauth_cred_rele(nx->nx_defopt.nxo_cred);
2585 nx->nx_defopt.nxo_cred = NULL;
2586 }
2587 }
2588 }
2589 if (unxa->nxa_flags & NXA_ADD) {
2590 error = nfsrv_hang_addrlist(nx, unxa);
2591 if (!error)
2592 mp->mnt_flag |= MNT_EXPORTED;
2593 }
2594
2595 out1:
2596 if (nx && !nx->nx_expcnt) {
2597 /* export has no export options */
2598 LIST_REMOVE(nx, nx_next);
2599 LIST_REMOVE(nx, nx_hash);
2600 FREE(nx->nx_path, M_TEMP);
2601 FREE(nx, M_TEMP);
2602 }
2603 if (LIST_EMPTY(&nxfs->nxfs_exports)) {
2604 /* exported file system has no more exports */
2605 LIST_REMOVE(nxfs, nxfs_next);
2606 FREE(nxfs->nxfs_path, M_TEMP);
2607 FREE(nxfs, M_TEMP);
2608 mp->mnt_flag &= ~MNT_EXPORTED;
2609 }
2610
2611 out:
2612 if (mvp) {
2613 vnode_put(mvp);
2614 nameidone(&mnd);
2615 }
2616 unlock_out:
2617 lck_rw_done(&nfs_export_rwlock);
2618 return (error);
2619 }
2620
2621 static struct nfs_export_options *
2622 nfsrv_export_lookup(struct nfs_export *nx, mbuf_t nam)
2623 {
2624 struct nfs_export_options *nxo = NULL;
2625 struct nfs_netopt *no = NULL;
2626 struct radix_node_head *rnh;
2627 struct sockaddr *saddr;
2628
2629 /* Lookup in the export list first. */
2630 if (nam != NULL) {
2631 saddr = mbuf_data(nam);
2632 rnh = nx->nx_rtable[saddr->sa_family];
2633 if (rnh != NULL) {
2634 no = (struct nfs_netopt *)
2635 (*rnh->rnh_matchaddr)((caddr_t)saddr, rnh);
2636 if (no && no->no_rnodes->rn_flags & RNF_ROOT)
2637 no = NULL;
2638 if (no)
2639 nxo = &no->no_opt;
2640 }
2641 }
2642 /* If no address match, use the default if it exists. */
2643 if ((nxo == NULL) && (nx->nx_flags & NX_DEFAULTEXPORT))
2644 nxo = &nx->nx_defopt;
2645 return (nxo);
2646 }
2647
2648 /* find an export for the given handle */
2649 static struct nfs_export *
2650 nfsrv_fhtoexport(struct nfs_filehandle *nfhp)
2651 {
2652 struct nfs_export *nx;
2653 nx = NFSEXPHASH(nfhp->nfh_xh.nxh_fsid, nfhp->nfh_xh.nxh_expid)->lh_first;
2654 for (; nx; nx = LIST_NEXT(nx, nx_hash)) {
2655 if (nx->nx_fs->nxfs_id != nfhp->nfh_xh.nxh_fsid)
2656 continue;
2657 if (nx->nx_id != nfhp->nfh_xh.nxh_expid)
2658 continue;
2659 break;
2660 }
2661 return nx;
2662 }
2663
2664 /*
2665 * nfsrv_fhtovp() - convert FH to vnode and export info
2666 */
2667 int
2668 nfsrv_fhtovp(
2669 struct nfs_filehandle *nfhp,
2670 mbuf_t nam,
2671 __unused int pubflag,
2672 vnode_t *vpp,
2673 struct nfs_export **nxp,
2674 struct nfs_export_options **nxop)
2675 {
2676 int error;
2677 struct mount *mp;
2678
2679 *vpp = NULL;
2680 *nxp = NULL;
2681 *nxop = NULL;
2682
2683 if (nfhp->nfh_xh.nxh_version != NFS_FH_VERSION) {
2684 /* file handle format not supported */
2685 return (ESTALE);
2686 }
2687 if (nfhp->nfh_len > NFS_MAX_FH_SIZE)
2688 return (EBADRPC);
2689 if (nfhp->nfh_len < (int)sizeof(nfhp->nfh_xh))
2690 return (ESTALE);
2691 if (nfhp->nfh_xh.nxh_flags & NXHF_INVALIDFH)
2692 return (ESTALE);
2693
2694 /* XXX Revisit when enabling WebNFS */
2695 #ifdef WEBNFS_ENABLED
2696 if (nfs_ispublicfh(nfhp)) {
2697 if (!pubflag || !nfs_pub.np_valid)
2698 return (ESTALE);
2699 nfhp = &nfs_pub.np_handle;
2700 }
2701 #endif
2702
2703 *nxp = nfsrv_fhtoexport(nfhp);
2704 if (!*nxp)
2705 return (ESTALE);
2706
2707 /* Get the export option structure for this <export, client> tuple. */
2708 *nxop = nfsrv_export_lookup(*nxp, nam);
2709 if (nam && (*nxop == NULL))
2710 return (EACCES);
2711
2712 /* find mount structure */
2713 mp = vfs_getvfs_by_mntonname((*nxp)->nx_fs->nxfs_path);
2714 if (!mp)
2715 return (ESTALE);
2716
2717 error = VFS_FHTOVP(mp, nfhp->nfh_xh.nxh_fidlen, &nfhp->nfh_fid[0], vpp, NULL);
2718 if (error)
2719 return (error);
2720 /* vnode pointer should be good at this point or ... */
2721 if (*vpp == NULL)
2722 return (ESTALE);
2723 return (0);
2724 }
2725
2726 /*
2727 * nfsrv_credcheck() - check/map credentials according to given export options
2728 */
2729 int
2730 nfsrv_credcheck(
2731 struct nfsrv_descript *nfsd,
2732 __unused struct nfs_export *nx,
2733 struct nfs_export_options *nxo)
2734 {
2735 if (nxo && nxo->nxo_cred) {
2736 if ((nxo->nxo_flags & NX_MAPALL) ||
2737 ((nxo->nxo_flags & NX_MAPROOT) && !suser(nfsd->nd_cr, NULL))) {
2738 kauth_cred_rele(nfsd->nd_cr);
2739 nfsd->nd_cr = nxo->nxo_cred;
2740 kauth_cred_ref(nfsd->nd_cr);
2741 }
2742 }
2743 return (0);
2744 }
2745
2746
2747 /*
2748 * WebNFS: check if a filehandle is a public filehandle. For v3, this
2749 * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
2750 * transformed this to all zeroes in both cases, so check for it.
2751 */
2752 int
2753 nfs_ispublicfh(struct nfs_filehandle *nfhp)
2754 {
2755 char *cp = (char *)nfhp;
2756 unsigned int i;
2757
2758 if (nfhp->nfh_len == 0)
2759 return (TRUE);
2760 if (nfhp->nfh_len != NFSX_V2FH)
2761 return (FALSE);
2762 for (i = 0; i < NFSX_V2FH; i++)
2763 if (*cp++ != 0)
2764 return (FALSE);
2765 return (TRUE);
2766 }
2767
2768 /*
2769 * nfsrv_vptofh() - convert vnode to file handle for given export
2770 *
2771 * If the caller is passing in a vnode for a ".." directory entry,
2772 * they can pass a directory NFS file handle (dnfhp) which will be
2773 * checked against the root export file handle. If it matches, we
2774 * refuse to provide the file handle for the out-of-export directory.
2775 */
2776 int
2777 nfsrv_vptofh(
2778 struct nfs_export *nx,
2779 int v2,
2780 struct nfs_filehandle *dnfhp,
2781 vnode_t vp,
2782 struct vfs_context *ctx,
2783 struct nfs_filehandle *nfhp)
2784 {
2785 int error;
2786
2787 nfhp->nfh_xh.nxh_version = NFS_FH_VERSION;
2788 nfhp->nfh_xh.nxh_fsid = nx->nx_fs->nxfs_id;
2789 nfhp->nfh_xh.nxh_expid = nx->nx_id;
2790 nfhp->nfh_xh.nxh_flags = 0;
2791 nfhp->nfh_xh.nxh_reserved = 0;
2792
2793 if (v2)
2794 bzero(&nfhp->nfh_fid[0], NFSV2_MAX_FID_SIZE);
2795
2796 /* if directory FH matches export root, return invalid FH */
2797 if (dnfhp && nfsrv_fhmatch(dnfhp, &nx->nx_fh)) {
2798 nfhp->nfh_len = v2 ? NFSX_V2FH : sizeof(nfhp->nfh_xh);
2799 nfhp->nfh_xh.nxh_fidlen = 0;
2800 nfhp->nfh_xh.nxh_flags = NXHF_INVALIDFH;
2801 return (0);
2802 }
2803
2804 nfhp->nfh_len = v2 ? NFSV2_MAX_FID_SIZE : NFS_MAX_FID_SIZE;
2805 error = VFS_VPTOFH(vp, &nfhp->nfh_len, &nfhp->nfh_fid[0], ctx);
2806 if (error)
2807 return (error);
2808 if (nfhp->nfh_len > (int)(v2 ? NFSV2_MAX_FID_SIZE : NFS_MAX_FID_SIZE))
2809 return (EOVERFLOW);
2810 nfhp->nfh_xh.nxh_fidlen = nfhp->nfh_len;
2811 nfhp->nfh_len += sizeof(nfhp->nfh_xh);
2812 if (v2 && (nfhp->nfh_len < NFSX_V2FH))
2813 nfhp->nfh_len = NFSX_V2FH;
2814
2815 return (0);
2816 }
2817
2818 int
2819 nfsrv_fhmatch(struct nfs_filehandle *fh1, struct nfs_filehandle *fh2)
2820 {
2821 int len1, len2;
2822
2823 len1 = sizeof(fh1->nfh_xh) + fh1->nfh_xh.nxh_fidlen;
2824 len2 = sizeof(fh2->nfh_xh) + fh2->nfh_xh.nxh_fidlen;
2825 if (len1 != len2)
2826 return (0);
2827 if (bcmp(&fh1->nfh_xh, &fh2->nfh_xh, len1))
2828 return (0);
2829 return (1);
2830 }
2831
2832 #endif /* NFS_NOSERVER */
2833 /*
2834 * This function compares two net addresses by family and returns TRUE
2835 * if they are the same host.
2836 * If there is any doubt, return FALSE.
2837 * The AF_INET family is handled as a special case so that address mbufs
2838 * don't need to be saved to store "struct in_addr", which is only 4 bytes.
2839 */
2840 int
2841 netaddr_match(family, haddr, nam)
2842 int family;
2843 union nethostaddr *haddr;
2844 mbuf_t nam;
2845 {
2846 struct sockaddr_in *inetaddr;
2847
2848 switch (family) {
2849 case AF_INET:
2850 inetaddr = mbuf_data(nam);
2851 if (inetaddr->sin_family == AF_INET &&
2852 inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
2853 return (1);
2854 break;
2855 #if ISO
2856 case AF_ISO:
2857 {
2858 struct sockaddr_iso *isoaddr1, *isoaddr2;
2859
2860 isoaddr1 = mbuf_data(nam);
2861 isoaddr2 = mbuf_data(haddr->had_nam);
2862 if (isoaddr1->siso_family == AF_ISO &&
2863 isoaddr1->siso_nlen > 0 &&
2864 isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
2865 SAME_ISOADDR(isoaddr1, isoaddr2))
2866 return (1);
2867 break;
2868 }
2869 #endif /* ISO */
2870 default:
2871 break;
2872 };
2873 return (0);
2874 }
2875
2876 static nfsuint64 nfs_nullcookie = { { 0, 0 } };
2877 /*
2878 * This function finds the directory cookie that corresponds to the
2879 * logical byte offset given.
2880 */
2881 nfsuint64 *
2882 nfs_getcookie(np, off, add)
2883 struct nfsnode *np;
2884 off_t off;
2885 int add;
2886 {
2887 struct nfsdmap *dp, *dp2;
2888 int pos;
2889
2890 pos = off / NFS_DIRBLKSIZ;
2891 if (pos == 0) {
2892 #if DIAGNOSTIC
2893 if (add)
2894 panic("nfs getcookie add at 0");
2895 #endif
2896 return (&nfs_nullcookie);
2897 }
2898 pos--;
2899 dp = np->n_cookies.lh_first;
2900 if (!dp) {
2901 if (add) {
2902 MALLOC_ZONE(dp, struct nfsdmap *, sizeof(struct nfsdmap),
2903 M_NFSDIROFF, M_WAITOK);
2904 if (!dp)
2905 return ((nfsuint64 *)0);
2906 dp->ndm_eocookie = 0;
2907 LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
2908 } else
2909 return ((nfsuint64 *)0);
2910 }
2911 while (pos >= NFSNUMCOOKIES) {
2912 pos -= NFSNUMCOOKIES;
2913 if (dp->ndm_list.le_next) {
2914 if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
2915 pos >= dp->ndm_eocookie)
2916 return ((nfsuint64 *)0);
2917 dp = dp->ndm_list.le_next;
2918 } else if (add) {
2919 MALLOC_ZONE(dp2, struct nfsdmap *, sizeof(struct nfsdmap),
2920 M_NFSDIROFF, M_WAITOK);
2921 if (!dp2)
2922 return ((nfsuint64 *)0);
2923 dp2->ndm_eocookie = 0;
2924 LIST_INSERT_AFTER(dp, dp2, ndm_list);
2925 dp = dp2;
2926 } else
2927 return ((nfsuint64 *)0);
2928 }
2929 if (pos >= dp->ndm_eocookie) {
2930 if (add)
2931 dp->ndm_eocookie = pos + 1;
2932 else
2933 return ((nfsuint64 *)0);
2934 }
2935 return (&dp->ndm_cookies[pos]);
2936 }
2937
2938 /*
2939 * Invalidate cached directory information, except for the actual directory
2940 * blocks (which are invalidated separately).
2941 * Done mainly to avoid the use of stale offset cookies.
2942 */
2943 void
2944 nfs_invaldir(vp)
2945 vnode_t vp;
2946 {
2947 struct nfsnode *np = VTONFS(vp);
2948
2949 #if DIAGNOSTIC
2950 if (vnode_vtype(vp) != VDIR)
2951 panic("nfs: invaldir not dir");
2952 #endif
2953 np->n_direofoffset = 0;
2954 np->n_cookieverf.nfsuquad[0] = 0;
2955 np->n_cookieverf.nfsuquad[1] = 0;
2956 if (np->n_cookies.lh_first)
2957 np->n_cookies.lh_first->ndm_eocookie = 0;
2958 }
2959
2960 #ifndef NFS_NOSERVER
2961 /*
2962 * Map errnos to NFS error numbers. For Version 3 also filter out error
2963 * numbers not specified for the associated procedure.
2964 */
2965 int
2966 nfsrv_errmap(nd, err)
2967 struct nfsrv_descript *nd;
2968 int err;
2969 {
2970 short *defaulterrp, *errp;
2971
2972 if (nd->nd_flag & ND_NFSV3) {
2973 if (nd->nd_procnum <= NFSPROC_COMMIT) {
2974 errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
2975 while (*++errp) {
2976 if (*errp == err)
2977 return (err);
2978 else if (*errp > err)
2979 break;
2980 }
2981 return ((int)*defaulterrp);
2982 } else
2983 return (err & 0xffff);
2984 }
2985 if (err <= ELAST)
2986 return ((int)nfsrv_v2errmap[err - 1]);
2987 return (NFSERR_IO);
2988 }
2989
2990 #endif /* NFS_NOSERVER */
2991