1 /*
2 * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)nfs_serv.c 8.7 (Berkeley) 5/14/95
65 * FreeBSD-Id: nfs_serv.c,v 1.52 1997/10/28 15:59:05 bde Exp $
66 */
67
68 #include <nfs/nfs_conf.h>
69 #if CONFIG_NFS_SERVER
70
71 #include <sys/param.h>
72 #include <sys/systm.h>
73 #include <sys/proc.h>
74 #include <sys/kauth.h>
75 #include <sys/unistd.h>
76 #include <sys/malloc.h>
77 #include <sys/vnode.h>
78 #include <sys/mount_internal.h>
79 #include <sys/socket.h>
80 #include <sys/socketvar.h>
81 #include <sys/kpi_mbuf.h>
82 #include <sys/dirent.h>
83 #include <sys/stat.h>
84 #include <sys/kernel.h>
85 #include <sys/ubc.h>
86 #include <sys/vnode_internal.h>
87 #include <sys/uio_internal.h>
88 #include <libkern/OSAtomic.h>
89 #include <IOKit/IOLib.h>
90 #include <sys/fsevents.h>
91 #include <kern/thread_call.h>
92 #include <sys/time.h>
93
94 #include <sys/vm.h>
95 #include <sys/vmparam.h>
96
97 #include <sys/fcntl.h>
98
99 #include <netinet/in.h>
100
101 #include <nfs/nfsproto.h>
102 #include <nfs/rpcv2.h>
103 #include <nfs/nfs.h>
104 #include <nfs/xdr_subs.h>
105 #include <nfs/nfsm_subs.h>
106 #include <nfs/nfsrvcache.h>
107 #include <nfs/nfs_gss.h>
108
109 #if CONFIG_MACF
110 #include <security/mac.h>
111 #include <security/mac_framework.h>
112 #endif
113
114 /*
115 * NFS server globals
116 */
117
118 int nfsd_thread_count = 0;
119 int nfsd_thread_max = 0;
120 static LCK_GRP_DECLARE(nfsd_lck_grp, "nfsd");
121 LCK_MTX_DECLARE(nfsd_mutex, &nfsd_lck_grp);
122 struct nfsd_head nfsd_head, nfsd_queue;
123
124 LCK_GRP_DECLARE(nfsrv_slp_rwlock_group, "nfsrv-slp-rwlock");
125 LCK_GRP_DECLARE(nfsrv_slp_mutex_group, "nfsrv-slp-mutex");
126 struct nfsrv_sockhead nfsrv_socklist, nfsrv_sockwg,
127 nfsrv_sockwait, nfsrv_sockwork;
128 struct nfsrv_sock *nfsrv_udpsock = NULL;
129 struct nfsrv_sock *nfsrv_udp6sock = NULL;
130
131 /* NFS exports */
132 struct nfsrv_expfs_list nfsrv_exports;
133 struct nfsrv_export_hashhead *nfsrv_export_hashtbl = NULL;
134 int nfsrv_export_hash_size = NFSRVEXPHASHSZ;
135 u_long nfsrv_export_hash;
136 static LCK_GRP_DECLARE(nfsrv_export_rwlock_group, "nfsrv-export-rwlock");
137 LCK_RW_DECLARE(nfsrv_export_rwlock, &nfsrv_export_rwlock_group);
138
139 #if CONFIG_FSE
140 /* NFS server file modification event generator */
141 struct nfsrv_fmod_hashhead *nfsrv_fmod_hashtbl;
142 u_long nfsrv_fmod_hash;
143 static LCK_GRP_DECLARE(nfsrv_fmod_grp, "nfsrv_fmod");
144 LCK_MTX_DECLARE(nfsrv_fmod_mutex, &nfsrv_fmod_grp);
145 static int nfsrv_fmod_timer_on = 0;
146 int nfsrv_fsevents_enabled = 1;
147 #endif
148
149 /* NFS server timers */
150 #if CONFIG_FSE
151 thread_call_t nfsrv_fmod_timer_call;
152 #endif
153 thread_call_t nfsrv_idlesock_timer_call;
154 thread_call_t nfsrv_wg_timer_call;
155 int nfsrv_wg_timer_on;
156
157 /* globals for the active user list */
158 uint32_t nfsrv_user_stat_enabled = 1;
159 uint32_t nfsrv_user_stat_node_count = 0;
160 uint32_t nfsrv_user_stat_max_idle_sec = NFSRV_USER_STAT_DEF_IDLE_SEC;
161 uint32_t nfsrv_user_stat_max_nodes = NFSRV_USER_STAT_DEF_MAX_NODES;
162 LCK_GRP_DECLARE(nfsrv_active_user_mutex_group, "nfs-active-user-mutex");
163
164 int nfsrv_wg_delay = NFSRV_WGATHERDELAY * 1000;
165 int nfsrv_wg_delay_v3 = 0;
166
167 int nfsrv_async = 0;
168
169 int nfsrv_authorize(vnode_t, vnode_t, kauth_action_t, vfs_context_t, struct nfs_export_options*, int);
170 int nfsrv_wg_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
171 void nfsrv_modified(vnode_t, vfs_context_t);
172
173 extern int safe_getpath(struct vnode *dvp, char *leafname, char *path, int _len, int *truncated_path);
174
175 /*
176 * Initialize the data structures for the server.
177 */
178
179 #define NFSRV_NOT_INITIALIZED 0
180 #define NFSRV_INITIALIZING 1
181 #define NFSRV_INITIALIZED 2
182 static volatile UInt32 nfsrv_initted = NFSRV_NOT_INITIALIZED;
183
184 int
nfsrv_is_initialized(void)185 nfsrv_is_initialized(void)
186 {
187 return nfsrv_initted == NFSRV_INITIALIZED;
188 }
189
190 void
nfsrv_init(void)191 nfsrv_init(void)
192 {
193 /* make sure we init only once */
194 if (!OSCompareAndSwap(NFSRV_NOT_INITIALIZED, NFSRV_INITIALIZING, &nfsrv_initted)) {
195 /* wait until initialization is complete */
196 while (!nfsrv_is_initialized()) {
197 IOSleep(500);
198 }
199 return;
200 }
201
202 if (sizeof(struct nfsrv_sock) > NFS_SVCALLOC) {
203 printf("struct nfsrv_sock bloated (> %dbytes)\n", NFS_SVCALLOC);
204 }
205
206 /* init export data structures */
207 LIST_INIT(&nfsrv_exports);
208
209 #if CONFIG_FSE
210 /* init NFS server file modified event generation */
211 nfsrv_fmod_hashtbl = hashinit(NFSRVFMODHASHSZ, M_TEMP, &nfsrv_fmod_hash);
212 #endif
213
214 #if CONFIG_NFS_GSS
215 nfs_gss_svc_init(); /* Init RPCSEC_GSS security */
216 #endif
217
218 /* initialize NFS server timer callouts */
219 #if CONFIG_FSE
220 nfsrv_fmod_timer_call = thread_call_allocate(nfsrv_fmod_timer, NULL);
221 #endif
222 nfsrv_idlesock_timer_call = thread_call_allocate(nfsrv_idlesock_timer, NULL);
223 nfsrv_wg_timer_call = thread_call_allocate(nfsrv_wg_timer, NULL);
224
225 /* Init server data structures */
226 TAILQ_INIT(&nfsrv_socklist);
227 TAILQ_INIT(&nfsrv_sockwait);
228 TAILQ_INIT(&nfsrv_sockwork);
229 TAILQ_INIT(&nfsrv_sockwg);
230 TAILQ_INIT(&nfsd_head);
231 TAILQ_INIT(&nfsd_queue);
232 nfsrv_udpsock = NULL;
233 nfsrv_udp6sock = NULL;
234
235 /* Setup the up-call handling */
236 nfsrv_uc_init();
237
238 /* initialization complete */
239 nfsrv_initted = NFSRV_INITIALIZED;
240 }
241
242
243 /*
244 *
245 * NFS version 2 and 3 server request processing functions
246 *
247 * These functions take the following parameters:
248 *
249 * struct nfsrv_descript *nd - the NFS request descriptor
250 * struct nfsrv_sock *slp - the NFS socket the request came in on
251 * vfs_context_t ctx - VFS context
252 * mbuf_t *mrepp - pointer to hold the reply mbuf list
253 *
254 * These routines generally have 3 phases:
255 *
256 * 1 - break down and validate the RPC request in the mbuf chain
257 * provided in nd->nd_nmreq.
258 * 2 - perform the vnode operations for the request
259 * (many are very similar to syscalls in vfs_syscalls.c and
260 * should therefore be kept in sync with those implementations)
261 * 3 - build the RPC reply in an mbuf chain (nmrep) and return the mbuf chain
262 *
263 */
264
265 /*
266 * nfs v3 access service
267 */
268 int
nfsrv_access(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)269 nfsrv_access(
270 struct nfsrv_descript *nd,
271 struct nfsrv_sock *slp,
272 vfs_context_t ctx,
273 mbuf_t *mrepp)
274 {
275 struct nfsm_chain *nmreq, nmrep;
276 vnode_t vp;
277 int error, attrerr;
278 struct vnode_attr vattr;
279 struct nfs_filehandle nfh;
280 u_int32_t nfsmode;
281 kauth_action_t testaction;
282 struct nfs_export *nx;
283 struct nfs_export_options *nxo;
284
285 error = 0;
286 attrerr = ENOENT;
287 nfsmode = 0;
288 nmreq = &nd->nd_nmreq;
289 nfsm_chain_null(&nmrep);
290 *mrepp = NULL;
291 vp = NULL;
292
293 nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
294 nfsm_chain_get_32(error, nmreq, nfsmode);
295 nfsmerr_if(error);
296 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
297 nfsmerr_if(error);
298
299 /* update export stats */
300 NFSStatAdd64(&nx->nx_stats.ops, 1);
301
302 /* update active user stats */
303 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
304
305 error = nfsrv_credcheck(nd, ctx, nx, nxo);
306 nfsmerr_if(error);
307
308 /*
309 * Each NFS mode bit is tested separately.
310 *
311 * XXX this code is nominally correct, but returns a pessimistic
312 * rather than optimistic result. It will be necessary to add
313 * an NFS-specific interface to the vnode_authorize code to
314 * obtain good performance in the optimistic mode.
315 */
316 if (nfsmode & NFS_ACCESS_READ) {
317 testaction = vnode_isdir(vp) ? KAUTH_VNODE_LIST_DIRECTORY : KAUTH_VNODE_READ_DATA;
318 if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) {
319 nfsmode &= ~NFS_ACCESS_READ;
320 }
321 }
322 if ((nfsmode & NFS_ACCESS_LOOKUP) &&
323 (!vnode_isdir(vp) ||
324 nfsrv_authorize(vp, NULL, KAUTH_VNODE_SEARCH, ctx, nxo, 0))) {
325 nfsmode &= ~NFS_ACCESS_LOOKUP;
326 }
327 if (nfsmode & NFS_ACCESS_MODIFY) {
328 if (vnode_isdir(vp)) {
329 testaction =
330 KAUTH_VNODE_ADD_FILE |
331 KAUTH_VNODE_ADD_SUBDIRECTORY |
332 KAUTH_VNODE_DELETE_CHILD;
333 } else {
334 testaction =
335 KAUTH_VNODE_WRITE_DATA;
336 }
337 if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) {
338 nfsmode &= ~NFS_ACCESS_MODIFY;
339 }
340 }
341 if (nfsmode & NFS_ACCESS_EXTEND) {
342 if (vnode_isdir(vp)) {
343 testaction =
344 KAUTH_VNODE_ADD_FILE |
345 KAUTH_VNODE_ADD_SUBDIRECTORY;
346 } else {
347 testaction =
348 KAUTH_VNODE_WRITE_DATA |
349 KAUTH_VNODE_APPEND_DATA;
350 }
351 if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) {
352 nfsmode &= ~NFS_ACCESS_EXTEND;
353 }
354 }
355
356 /*
357 * Note concerning NFS_ACCESS_DELETE:
358 * For hard links, the answer may be wrong if the vnode
359 * has multiple parents with different permissions.
360 * Also, some clients (e.g. MacOSX 10.3) may incorrectly
361 * interpret the missing/cleared DELETE bit.
362 * So we'll just leave the DELETE bit alone. At worst,
363 * we're telling the client it might be able to do
364 * something it really can't.
365 */
366
367 if ((nfsmode & NFS_ACCESS_EXECUTE) &&
368 (vnode_isdir(vp) ||
369 nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 0))) {
370 nfsmode &= ~NFS_ACCESS_EXECUTE;
371 }
372
373 /* get postop attributes */
374 nfsm_srv_vattr_init(&vattr, NFS_VER3);
375 attrerr = vnode_getattr(vp, &vattr, ctx);
376
377 nfsmerr:
378 /* assemble reply */
379 nd->nd_repstat = error;
380 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(NFS_VER3) + NFSX_UNSIGNED);
381 nfsmout_if(error);
382 *mrepp = nmrep.nmc_mhead;
383 nfsmout_on_status(nd, error);
384 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &vattr);
385 if (!nd->nd_repstat) {
386 nfsm_chain_add_32(error, &nmrep, nfsmode);
387 }
388 nfsmout:
389 nfsm_chain_build_done(error, &nmrep);
390 if (vp) {
391 vnode_put(vp);
392 }
393 if (error) {
394 nfsm_chain_cleanup(&nmrep);
395 *mrepp = NULL;
396 }
397 return error;
398 }
399
400 /*
401 * nfs getattr service
402 */
403 int
nfsrv_getattr(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)404 nfsrv_getattr(
405 struct nfsrv_descript *nd,
406 struct nfsrv_sock *slp,
407 vfs_context_t ctx,
408 mbuf_t *mrepp)
409 {
410 struct nfsm_chain *nmreq, nmrep;
411 struct vnode_attr vattr;
412 vnode_t vp;
413 int error;
414 struct nfs_filehandle nfh;
415 struct nfs_export *nx;
416 struct nfs_export_options *nxo;
417
418 error = 0;
419 nmreq = &nd->nd_nmreq;
420 nfsm_chain_null(&nmrep);
421 *mrepp = NULL;
422 vp = NULL;
423
424 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
425 nfsmerr_if(error);
426 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
427 nfsmerr_if(error);
428
429 /* update export stats */
430 NFSStatAdd64(&nx->nx_stats.ops, 1);
431
432 /* update active user stats */
433 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
434
435 error = nfsrv_credcheck(nd, ctx, nx, nxo);
436 nfsmerr_if(error);
437
438 #if CONFIG_MACF
439 if (mac_vnode_check_open(ctx, vp, FREAD)) {
440 error = ESTALE;
441 }
442 nfsmerr_if(error);
443 #endif
444
445 nfsm_srv_vattr_init(&vattr, nd->nd_vers);
446 error = vnode_getattr(vp, &vattr, ctx);
447
448 #if CONFIG_MACF
449 /* XXXab: Comment in the VFS code makes it sound like
450 * some arguments can be filtered out, but not
451 * what it actually means. Hopefully not like
452 * they gonna set mtime to 0 or something. For
453 * now trust there are no shenanigans here.
454 */
455 error = mac_vnode_check_getattr(ctx, NOCRED, vp, &vattr);
456 nfsmerr_if(error);
457 #endif
458
459 vnode_put(vp);
460 vp = NULL;
461
462 nfsmerr:
463 /* assemble reply */
464 nd->nd_repstat = error;
465 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_FATTR(nd->nd_vers));
466 nfsmout_if(error);
467 *mrepp = nmrep.nmc_mhead;
468 nfsmout_if(nd->nd_repstat);
469 error = nfsm_chain_add_fattr(nd, &nmrep, &vattr);
470 nfsmout:
471 nfsm_chain_build_done(error, &nmrep);
472 if (vp) {
473 vnode_put(vp);
474 }
475 if (error) {
476 nfsm_chain_cleanup(&nmrep);
477 *mrepp = NULL;
478 }
479 return error;
480 }
481
482 /*
483 * nfs setattr service
484 */
485 int
nfsrv_setattr(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)486 nfsrv_setattr(
487 struct nfsrv_descript *nd,
488 struct nfsrv_sock *slp,
489 vfs_context_t ctx,
490 mbuf_t *mrepp)
491 {
492 struct nfsm_chain *nmreq, nmrep;
493 struct vnode_attr preattr, postattr;
494 struct vnode_attr vattr, *vap = &vattr;
495 vnode_t vp;
496 struct nfs_export *nx;
497 struct nfs_export_options *nxo;
498 int error, preattrerr, postattrerr, gcheck;
499 struct nfs_filehandle nfh;
500 struct timespec guard = { .tv_sec = 0, .tv_nsec = 0 };
501 kauth_action_t action;
502 uid_t saved_uid;
503
504 error = 0;
505 preattrerr = postattrerr = ENOENT;
506 gcheck = 0;
507 nmreq = &nd->nd_nmreq;
508 nfsm_chain_null(&nmrep);
509 *mrepp = NULL;
510 vp = NULL;
511
512 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
513 nfsmerr_if(error);
514
515 VATTR_INIT(vap);
516 error = nfsm_chain_get_sattr(nd, nmreq, vap);
517 if (nd->nd_vers == NFS_VER3) {
518 nfsm_chain_get_32(error, nmreq, gcheck);
519 if (gcheck) {
520 nfsm_chain_get_time(error, nmreq, nd->nd_vers, guard.tv_sec, guard.tv_nsec);
521 }
522 }
523 nfsmerr_if(error);
524
525 /*
526 * Save the original credential UID in case they are
527 * mapped and we need to map the IDs in the attributes.
528 */
529 saved_uid = kauth_cred_getuid(nd->nd_cr);
530
531 /*
532 * Now that we have all the fields, lets do it.
533 */
534 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
535 nfsmerr_if(error);
536
537 /* update export stats */
538 NFSStatAdd64(&nx->nx_stats.ops, 1);
539
540 /* update active user stats */
541 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
542
543 error = nfsrv_credcheck(nd, ctx, nx, nxo);
544 nfsmerr_if(error);
545
546 if (nd->nd_vers == NFS_VER3) {
547 nfsm_srv_pre_vattr_init(&preattr);
548 error = preattrerr = vnode_getattr(vp, &preattr, ctx);
549 if (!error && gcheck && VATTR_IS_SUPPORTED(&preattr, va_change_time) &&
550 (preattr.va_change_time.tv_sec != guard.tv_sec ||
551 preattr.va_change_time.tv_nsec != guard.tv_nsec)) {
552 error = NFSERR_NOT_SYNC;
553 }
554 if (!preattrerr && !VATTR_ALL_SUPPORTED(&preattr)) {
555 preattrerr = ENOENT;
556 }
557 nfsmerr_if(error);
558 }
559
560 /*
561 * If the credentials were mapped, we should
562 * map the same values in the attributes.
563 */
564 if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nd->nd_cr) != saved_uid)) {
565 int ismember;
566 VATTR_SET(vap, va_uid, kauth_cred_getuid(nd->nd_cr));
567 if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember) {
568 VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
569 }
570 }
571
572 /* Authorize the attribute changes. */
573 error = vnode_authattr(vp, vap, &action, ctx);
574 if (!error) {
575 error = nfsrv_authorize(vp, NULL, action, ctx, nxo, 0);
576 }
577
578 #if CONFIG_MACF
579 if (!error && mac_vnode_check_open(ctx, vp, FREAD | FWRITE)) {
580 error = ESTALE;
581 }
582
583 if (!error) {
584 /* chown case */
585 if (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid)) {
586 error = mac_vnode_check_setowner(ctx, vp,
587 VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : -1,
588 VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : -1);
589 }
590 /* chmod case */
591 if (!error && VATTR_IS_ACTIVE(vap, va_mode)) {
592 error = mac_vnode_check_setmode(ctx, vp, (mode_t)vap->va_mode);
593 }
594 /* truncate case */
595 if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
596 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
597 error = mac_vnode_check_truncate(ctx, NOCRED, vp);
598 }
599 /* set utimes case */
600 if (!error && (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time))) {
601 struct timespec current_time;
602 nanotime(¤t_time);
603
604 error = mac_vnode_check_setutimes(ctx, vp,
605 VATTR_IS_ACTIVE(vap, va_access_time) ? vap->va_access_time : current_time,
606 VATTR_IS_ACTIVE(vap, va_modify_time) ? vap->va_modify_time : current_time);
607 }
608 }
609 #endif
610 /* set the new attributes */
611 if (!error) {
612 error = vnode_setattr(vp, vap, ctx);
613 }
614
615 if (!error || (nd->nd_vers == NFS_VER3)) {
616 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
617 postattrerr = vnode_getattr(vp, &postattr, ctx);
618 if (!error) {
619 error = postattrerr;
620 }
621 }
622
623 nfsmerr:
624 if (vp) {
625 vnode_put(vp);
626 }
627
628 /* assemble reply */
629 nd->nd_repstat = error;
630 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCORFATTR(nd->nd_vers));
631 nfsmout_if(error);
632 *mrepp = nmrep.nmc_mhead;
633 nfsmout_on_status(nd, error);
634 if (nd->nd_vers == NFS_VER3) {
635 nfsm_chain_add_wcc_data(error, nd, &nmrep,
636 preattrerr, &preattr, postattrerr, &postattr);
637 } else {
638 error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
639 }
640 nfsmout:
641 nfsm_chain_build_done(error, &nmrep);
642 if (error) {
643 nfsm_chain_cleanup(&nmrep);
644 *mrepp = NULL;
645 }
646 return error;
647 }
648
649 /*
650 * nfs lookup rpc
651 */
652 int
nfsrv_lookup(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)653 nfsrv_lookup(
654 struct nfsrv_descript *nd,
655 struct nfsrv_sock *slp,
656 vfs_context_t ctx,
657 mbuf_t *mrepp)
658 {
659 struct nameidata ni;
660 vnode_t vp, dirp = NULL;
661 struct nfs_filehandle dnfh, nfh = {};
662 struct nfs_export *nx = NULL;
663 struct nfs_export_options *nxo;
664 int error, attrerr, dirattrerr, isdotdot;
665 uint32_t len = 0;
666 uid_t saved_uid;
667 struct vnode_attr va, dirattr, *vap = &va;
668 struct nfsm_chain *nmreq, nmrep;
669
670 error = 0;
671 attrerr = dirattrerr = ENOENT;
672 nmreq = &nd->nd_nmreq;
673 nfsm_chain_null(&nmrep);
674 saved_uid = kauth_cred_getuid(nd->nd_cr);
675
676 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
677 nfsm_chain_get_32(error, nmreq, len);
678 nfsm_name_len_check(error, nd, len);
679 nfsmerr_if(error);
680
681 NDINIT(&ni, LOOKUP, OP_LOOKUP, LOCKLEAF | CN_FIRMLINK_NOFOLLOW, UIO_SYSSPACE, 0, ctx);
682 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
683 isdotdot = ((len == 2) && (ni.ni_cnd.cn_pnbuf[0] == '.') && (ni.ni_cnd.cn_pnbuf[1] == '.'));
684 if (!error) {
685 error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo);
686 if (nx != NULL) {
687 /* update export stats */
688 NFSStatAdd64(&nx->nx_stats.ops, 1);
689
690 /* update active user stats */
691 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
692 }
693 if (!error && mac_vnode_check_open(ctx, ni.ni_vp, FREAD)) {
694 error = EACCES;
695 if (dirp) {
696 vnode_put(dirp);
697 dirp = NULL;
698 }
699
700 if (ni.ni_vp) {
701 vnode_put(ni.ni_vp);
702 ni.ni_vp = NULL;
703 }
704 }
705 }
706
707 if (dirp) {
708 if (nd->nd_vers == NFS_VER3) {
709 nfsm_srv_vattr_init(&dirattr, NFS_VER3);
710 dirattrerr = vnode_getattr(dirp, &dirattr, ctx);
711 }
712 vnode_put(dirp);
713 }
714 nfsmerr_if(error);
715
716 nameidone(&ni);
717
718 vp = ni.ni_vp;
719 error = nfsrv_vptofh(nx, nd->nd_vers, (isdotdot ? &dnfh : NULL), vp, ctx, &nfh);
720 if (!error) {
721 nfsm_srv_vattr_init(vap, nd->nd_vers);
722 attrerr = vnode_getattr(vp, vap, ctx);
723 }
724 vnode_put(vp);
725
726 nfsmerr:
727 /* assemble reply */
728 nd->nd_repstat = error;
729 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
730 NFSX_POSTOPORFATTR(nd->nd_vers) + NFSX_POSTOPATTR(nd->nd_vers));
731 nfsmout_if(error);
732 *mrepp = nmrep.nmc_mhead;
733 if (nd->nd_repstat) {
734 if (nd->nd_vers == NFS_VER3) {
735 nfsm_chain_add_postop_attr(error, nd, &nmrep, dirattrerr, &dirattr);
736 }
737 goto nfsmout;
738 }
739 nfsm_chain_add_fh(error, &nmrep, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
740 if (nd->nd_vers == NFS_VER3) {
741 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, vap);
742 nfsm_chain_add_postop_attr(error, nd, &nmrep, dirattrerr, &dirattr);
743 } else if (!error) {
744 error = nfsm_chain_add_fattr(nd, &nmrep, vap);
745 }
746 nfsmout:
747 nfsm_chain_build_done(error, &nmrep);
748 if (error) {
749 nfsm_chain_cleanup(&nmrep);
750 *mrepp = NULL;
751 }
752 return error;
753 }
754
755 /*
756 * nfs readlink service
757 */
758 int
nfsrv_readlink(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)759 nfsrv_readlink(
760 struct nfsrv_descript *nd,
761 struct nfsrv_sock *slp,
762 vfs_context_t ctx,
763 mbuf_t *mrepp)
764 {
765 int error, mpcnt, tlen, len, attrerr;
766 vnode_t vp;
767 struct vnode_attr vattr;
768 struct nfs_filehandle nfh;
769 struct nfs_export *nx;
770 struct nfs_export_options *nxo;
771 struct nfsm_chain *nmreq, nmrep;
772 mbuf_t mpath, mp;
773 uio_t auio = NULL;
774 uio_stackbuf_t uio_buf[UIO_SIZEOF(4)];
775 char *uio_bufp = &uio_buf[0];
776 int uio_buflen = UIO_SIZEOF(4);
777
778 error = 0;
779 attrerr = ENOENT;
780 nmreq = &nd->nd_nmreq;
781 nfsm_chain_null(&nmrep);
782 mpath = NULL;
783 vp = NULL;
784 len = NFS_MAXPATHLEN;
785
786 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
787 nfsmerr_if(error);
788
789 /* get mbuf list to hold symlink path */
790 error = nfsm_mbuf_get_list(len, &mpath, &mpcnt);
791 nfsmerr_if(error);
792 if (mpcnt > 4) {
793 uio_buflen = UIO_SIZEOF(mpcnt);
794 uio_bufp = kalloc_data(uio_buflen, Z_WAITOK);
795 if (!uio_bufp) {
796 error = ENOMEM;
797 }
798 nfsmerr_if(error);
799 }
800 auio = uio_createwithbuffer(mpcnt, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
801 if (!auio) {
802 error = ENOMEM;
803 }
804 nfsmerr_if(error);
805
806 for (mp = mpath; mp; mp = mbuf_next(mp)) {
807 uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(mp));
808 }
809
810 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
811 nfsmerr_if(error);
812
813 /* update export stats */
814 NFSStatAdd64(&nx->nx_stats.ops, 1);
815
816 /* update active user stats */
817 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
818
819 error = nfsrv_credcheck(nd, ctx, nx, nxo);
820 nfsmerr_if(error);
821
822 if (vnode_vtype(vp) != VLNK) {
823 if (nd->nd_vers == NFS_VER3) {
824 error = EINVAL;
825 } else {
826 error = ENXIO;
827 }
828 }
829
830 if (!error) {
831 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 0);
832 }
833 #if CONFIG_MACF
834 if (mac_vnode_check_open(ctx, vp, FREAD)) {
835 error = ESTALE;
836 }
837 nfsmerr_if(error);
838 if (!error) {
839 error = mac_vnode_check_readlink(ctx, vp);
840 }
841 #endif
842 if (!error) {
843 error = VNOP_READLINK(vp, auio, ctx);
844 }
845 if (vp) {
846 if (nd->nd_vers == NFS_VER3) {
847 nfsm_srv_vattr_init(&vattr, NFS_VER3);
848 attrerr = vnode_getattr(vp, &vattr, ctx);
849 }
850 vnode_put(vp);
851 vp = NULL;
852 }
853 if (error) {
854 mbuf_freem(mpath);
855 mpath = NULL;
856 }
857
858 nfsmerr:
859 /* assemble reply */
860 nd->nd_repstat = error;
861 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_UNSIGNED);
862 nfsmout_if(error);
863 *mrepp = nmrep.nmc_mhead;
864 nfsmout_on_status(nd, error);
865 if (nd->nd_vers == NFS_VER3) {
866 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &vattr);
867 }
868 if (error || nd->nd_repstat) {
869 nfsm_chain_build_done(error, &nmrep);
870 goto nfsmout;
871 }
872 if (auio && (uio_resid(auio) > 0)) {
873 len -= uio_resid(auio);
874 tlen = nfsm_rndup(len);
875 nfsm_adj(mpath, NFS_MAXPATHLEN - tlen, tlen - len);
876 }
877 nfsm_chain_add_32(error, &nmrep, len);
878 nfsm_chain_build_done(error, &nmrep);
879 nfsmout_if(error);
880 error = mbuf_setnext(nmrep.nmc_mcur, mpath);
881 if (!error) {
882 mpath = NULL;
883 }
884 nfsmout:
885 if (vp) {
886 vnode_put(vp);
887 }
888 if (mpath) {
889 mbuf_freem(mpath);
890 }
891 if (uio_bufp != &uio_buf[0]) {
892 kfree_data(uio_bufp, uio_buflen);
893 }
894 if (error) {
895 nfsm_chain_cleanup(&nmrep);
896 *mrepp = NULL;
897 }
898 return error;
899 }
900
901 /*
902 * nfs read service
903 */
904 int
nfsrv_read(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)905 nfsrv_read(
906 struct nfsrv_descript *nd,
907 struct nfsrv_sock *slp,
908 vfs_context_t ctx,
909 mbuf_t *mrepp)
910 {
911 int error, attrerr, mreadcnt;
912 uint32_t reqlen, maxlen, count, len, tlen;
913 mbuf_t mread, m;
914 vnode_t vp;
915 struct nfs_filehandle nfh;
916 struct nfs_export *nx = NULL;
917 struct nfs_export_options *nxo;
918 uio_t auio = NULL;
919 char *uio_bufp = NULL;
920 struct vnode_attr vattr, *vap = &vattr;
921 off_t off;
922 uid_t saved_uid;
923 uio_stackbuf_t uio_buf[UIO_SIZEOF(0)];
924 struct nfsm_chain *nmreq, nmrep;
925
926 error = 0;
927 attrerr = ENOENT;
928 nmreq = &nd->nd_nmreq;
929 nfsm_chain_null(&nmrep);
930 mread = NULL;
931 vp = NULL;
932 len = reqlen = 0;
933 saved_uid = kauth_cred_getuid(nd->nd_cr);
934
935 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
936 nfsmerr_if(error);
937 if (nd->nd_vers == NFS_VER3) {
938 nfsm_chain_get_64(error, nmreq, off);
939 } else {
940 nfsm_chain_get_32(error, nmreq, off);
941 }
942 nfsm_chain_get_32(error, nmreq, reqlen);
943 maxlen = NFSRV_NDMAXDATA(nd);
944 if (reqlen > maxlen) {
945 reqlen = maxlen;
946 }
947 nfsmerr_if(error);
948 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
949 nfsmerr_if(error);
950
951 /* update export stats */
952 NFSStatAdd64(&nx->nx_stats.ops, 1);
953
954 error = nfsrv_credcheck(nd, ctx, nx, nxo);
955 nfsmerr_if(error);
956
957 if (vnode_vtype(vp) != VREG) {
958 if (nd->nd_vers == NFS_VER3) {
959 error = EINVAL;
960 } else {
961 error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
962 }
963 }
964
965 if (!error) {
966 if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 1))) {
967 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 1);
968 }
969 }
970 #if CONFIG_MACF
971 if (!error) {
972 error = mac_vnode_check_open(ctx, vp, FREAD);
973 if (error) {
974 error = EACCES;
975 } else {
976 /* XXXab: Do we need to do this?! */
977 error = mac_vnode_check_read(ctx, vfs_context_ucred(ctx), vp);
978 if (error) {
979 error = EACCES;
980 }
981 /* mac_vnode_check_exec() can't be done here. */
982 }
983 }
984 nfsmerr_if(error);
985 #endif
986 nfsm_srv_vattr_init(vap, nd->nd_vers);
987 attrerr = vnode_getattr(vp, vap, ctx);
988 if (!error) {
989 error = attrerr;
990 }
991 nfsmerr_if(error);
992
993 if ((u_quad_t)off >= vap->va_data_size) {
994 count = 0;
995 } else if (((u_quad_t)off + reqlen) > vap->va_data_size) {
996 count = (int)nfsm_rndup(vap->va_data_size - off);
997 } else {
998 count = reqlen;
999 }
1000
1001 len = count;
1002 if (count > 0) {
1003 /* get mbuf list to hold read data */
1004 error = nfsm_mbuf_get_list(count, &mread, &mreadcnt);
1005 nfsmerr_if(error);
1006 uio_bufp = kalloc_data(UIO_SIZEOF(mreadcnt), Z_WAITOK);
1007 if (uio_bufp) {
1008 auio = uio_createwithbuffer(mreadcnt, off, UIO_SYSSPACE,
1009 UIO_READ, uio_bufp, UIO_SIZEOF(mreadcnt));
1010 }
1011 if (!uio_bufp || !auio) {
1012 error = ENOMEM;
1013 goto errorexit;
1014 }
1015 for (m = mread; m; m = mbuf_next(m)) {
1016 uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m));
1017 }
1018 error = VNOP_READ(vp, auio, IO_NODELOCKED, ctx);
1019 } else {
1020 auio = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
1021 if (!auio) {
1022 error = ENOMEM;
1023 goto errorexit;
1024 }
1025 }
1026
1027 errorexit:
1028 if (!error || (nd->nd_vers == NFS_VER3)) {
1029 nfsm_srv_vattr_init(vap, nd->nd_vers);
1030 attrerr = vnode_getattr(vp, vap, ctx);
1031 if (!error && (nd->nd_vers == NFS_VER2)) {
1032 error = attrerr; /* NFSv2 must have attributes to return */
1033 }
1034 }
1035 nfsmerr_if(error);
1036
1037 vnode_put(vp);
1038 vp = NULL;
1039
1040 /* trim off any data not actually read */
1041 len -= uio_resid(auio);
1042 tlen = nfsm_rndup(len);
1043 if (count != tlen || tlen != len) {
1044 nfsm_adj(mread, count - tlen, tlen - len);
1045 }
1046
1047 nfsmerr:
1048 /* assemble reply */
1049 nd->nd_repstat = error;
1050 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPORFATTR(nd->nd_vers) + 3 * NFSX_UNSIGNED);
1051 nfsmout_if(error);
1052 *mrepp = nmrep.nmc_mhead;
1053 nfsmout_on_status(nd, error);
1054 if (nd->nd_vers == NFS_VER3) {
1055 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, vap);
1056 }
1057 if (error || nd->nd_repstat) {
1058 nfsm_chain_build_done(error, &nmrep);
1059 goto nfsmout;
1060 }
1061 if (nd->nd_vers == NFS_VER3) {
1062 nfsm_chain_add_32(error, &nmrep, len);
1063 nfsm_chain_add_32(error, &nmrep, (len < reqlen) ? TRUE : FALSE);
1064 } else {
1065 error = nfsm_chain_add_fattr(nd, &nmrep, vap);
1066 }
1067 nfsm_chain_add_32(error, &nmrep, len);
1068 nfsm_chain_build_done(error, &nmrep);
1069 nfsmout_if(error);
1070 error = mbuf_setnext(nmrep.nmc_mcur, mread);
1071 if (!error) {
1072 mread = NULL;
1073 }
1074
1075 /* update export stats */
1076 NFSStatAdd64(&nx->nx_stats.bytes_read, len);
1077
1078 /* update active user stats */
1079 nfsrv_update_user_stat(nx, nd, saved_uid, 1, len, 0);
1080 nfsmout:
1081 if (vp) {
1082 vnode_put(vp);
1083 }
1084 if (mread) {
1085 mbuf_freem(mread);
1086 }
1087 if (uio_bufp != NULL) {
1088 kfree_data(uio_bufp, UIO_SIZEOF(mreadcnt));
1089 }
1090 if (error) {
1091 nfsm_chain_cleanup(&nmrep);
1092 *mrepp = NULL;
1093 }
1094 return error;
1095 }
1096
1097 #if CONFIG_FSE
1098 /*
1099 * NFS File modification reporting
1100 *
1101 * When the contents of a file are changed, a "content modified"
1102 * fsevent needs to be issued. Normally this would be done at
1103 * file close time. This is difficult for NFS because the protocol
1104 * has no "close" operation. The client sends a stream of write
1105 * requests that just stop. So we keep a hash table full of
1106 * vnodes that have been written to recently, and issue a
1107 * "content modified" fsevent only if there are no writes to
1108 * a vnode for nfsrv_fmod_pendtime milliseconds.
1109 */
1110 int nfsrv_fmod_pending; /* count of vnodes being written to */
1111 int nfsrv_fmod_pendtime = 1000; /* msec to wait */
1112 int nfsrv_fmod_min_interval = 100; /* msec min interval between callbacks */
1113
1114 /*
1115 * This function is called via the kernel's callout
1116 * mechanism. Calls are made only when there are
1117 * vnodes pending a fsevent creation, and no more
1118 * frequently than every nfsrv_fmod_min_interval ms.
1119 */
1120 void
nfsrv_fmod_timer(__unused void * param0,__unused void * param1)1121 nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
1122 {
1123 struct nfsrv_fmod_hashhead *headp, firehead;
1124 struct nfsrv_fmod *fp, *nfp, *pfp;
1125 uint64_t timenow, next_deadline;
1126 time_t interval = 0;
1127 int i, fmod_fire;
1128
1129 LIST_INIT(&firehead);
1130 lck_mtx_lock(&nfsrv_fmod_mutex);
1131 again:
1132 clock_get_uptime(&timenow);
1133 clock_interval_to_deadline(nfsrv_fmod_pendtime, 1000 * 1000,
1134 &next_deadline);
1135
1136 /*
1137 * Scan all the hash chains
1138 */
1139 fmod_fire = 0;
1140 for (i = 0; i < NFSRVFMODHASHSZ; i++) {
1141 /*
1142 * For each hash chain, look for an entry
1143 * that has exceeded the deadline.
1144 */
1145 headp = &nfsrv_fmod_hashtbl[i];
1146 LIST_FOREACH(fp, headp, fm_link) {
1147 if (timenow >= fp->fm_deadline) {
1148 break;
1149 }
1150 if (fp->fm_deadline < next_deadline) {
1151 next_deadline = fp->fm_deadline;
1152 }
1153 }
1154
1155 /*
1156 * If we have an entry that's exceeded the
1157 * deadline, then the same is true for all
1158 * following entries in the chain, since they're
1159 * sorted in time order.
1160 */
1161 pfp = NULL;
1162 while (fp) {
1163 /* move each entry to the fire list */
1164 nfp = LIST_NEXT(fp, fm_link);
1165 LIST_REMOVE(fp, fm_link);
1166 fmod_fire++;
1167 if (pfp) {
1168 LIST_INSERT_AFTER(pfp, fp, fm_link);
1169 } else {
1170 LIST_INSERT_HEAD(&firehead, fp, fm_link);
1171 }
1172 pfp = fp;
1173 fp = nfp;
1174 }
1175 }
1176
1177 if (fmod_fire) {
1178 lck_mtx_unlock(&nfsrv_fmod_mutex);
1179 /*
1180 * Fire off the content modified fsevent for each
1181 * entry and free it.
1182 */
1183 LIST_FOREACH_SAFE(fp, &firehead, fm_link, nfp) {
1184 if (nfsrv_fsevents_enabled) {
1185 fp->fm_context.vc_thread = current_thread();
1186 add_fsevent(FSE_CONTENT_MODIFIED, &fp->fm_context,
1187 FSE_ARG_VNODE, fp->fm_vp,
1188 FSE_ARG_DONE);
1189 }
1190 vnode_put(fp->fm_vp);
1191 kauth_cred_unref(&fp->fm_context.vc_ucred);
1192 LIST_REMOVE(fp, fm_link);
1193 kfree_type(struct nfsrv_fmod, fp);
1194 }
1195 lck_mtx_lock(&nfsrv_fmod_mutex);
1196 nfsrv_fmod_pending -= fmod_fire;
1197 goto again;
1198 }
1199
1200 /*
1201 * If there are still pending entries, set up another
1202 * callout to handle them later. Set the timeout deadline
1203 * so that the callout happens when the oldest pending
1204 * entry is ready to send its fsevent.
1205 */
1206 if (nfsrv_fmod_pending > 0) {
1207 interval = ((time_t)(next_deadline - timenow)) / (1000 * 1000);
1208 if (interval < nfsrv_fmod_min_interval) {
1209 interval = nfsrv_fmod_min_interval;
1210 }
1211 }
1212
1213 nfsrv_fmod_timer_on = interval > 0;
1214 if (nfsrv_fmod_timer_on) {
1215 nfs_interval_timer_start(nfsrv_fmod_timer_call, interval);
1216 }
1217
1218 lck_mtx_unlock(&nfsrv_fmod_mutex);
1219 }
1220
1221 /*
1222 * When a vnode has been written to, enter it in the hash
1223 * table of vnodes pending creation of an fsevent. If the
1224 * callout timer isn't already running, schedule a callback
1225 * for nfsrv_fmod_pendtime msec from now.
1226 */
1227 void
nfsrv_modified(vnode_t vp,vfs_context_t ctx)1228 nfsrv_modified(vnode_t vp, vfs_context_t ctx)
1229 {
1230 uint64_t deadline;
1231 struct nfsrv_fmod *fp;
1232 struct nfsrv_fmod_hashhead *head;
1233
1234 lck_mtx_lock(&nfsrv_fmod_mutex);
1235
1236 /*
1237 * Compute the time in the future when the
1238 * content modified fsevent is to be issued.
1239 */
1240 clock_interval_to_deadline(nfsrv_fmod_pendtime, 1000 * 1000, &deadline);
1241
1242 /*
1243 * Check if there's already a file content change fsevent
1244 * pending for this vnode. If there is, update its
1245 * timestamp and make sure it's at the front of the hash chain.
1246 */
1247 head = &nfsrv_fmod_hashtbl[NFSRVFMODHASH(vp)];
1248 LIST_FOREACH(fp, head, fm_link) {
1249 if (vp == fp->fm_vp) {
1250 fp->fm_deadline = deadline;
1251 if (fp != LIST_FIRST(head)) {
1252 LIST_REMOVE(fp, fm_link);
1253 LIST_INSERT_HEAD(head, fp, fm_link);
1254 }
1255 lck_mtx_unlock(&nfsrv_fmod_mutex);
1256 return;
1257 }
1258 }
1259
1260 /*
1261 * First content change fsevent for this vnode.
1262 * Allocate a new file mod entry and add it
1263 * on the front of the hash chain.
1264 */
1265 if (vnode_get(vp) != 0) {
1266 goto done;
1267 }
1268 fp = kalloc_type(struct nfsrv_fmod, Z_WAITOK | Z_NOFAIL);
1269 fp->fm_vp = vp;
1270 kauth_cred_ref(vfs_context_ucred(ctx));
1271 fp->fm_context = *ctx;
1272 fp->fm_deadline = deadline;
1273 LIST_INSERT_HEAD(head, fp, fm_link);
1274
1275 /*
1276 * If added to an empty hash table, then set the
1277 * callout timer to go off after nfsrv_fmod_pendtime.
1278 */
1279 nfsrv_fmod_pending++;
1280 if (!nfsrv_fmod_timer_on) {
1281 nfsrv_fmod_timer_on = 1;
1282 nfs_interval_timer_start(nfsrv_fmod_timer_call,
1283 nfsrv_fmod_pendtime);
1284 }
1285 done:
1286 lck_mtx_unlock(&nfsrv_fmod_mutex);
1287 return;
1288 }
1289 #endif /* CONFIG_FSE */
1290
1291 /*
1292 * nfs write service
1293 */
1294 int
nfsrv_write(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)1295 nfsrv_write(
1296 struct nfsrv_descript *nd,
1297 struct nfsrv_sock *slp,
1298 vfs_context_t ctx,
1299 mbuf_t *mrepp)
1300 {
1301 struct vnode_attr preattr, postattr;
1302 int error, preattrerr, postattrerr;
1303 int ioflags, len, retlen;
1304 int mlen, mcount;
1305 int stable = NFS_WRITE_FILESYNC;
1306 mbuf_t m;
1307 vnode_t vp;
1308 struct nfs_filehandle nfh;
1309 struct nfs_export *nx = NULL;
1310 struct nfs_export_options *nxo;
1311 uio_t auio = NULL;
1312 char *uio_bufp = NULL;
1313 off_t off;
1314 uid_t saved_uid;
1315 struct nfsm_chain *nmreq, nmrep;
1316
1317 if (nd->nd_nmreq.nmc_mhead == NULL) {
1318 *mrepp = NULL;
1319 return 0;
1320 }
1321
1322 error = 0;
1323 preattrerr = postattrerr = ENOENT;
1324 saved_uid = kauth_cred_getuid(nd->nd_cr);
1325 nmreq = &nd->nd_nmreq;
1326 nfsm_chain_null(&nmrep);
1327 vp = NULL;
1328 len = retlen = 0;
1329
1330 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
1331 nfsmerr_if(error);
1332 if (nd->nd_vers == NFS_VER3) {
1333 nfsm_chain_get_64(error, nmreq, off);
1334 nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1335 nfsm_chain_get_32(error, nmreq, stable);
1336 } else {
1337 nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1338 nfsm_chain_get_32(error, nmreq, off);
1339 nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1340 if (nfsrv_async) {
1341 stable = NFS_WRITE_UNSTABLE;
1342 }
1343 }
1344 nfsm_chain_get_32(error, nmreq, len);
1345 nfsmerr_if(error);
1346 retlen = len;
1347
1348 /*
1349 * For NFS Version 2, it is not obvious what a write of zero length
1350 * should do, but I might as well be consistent with Version 3,
1351 * which is to return ok so long as there are no permission problems.
1352 */
1353
1354 if (len > 0) {
1355 error = nfsm_chain_trim_data(nmreq, len, &mlen);
1356 nfsmerr_if(error);
1357 } else {
1358 mlen = 0;
1359 }
1360 if ((len > NFSRV_MAXDATA) || (len < 0) || (mlen < len)) {
1361 error = EIO;
1362 goto nfsmerr;
1363 }
1364 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
1365 nfsmerr_if(error);
1366
1367 /* update export stats */
1368 NFSStatAdd64(&nx->nx_stats.ops, 1);
1369
1370 error = nfsrv_credcheck(nd, ctx, nx, nxo);
1371 nfsmerr_if(error);
1372
1373 if (nd->nd_vers == NFS_VER3) {
1374 nfsm_srv_pre_vattr_init(&preattr);
1375 preattrerr = vnode_getattr(vp, &preattr, ctx);
1376 }
1377 if (vnode_vtype(vp) != VREG) {
1378 if (nd->nd_vers == NFS_VER3) {
1379 error = EINVAL;
1380 } else {
1381 error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
1382 }
1383 }
1384 if (!error) {
1385 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 1);
1386 }
1387 nfsmerr_if(error);
1388
1389 #if CONFIG_MACF
1390 if (!error) {
1391 error = mac_vnode_check_open(ctx, vp, FWRITE);
1392 if (error) {
1393 error = EACCES;
1394 } else {
1395 /* XXXab: Do we need to do this?! */
1396 error = mac_vnode_check_write(ctx, vfs_context_ucred(ctx), vp);
1397 if (error) {
1398 error = EACCES;
1399 }
1400 }
1401 }
1402 nfsmerr_if(error);
1403 #endif
1404
1405 if (len > 0) {
1406 for (mcount = 0, m = nmreq->nmc_mcur; m; m = mbuf_next(m)) {
1407 if (mbuf_len(m) > 0) {
1408 mcount++;
1409 }
1410 }
1411 uio_bufp = kalloc_data(UIO_SIZEOF(mcount), Z_WAITOK);
1412 if (uio_bufp) {
1413 auio = uio_createwithbuffer(mcount, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(mcount));
1414 }
1415 if (!uio_bufp || !auio) {
1416 error = ENOMEM;
1417 }
1418 nfsmerr_if(error);
1419 for (m = nmreq->nmc_mcur; m; m = mbuf_next(m)) {
1420 if ((mlen = (int)mbuf_len(m)) > 0) {
1421 uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mlen);
1422 }
1423 }
1424 /*
1425 * XXX The IO_METASYNC flag indicates that all metadata (and not just
1426 * enough to ensure data integrity) mus be written to stable storage
1427 * synchronously. (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
1428 */
1429 if (stable == NFS_WRITE_UNSTABLE) {
1430 ioflags = IO_NODELOCKED;
1431 } else if (stable == NFS_WRITE_DATASYNC) {
1432 ioflags = (IO_SYNC | IO_NODELOCKED);
1433 } else {
1434 ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
1435 }
1436
1437 error = VNOP_WRITE(vp, auio, ioflags, ctx);
1438 OSAddAtomic64(1, &nfsrvstats.srvvop_writes);
1439
1440 /* update export stats */
1441 NFSStatAdd64(&nx->nx_stats.bytes_written, len);
1442
1443 /* update active user stats */
1444 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, len);
1445
1446 #if CONFIG_FSE
1447 if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) {
1448 nfsrv_modified(vp, ctx);
1449 }
1450 #endif
1451 }
1452 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
1453 postattrerr = vnode_getattr(vp, &postattr, ctx);
1454 if (!error && (nd->nd_vers == NFS_VER2)) {
1455 error = postattrerr; /* NFSv2 must have attributes to return */
1456 }
1457 vnode_put(vp);
1458 vp = NULL;
1459
1460 nfsmerr:
1461 /* assemble reply */
1462 nd->nd_repstat = error;
1463 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_PREOPATTR(nd->nd_vers) +
1464 NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED +
1465 NFSX_WRITEVERF(nd->nd_vers));
1466 nfsmout_if(error);
1467 *mrepp = nmrep.nmc_mhead;
1468 nfsmout_on_status(nd, error);
1469 if (nd->nd_vers == NFS_VER3) {
1470 nfsm_chain_add_wcc_data(error, nd, &nmrep,
1471 preattrerr, &preattr, postattrerr, &postattr);
1472 nfsmout_if(error || nd->nd_repstat);
1473 nfsm_chain_add_32(error, &nmrep, retlen);
1474 /* If nfsrv_async is set, then pretend the write was FILESYNC. */
1475 if ((stable == NFS_WRITE_UNSTABLE) && !nfsrv_async) {
1476 nfsm_chain_add_32(error, &nmrep, stable);
1477 } else {
1478 nfsm_chain_add_32(error, &nmrep, NFS_WRITE_FILESYNC);
1479 }
1480 /* write verifier */
1481 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
1482 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
1483 } else {
1484 error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
1485 }
1486 nfsmout:
1487 nfsm_chain_build_done(error, &nmrep);
1488 if (vp) {
1489 vnode_put(vp);
1490 }
1491 if (uio_bufp != NULL) {
1492 kfree_data(uio_bufp, UIO_SIZEOF(mcount));
1493 }
1494 if (error) {
1495 nfsm_chain_cleanup(&nmrep);
1496 *mrepp = NULL;
1497 }
1498 return error;
1499 }
1500
1501 /*
1502 * NFS write service with write gathering support. Called when
1503 * nfsrv_wg_delay > 0.
1504 * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
1505 * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
1506 * Jan. 1994.
1507 */
1508
1509 #define NWDELAYHASH(sock, f) \
1510 (&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ])
1511 /* These macros compare nfsrv_descript structures. */
1512 #define NFSW_CONTIG(o, n) \
1513 (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh))
1514 /*
1515 * XXX The following is an incorrect comparison; it fails to take into account
1516 * XXX scoping of MAC labels, but we currently lack KPI for credential
1517 * XXX comparisons.
1518 */
1519 #define NFSW_SAMECRED(o, n) \
1520 (!bcmp((caddr_t)(o)->nd_cr, (caddr_t)(n)->nd_cr, \
1521 sizeof (struct ucred)))
1522
1523 int
nfsrv_writegather(struct nfsrv_descript ** ndp,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)1524 nfsrv_writegather(
1525 struct nfsrv_descript **ndp,
1526 struct nfsrv_sock *slp,
1527 vfs_context_t ctx,
1528 mbuf_t *mrepp)
1529 {
1530 struct nfsrv_descript *nd, *wp, *owp, *swp;
1531 struct nfs_export *nx;
1532 struct nfs_export_options *nxo;
1533 struct nfsrv_wg_delayhash *wpp;
1534 uid_t saved_uid;
1535 struct vnode_attr preattr, postattr;
1536 int error, mlen, i, ioflags;
1537 size_t tlen;
1538 int preattrerr, postattrerr;
1539 vnode_t vp;
1540 mbuf_t m;
1541 uio_t auio = NULL;
1542 char *uio_bufp = NULL;
1543 time_t cur_usec;
1544 struct timeval now;
1545 struct nfsm_chain *nmreq, nmrep;
1546
1547 error = 0;
1548 preattrerr = postattrerr = ENOENT;
1549 nfsm_chain_null(&nmrep);
1550 vp = NULL;
1551
1552 *mrepp = NULL;
1553 if (*ndp) {
1554 nd = *ndp;
1555 *ndp = NULL;
1556 nmreq = &nd->nd_nmreq;
1557 LIST_INIT(&nd->nd_coalesce);
1558 nd->nd_mrep = NULL;
1559 nd->nd_stable = NFS_WRITE_FILESYNC;
1560 microuptime(&now);
1561 cur_usec = now.tv_sec * 1000000 + now.tv_usec;
1562 nd->nd_time = cur_usec +
1563 ((nd->nd_vers == NFS_VER3) ? nfsrv_wg_delay_v3 : nfsrv_wg_delay);
1564
1565 /* Now, get the write header... */
1566 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nd->nd_fh.nfh_fhp, nd->nd_fh.nfh_len);
1567 /* XXX shouldn't we be checking for invalid FHs before doing any more work? */
1568 nfsmerr_if(error);
1569 if (nd->nd_vers == NFS_VER3) {
1570 nfsm_chain_get_64(error, nmreq, nd->nd_off);
1571 nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1572 nfsm_chain_get_32(error, nmreq, nd->nd_stable);
1573 } else {
1574 nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1575 nfsm_chain_get_32(error, nmreq, nd->nd_off);
1576 nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED);
1577 if (nfsrv_async) {
1578 nd->nd_stable = NFS_WRITE_UNSTABLE;
1579 }
1580 }
1581 nfsm_chain_get_32(error, nmreq, nd->nd_len);
1582 nfsmerr_if(error);
1583 nd->nd_eoff = nd->nd_off + nd->nd_len;
1584
1585 if (nd->nd_len > 0) {
1586 error = nfsm_chain_trim_data(nmreq, nd->nd_len, &mlen);
1587 nfsmerr_if(error);
1588 } else {
1589 mlen = 0;
1590 }
1591
1592 if ((nd->nd_len > NFSRV_MAXDATA) || (nd->nd_len < 0) || (mlen < nd->nd_len)) {
1593 error = EIO;
1594 nfsmerr:
1595 nd->nd_repstat = error;
1596 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
1597 if (!error) {
1598 nd->nd_mrep = nmrep.nmc_mhead;
1599 if (nd->nd_vers == NFS_VER3) {
1600 nfsm_chain_add_wcc_data(error, nd, &nmrep,
1601 preattrerr, &preattr, postattrerr, &postattr);
1602 }
1603 }
1604 nfsm_chain_build_done(error, &nmrep);
1605 nd->nd_time = 1;
1606 }
1607
1608 /*
1609 * Add this entry to the hash and time queues.
1610 */
1611 lck_mtx_lock(&slp->ns_wgmutex);
1612 owp = NULL;
1613 wp = slp->ns_tq.lh_first;
1614 while (wp && wp->nd_time < nd->nd_time) {
1615 owp = wp;
1616 wp = wp->nd_tq.le_next;
1617 }
1618 if (owp) {
1619 LIST_INSERT_AFTER(owp, nd, nd_tq);
1620 } else {
1621 LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq);
1622 }
1623 if (!error) {
1624 wpp = NWDELAYHASH(slp, nd->nd_fh.nfh_fid);
1625 owp = NULL;
1626 wp = wpp->lh_first;
1627 while (wp && !nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) {
1628 owp = wp;
1629 wp = wp->nd_hash.le_next;
1630 }
1631 while (wp && (wp->nd_off < nd->nd_off) &&
1632 nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) {
1633 owp = wp;
1634 wp = wp->nd_hash.le_next;
1635 }
1636 if (owp) {
1637 LIST_INSERT_AFTER(owp, nd, nd_hash);
1638 /*
1639 * Search the hash list for overlapping entries and
1640 * coalesce.
1641 */
1642 for (; nd && NFSW_CONTIG(owp, nd); nd = wp) {
1643 wp = nd->nd_hash.le_next;
1644 if (NFSW_SAMECRED(owp, nd)) {
1645 nfsrv_wg_coalesce(owp, nd);
1646 }
1647 }
1648 } else {
1649 LIST_INSERT_HEAD(wpp, nd, nd_hash);
1650 }
1651 }
1652 } else {
1653 lck_mtx_lock(&slp->ns_wgmutex);
1654 }
1655
1656 /*
1657 * Now, do VNOP_WRITE()s for any one(s) that need to be done now
1658 * and generate the associated reply mbuf list(s).
1659 */
1660 loop1:
1661 microuptime(&now);
1662 cur_usec = now.tv_sec * 1000000 + now.tv_usec;
1663 for (nd = slp->ns_tq.lh_first; nd; nd = owp) {
1664 owp = nd->nd_tq.le_next;
1665 if (nd->nd_time > cur_usec) {
1666 break;
1667 }
1668 if (nd->nd_mrep) {
1669 continue;
1670 }
1671 LIST_REMOVE(nd, nd_tq);
1672 LIST_REMOVE(nd, nd_hash);
1673 nmreq = &nd->nd_nmreq;
1674 preattrerr = postattrerr = ENOENT;
1675
1676 /* save the incoming uid before mapping, */
1677 /* for updating active user stats later */
1678 saved_uid = kauth_cred_getuid(nd->nd_cr);
1679
1680 error = nfsrv_fhtovp(&nd->nd_fh, nd, &vp, &nx, &nxo);
1681 if (!error) {
1682 /* update per-export stats */
1683 NFSStatAdd64(&nx->nx_stats.ops, 1);
1684
1685 error = nfsrv_credcheck(nd, ctx, nx, nxo);
1686 if (error) {
1687 vnode_put(vp);
1688 }
1689 }
1690 if (!error) {
1691 if (nd->nd_vers == NFS_VER3) {
1692 nfsm_srv_pre_vattr_init(&preattr);
1693 preattrerr = vnode_getattr(vp, &preattr, ctx);
1694 }
1695 if (vnode_vtype(vp) != VREG) {
1696 if (nd->nd_vers == NFS_VER3) {
1697 error = EINVAL;
1698 } else {
1699 error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
1700 }
1701 }
1702 } else {
1703 vp = NULL;
1704 }
1705 if (!error) {
1706 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 1);
1707 }
1708
1709 if (nd->nd_stable == NFS_WRITE_UNSTABLE) {
1710 ioflags = IO_NODELOCKED;
1711 } else if (nd->nd_stable == NFS_WRITE_DATASYNC) {
1712 ioflags = (IO_SYNC | IO_NODELOCKED);
1713 } else {
1714 ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
1715 }
1716
1717 if (!error && ((nd->nd_eoff - nd->nd_off) > 0)) {
1718 for (i = 0, m = nmreq->nmc_mhead; m; m = mbuf_next(m)) {
1719 if (mbuf_len(m) > 0) {
1720 i++;
1721 }
1722 }
1723
1724 uio_bufp = kalloc_data(UIO_SIZEOF(i), Z_WAITOK);
1725 if (uio_bufp) {
1726 auio = uio_createwithbuffer(i, nd->nd_off, UIO_SYSSPACE,
1727 UIO_WRITE, uio_bufp, UIO_SIZEOF(i));
1728 }
1729 if (!uio_bufp || !auio) {
1730 error = ENOMEM;
1731 }
1732 if (!error) {
1733 for (m = nmreq->nmc_mhead; m; m = mbuf_next(m)) {
1734 if ((tlen = mbuf_len(m)) > 0) {
1735 uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), tlen);
1736 }
1737 }
1738 error = VNOP_WRITE(vp, auio, ioflags, ctx);
1739 OSAddAtomic64(1, &nfsrvstats.srvvop_writes);
1740
1741 /* update export stats */
1742 NFSStatAdd64(&nx->nx_stats.bytes_written, nd->nd_len);
1743 /* update active user stats */
1744 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, nd->nd_len);
1745
1746 #if CONFIG_FSE
1747 if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) {
1748 nfsrv_modified(vp, ctx);
1749 }
1750 #endif
1751 }
1752 if (uio_bufp) {
1753 kfree_data(uio_bufp, UIO_SIZEOF(i));
1754 uio_bufp = NULL;
1755 }
1756 }
1757 if (vp) {
1758 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
1759 postattrerr = vnode_getattr(vp, &postattr, ctx);
1760 vnode_put(vp);
1761 }
1762
1763 /*
1764 * Loop around generating replies for all write rpcs that have
1765 * now been completed.
1766 */
1767 swp = nd;
1768 do {
1769 if (error) {
1770 nd->nd_repstat = error;
1771 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
1772 if (!error && (nd->nd_vers == NFS_VER3)) {
1773 nfsm_chain_add_wcc_data(error, nd, &nmrep,
1774 preattrerr, &preattr, postattrerr, &postattr);
1775 }
1776 } else {
1777 nd->nd_repstat = error;
1778 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_PREOPATTR(nd->nd_vers) +
1779 NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED +
1780 NFSX_WRITEVERF(nd->nd_vers));
1781 if (!error && (nd->nd_vers == NFS_VER3)) {
1782 nfsm_chain_add_wcc_data(error, nd, &nmrep,
1783 preattrerr, &preattr, postattrerr, &postattr);
1784 nfsm_chain_add_32(error, &nmrep, nd->nd_len);
1785 nfsm_chain_add_32(error, &nmrep, nd->nd_stable);
1786 /* write verifier */
1787 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
1788 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
1789 } else if (!error) {
1790 error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
1791 }
1792 }
1793 nfsm_chain_build_done(error, &nmrep);
1794 nfsmerr_if(error);
1795 nd->nd_mrep = nmrep.nmc_mhead;
1796
1797 /*
1798 * Done. Put it at the head of the timer queue so that
1799 * the final phase can return the reply.
1800 */
1801 if (nd != swp) {
1802 nd->nd_time = 1;
1803 LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq);
1804 }
1805 nd = swp->nd_coalesce.lh_first;
1806 if (nd) {
1807 LIST_REMOVE(nd, nd_tq);
1808 }
1809 } while (nd);
1810 swp->nd_time = 1;
1811 LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq);
1812 goto loop1;
1813 }
1814
1815 /*
1816 * Search for a reply to return.
1817 */
1818 for (nd = slp->ns_tq.lh_first; nd; nd = nd->nd_tq.le_next) {
1819 if (nd->nd_mrep) {
1820 LIST_REMOVE(nd, nd_tq);
1821 *mrepp = nd->nd_mrep;
1822 *ndp = nd;
1823 break;
1824 }
1825 }
1826 slp->ns_wgtime = slp->ns_tq.lh_first ? slp->ns_tq.lh_first->nd_time : 0;
1827 lck_mtx_unlock(&slp->ns_wgmutex);
1828
1829 /*
1830 * If we've just created a write pending gather,
1831 * start the timer to check on it soon to make sure
1832 * the write will be completed.
1833 *
1834 * Add/Remove the socket in the nfsrv_sockwg queue as needed.
1835 */
1836 lck_mtx_lock(&nfsd_mutex);
1837 if (slp->ns_wgtime) {
1838 if (slp->ns_wgq.tqe_next == SLPNOLIST) {
1839 TAILQ_INSERT_HEAD(&nfsrv_sockwg, slp, ns_wgq);
1840 }
1841 if (!nfsrv_wg_timer_on) {
1842 nfsrv_wg_timer_on = 1;
1843 nfs_interval_timer_start(nfsrv_wg_timer_call,
1844 NFSRV_WGATHERDELAY);
1845 }
1846 } else if (slp->ns_wgq.tqe_next != SLPNOLIST) {
1847 TAILQ_REMOVE(&nfsrv_sockwg, slp, ns_wgq);
1848 slp->ns_wgq.tqe_next = SLPNOLIST;
1849 }
1850 lck_mtx_unlock(&nfsd_mutex);
1851
1852 return 0;
1853 }
1854
1855 /*
1856 * Coalesce the write request nd into owp. To do this we must:
1857 * - remove nd from the queues
1858 * - merge nd->nd_nmreq into owp->nd_nmreq
1859 * - update the nd_eoff and nd_stable for owp
1860 * - put nd on owp's nd_coalesce list
1861 */
1862 int
nfsrv_wg_coalesce(struct nfsrv_descript * owp,struct nfsrv_descript * nd)1863 nfsrv_wg_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nd)
1864 {
1865 int error;
1866 off_t overlap;
1867 mbuf_t mp, mpnext;
1868 struct nfsrv_descript *p;
1869
1870 LIST_REMOVE(nd, nd_hash);
1871 LIST_REMOVE(nd, nd_tq);
1872 if (owp->nd_eoff < nd->nd_eoff) {
1873 overlap = owp->nd_eoff - nd->nd_off;
1874 if (overlap < 0) {
1875 return EIO;
1876 }
1877 if (overlap > 0) {
1878 mbuf_adj(nd->nd_nmreq.nmc_mhead, (int)overlap);
1879 }
1880 mp = owp->nd_nmreq.nmc_mhead;
1881 while ((mpnext = mbuf_next(mp))) {
1882 mp = mpnext;
1883 }
1884 error = mbuf_setnext(mp, nd->nd_nmreq.nmc_mhead);
1885 if (error) {
1886 return error;
1887 }
1888 owp->nd_eoff = nd->nd_eoff;
1889 } else {
1890 mbuf_freem(nd->nd_nmreq.nmc_mhead);
1891 }
1892 nd->nd_nmreq.nmc_mhead = NULL;
1893 nd->nd_nmreq.nmc_mcur = NULL;
1894 if (nd->nd_stable == NFS_WRITE_FILESYNC) {
1895 owp->nd_stable = NFS_WRITE_FILESYNC;
1896 } else if ((nd->nd_stable == NFS_WRITE_DATASYNC) &&
1897 (owp->nd_stable == NFS_WRITE_UNSTABLE)) {
1898 owp->nd_stable = NFS_WRITE_DATASYNC;
1899 }
1900 LIST_INSERT_HEAD(&owp->nd_coalesce, nd, nd_tq);
1901
1902 /*
1903 * If nd had anything else coalesced into it, transfer them
1904 * to owp, otherwise their replies will never get sent.
1905 */
1906 while ((p = nd->nd_coalesce.lh_first)) {
1907 LIST_REMOVE(p, nd_tq);
1908 LIST_INSERT_HEAD(&owp->nd_coalesce, p, nd_tq);
1909 }
1910 return 0;
1911 }
1912
1913 /*
1914 * Scan the write gathering queues for writes that need to be
1915 * completed now.
1916 */
1917 void
nfsrv_wg_timer(__unused void * param0,__unused void * param1)1918 nfsrv_wg_timer(__unused void *param0, __unused void *param1)
1919 {
1920 struct timeval now;
1921 time_t cur_usec, next_usec;
1922 time_t interval;
1923 struct nfsrv_sock *slp;
1924 int writes_pending = 0;
1925
1926 microuptime(&now);
1927 cur_usec = now.tv_sec * 1000000 + now.tv_usec;
1928 next_usec = cur_usec + (NFSRV_WGATHERDELAY * 1000);
1929
1930 lck_mtx_lock(&nfsd_mutex);
1931 TAILQ_FOREACH(slp, &nfsrv_sockwg, ns_wgq) {
1932 if (slp->ns_wgtime) {
1933 writes_pending++;
1934 if (slp->ns_wgtime <= cur_usec) {
1935 lck_rw_lock_exclusive(&slp->ns_rwlock);
1936 slp->ns_flag |= SLP_DOWRITES;
1937 lck_rw_done(&slp->ns_rwlock);
1938 nfsrv_wakenfsd(slp);
1939 continue;
1940 }
1941 if (slp->ns_wgtime < next_usec) {
1942 next_usec = slp->ns_wgtime;
1943 }
1944 }
1945 }
1946
1947 if (writes_pending == 0) {
1948 nfsrv_wg_timer_on = 0;
1949 lck_mtx_unlock(&nfsd_mutex);
1950 return;
1951 }
1952 lck_mtx_unlock(&nfsd_mutex);
1953
1954 /*
1955 * Return the number of msec to wait again
1956 */
1957 interval = (next_usec - cur_usec) / 1000;
1958 if (interval < 1) {
1959 interval = 1;
1960 }
1961 nfs_interval_timer_start(nfsrv_wg_timer_call, interval);
1962 }
1963
1964 /*
1965 * Sort the group list in increasing numerical order.
1966 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1967 * that used to be here.)
1968 */
1969 void
nfsrv_group_sort(gid_t * list,int num)1970 nfsrv_group_sort(gid_t *list, int num)
1971 {
1972 int i, j;
1973 gid_t v;
1974
1975 /* Insertion sort. */
1976 for (i = 1; i < num; i++) {
1977 v = list[i];
1978 /* find correct slot for value v, moving others up */
1979 for (j = i; --j >= 0 && v < list[j];) {
1980 list[j + 1] = list[j];
1981 }
1982 list[j + 1] = v;
1983 }
1984 }
1985
1986 /*
1987 * nfs create service
1988 * now does a truncate to 0 length via. setattr if it already exists
1989 */
1990 int
nfsrv_create(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)1991 nfsrv_create(
1992 struct nfsrv_descript *nd,
1993 struct nfsrv_sock *slp,
1994 vfs_context_t ctx,
1995 mbuf_t *mrepp)
1996 {
1997 struct vnode_attr dpreattr, dpostattr, postattr;
1998 struct vnode_attr va, *vap = &va;
1999 struct nameidata ni;
2000 int error, dpreattrerr, dpostattrerr, postattrerr;
2001 int how, exclusive_flag;
2002 uint32_t len = 0, cnflags;
2003 uint64_t rdev;
2004 vnode_t vp, dvp, dirp;
2005 struct nfs_filehandle nfh = {};
2006 struct nfs_export *nx = NULL;
2007 struct nfs_export_options *nxo = NULL;
2008 u_quad_t tempsize;
2009 u_char cverf[NFSX_V3CREATEVERF];
2010 uid_t saved_uid;
2011 struct nfsm_chain *nmreq, nmrep;
2012 __unused const int nfs_vers = nd->nd_vers;
2013 assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
2014
2015 error = 0;
2016 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
2017 nmreq = &nd->nd_nmreq;
2018 nfsm_chain_null(&nmrep);
2019 vp = dvp = dirp = NULL;
2020 exclusive_flag = 0;
2021 ni.ni_cnd.cn_nameiop = 0;
2022 rdev = 0;
2023
2024 saved_uid = kauth_cred_getuid(nd->nd_cr);
2025
2026 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
2027 nfsm_chain_get_32(error, nmreq, len);
2028 nfsm_name_len_check(error, nd, len);
2029 nfsmerr_if(error);
2030
2031 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
2032 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
2033 if (!error) {
2034 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
2035 if (nx != NULL) {
2036 /* update export stats */
2037 NFSStatAdd64(&nx->nx_stats.ops, 1);
2038
2039 /* update active user stats */
2040 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
2041 }
2042 }
2043 if (dirp) {
2044 if (nd->nd_vers == NFS_VER3) {
2045 nfsm_srv_pre_vattr_init(&dpreattr);
2046 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
2047 } else {
2048 vnode_put(dirp);
2049 dirp = NULL;
2050 }
2051 }
2052
2053 if (error) {
2054 ni.ni_cnd.cn_nameiop = 0;
2055 goto nfsmerr;
2056 }
2057
2058 dvp = ni.ni_dvp;
2059 vp = ni.ni_vp;
2060 VATTR_INIT(vap);
2061
2062 if (nd->nd_vers == NFS_VER3) {
2063 nfsm_chain_get_32(error, nmreq, how);
2064 nfsmerr_if(error);
2065 switch (how) {
2066 case NFS_CREATE_GUARDED:
2067 if (vp) {
2068 error = EEXIST;
2069 break;
2070 }
2071 OS_FALLTHROUGH;
2072 case NFS_CREATE_UNCHECKED:
2073 error = nfsm_chain_get_sattr(nd, nmreq, vap);
2074 break;
2075 case NFS_CREATE_EXCLUSIVE:
2076 nfsm_chain_get_opaque(error, nmreq, NFSX_V3CREATEVERF, cverf);
2077 exclusive_flag = 1;
2078 if (vp == NULL) {
2079 VATTR_SET(vap, va_mode, 0);
2080 }
2081 break;
2082 }
2083 ;
2084 VATTR_SET(vap, va_type, VREG);
2085 } else {
2086 enum vtype v_type;
2087
2088 error = nfsm_chain_get_sattr(nd, nmreq, vap);
2089 nfsmerr_if(error);
2090 v_type = vap->va_type;
2091 if (v_type == VNON) {
2092 v_type = VREG;
2093 }
2094 VATTR_SET(vap, va_type, v_type);
2095
2096 switch (v_type) {
2097 case VCHR:
2098 case VBLK:
2099 case VFIFO:
2100 rdev = vap->va_data_size;
2101 VATTR_CLEAR_ACTIVE(vap, va_data_size);
2102 break;
2103 default:
2104 break;
2105 }
2106 ;
2107 }
2108 nfsmerr_if(error);
2109
2110 /*
2111 * If it doesn't exist, create it
2112 * otherwise just truncate to 0 length
2113 * should I set the mode too ??
2114 */
2115 if (vp == NULL) {
2116 kauth_acl_t xacl = NULL;
2117
2118 /* authorize before creating */
2119 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
2120
2121 /* construct ACL and handle inheritance */
2122 if (!error) {
2123 error = kauth_acl_inherit(dvp,
2124 NULL,
2125 &xacl,
2126 0 /* !isdir */,
2127 ctx);
2128
2129 if (!error && xacl != NULL) {
2130 VATTR_SET(vap, va_acl, xacl);
2131 }
2132 }
2133 VATTR_CLEAR_ACTIVE(vap, va_data_size);
2134 VATTR_CLEAR_ACTIVE(vap, va_access_time);
2135 /*
2136 * Server policy is to alway use the mapped rpc credential for
2137 * file system object creation. This has the nice side effect of
2138 * enforcing BSD creation semantics
2139 */
2140 VATTR_CLEAR_ACTIVE(vap, va_uid);
2141 VATTR_CLEAR_ACTIVE(vap, va_gid);
2142
2143 /* validate new-file security information */
2144 if (!error) {
2145 error = vnode_authattr_new(dvp, vap, 0, ctx);
2146 }
2147
2148 if (!error) {
2149 error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
2150 if (error) {
2151 error = EACCES;
2152 }
2153 }
2154
2155 if (vap->va_type == VREG || vap->va_type == VSOCK) {
2156 if (!error) {
2157 error = VNOP_CREATE(dvp, &vp, &ni.ni_cnd, vap, ctx);
2158 }
2159
2160 if (!error && !VATTR_ALL_SUPPORTED(vap)) {
2161 /*
2162 * If some of the requested attributes weren't handled by the VNOP,
2163 * use our fallback code.
2164 */
2165 error = vnode_setattr_fallback(vp, vap, ctx);
2166 }
2167
2168 if (xacl != NULL) {
2169 kauth_acl_free(xacl);
2170 }
2171
2172 if (!error) {
2173 if (exclusive_flag) {
2174 exclusive_flag = 0;
2175 VATTR_INIT(vap);
2176 bcopy(cverf, (caddr_t)&vap->va_access_time,
2177 NFSX_V3CREATEVERF);
2178 VATTR_SET_ACTIVE(vap, va_access_time);
2179 // skip authorization, as this is an
2180 // NFS internal implementation detail.
2181 error = vnode_setattr(vp, vap, ctx);
2182 }
2183
2184 #if CONFIG_FSE
2185 if (nfsrv_fsevents_enabled && need_fsevent(FSE_CREATE_FILE, vp)) {
2186 add_fsevent(FSE_CREATE_FILE, ctx,
2187 FSE_ARG_VNODE, vp,
2188 FSE_ARG_DONE);
2189 }
2190 #endif
2191 }
2192 } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
2193 vap->va_type == VFIFO) {
2194 if (vap->va_type == VCHR && rdev == 0xffffffff) {
2195 VATTR_SET(vap, va_type, VFIFO);
2196 }
2197 if (vap->va_type != VFIFO) {
2198 error = suser(nd->nd_cr, NULL);
2199 nfsmerr_if(error);
2200 }
2201 VATTR_SET(vap, va_rdev, (dev_t)rdev);
2202
2203 error = VNOP_MKNOD(dvp, &vp, &ni.ni_cnd, vap, ctx);
2204
2205 if (xacl != NULL) {
2206 kauth_acl_free(xacl);
2207 }
2208
2209 nfsmerr_if(error);
2210
2211 if (vp) {
2212 vnode_recycle(vp);
2213 vnode_put(vp);
2214 vp = NULL;
2215 }
2216 ni.ni_cnd.cn_nameiop = LOOKUP;
2217 #if CONFIG_TRIGGERS
2218 ni.ni_op = OP_LOOKUP;
2219 #endif
2220 ni.ni_cnd.cn_flags &= ~LOCKPARENT;
2221 ni.ni_cnd.cn_context = ctx;
2222 ni.ni_startdir = dvp;
2223 ni.ni_usedvp = dvp;
2224 ni.ni_rootdir = rootvnode;
2225 cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
2226 while ((error = lookup(&ni)) == ERECYCLE) {
2227 ni.ni_cnd.cn_flags = cnflags;
2228 ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
2229 ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
2230 }
2231 if (!error) {
2232 if (ni.ni_cnd.cn_flags & ISSYMLINK) {
2233 error = EINVAL;
2234 }
2235 vp = ni.ni_vp;
2236 }
2237 nfsmerr_if(error);
2238 } else {
2239 error = ENXIO;
2240 }
2241 /*
2242 * nameidone has to happen before we vnode_put(dvp)
2243 * since it may need to release the fs_nodelock on the dvp
2244 */
2245 nameidone(&ni);
2246 ni.ni_cnd.cn_nameiop = 0;
2247
2248 vnode_put(dvp);
2249 } else {
2250 /*
2251 * nameidone has to happen before we vnode_put(dvp)
2252 * since it may need to release the fs_nodelock on the dvp
2253 */
2254 nameidone(&ni);
2255 ni.ni_cnd.cn_nameiop = 0;
2256
2257 vnode_put(dvp);
2258
2259 #if CONFIG_MACF
2260 if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
2261 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
2262 error = mac_vnode_check_truncate(ctx, NOCRED, vp);
2263 if (error) {
2264 error = EACCES;
2265 }
2266 }
2267 #endif
2268 if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
2269 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA,
2270 ctx, nxo, 0);
2271 if (!error) {
2272 tempsize = vap->va_data_size;
2273 VATTR_INIT(vap);
2274 VATTR_SET(vap, va_data_size, tempsize);
2275 error = vnode_setattr(vp, vap, ctx);
2276 }
2277 }
2278 }
2279 if (!error) {
2280 error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh);
2281 if (!error) {
2282 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
2283 postattrerr = vnode_getattr(vp, &postattr, ctx);
2284 if (nd->nd_vers == NFS_VER2) {
2285 error = postattrerr;
2286 }
2287 }
2288 }
2289 if (vp) {
2290 vnode_put(vp);
2291 }
2292
2293 if (nd->nd_vers == NFS_VER3) {
2294 if (exclusive_flag && !error &&
2295 bcmp(cverf, &postattr.va_access_time, NFSX_V3CREATEVERF)) {
2296 error = EEXIST;
2297 }
2298 nfsm_srv_vattr_init(&dpostattr, NFS_VER3);
2299 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
2300 vnode_put(dirp);
2301 dirp = NULL;
2302 }
2303
2304 nfsmerr:
2305 /* assemble reply */
2306 nd->nd_repstat = error;
2307 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
2308 NFSX_FATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
2309 assert(nfs_vers == nd->nd_vers);
2310 nfsmout_if(error);
2311 *mrepp = nmrep.nmc_mhead;
2312 nfsmout_on_status(nd, error);
2313 if (nd->nd_vers == NFS_VER3) {
2314 if (!nd->nd_repstat) {
2315 if (!nfh.nfh_fhp) {
2316 error = NFSERR_SERVERFAULT;
2317 goto nfsmerr;
2318 }
2319 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
2320 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
2321 }
2322 nfsm_chain_add_wcc_data(error, nd, &nmrep,
2323 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
2324 } else {
2325 nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len);
2326 if (!error) {
2327 error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
2328 }
2329 }
2330 nfsmout:
2331 nfsm_chain_build_done(error, &nmrep);
2332 if (ni.ni_cnd.cn_nameiop) {
2333 /*
2334 * nameidone has to happen before we vnode_put(dvp)
2335 * since it may need to release the fs_nodelock on the dvp
2336 */
2337 nameidone(&ni);
2338
2339 if (vp) {
2340 vnode_put(vp);
2341 }
2342 vnode_put(dvp);
2343 }
2344 if (dirp) {
2345 vnode_put(dirp);
2346 }
2347 if (error) {
2348 nfsm_chain_cleanup(&nmrep);
2349 *mrepp = NULL;
2350 }
2351 return error;
2352 }
2353
2354 /*
2355 * nfs v3 mknod service
2356 */
2357 int
nfsrv_mknod(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)2358 nfsrv_mknod(
2359 struct nfsrv_descript *nd,
2360 struct nfsrv_sock *slp,
2361 vfs_context_t ctx,
2362 mbuf_t *mrepp)
2363 {
2364 struct vnode_attr dpreattr, dpostattr, postattr;
2365 struct vnode_attr va, *vap = &va;
2366 struct nameidata ni;
2367 int error, dpreattrerr, dpostattrerr, postattrerr;
2368 uint32_t len = 0, cnflags;
2369 u_int32_t major = 0, minor = 0;
2370 enum vtype vtyp;
2371 nfstype nvtype;
2372 vnode_t vp, dvp, dirp;
2373 struct nfs_filehandle nfh = {};
2374 struct nfs_export *nx = NULL;
2375 struct nfs_export_options *nxo = NULL;
2376 uid_t saved_uid;
2377 kauth_acl_t xacl = NULL;
2378 struct nfsm_chain *nmreq, nmrep;
2379
2380 error = 0;
2381 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
2382 nmreq = &nd->nd_nmreq;
2383 nfsm_chain_null(&nmrep);
2384 vp = dvp = dirp = NULL;
2385 ni.ni_cnd.cn_nameiop = 0;
2386
2387 saved_uid = kauth_cred_getuid(nd->nd_cr);
2388
2389 nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
2390 nfsm_chain_get_32(error, nmreq, len);
2391 nfsm_name_len_check(error, nd, len);
2392 nfsmerr_if(error);
2393
2394 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
2395 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
2396 if (!error) {
2397 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
2398 if (nx != NULL) {
2399 /* update export stats */
2400 NFSStatAdd64(&nx->nx_stats.ops, 1);
2401
2402 /* update active user stats */
2403 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
2404 }
2405 }
2406 if (dirp) {
2407 nfsm_srv_pre_vattr_init(&dpreattr);
2408 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
2409 }
2410 if (error) {
2411 ni.ni_cnd.cn_nameiop = 0;
2412 goto nfsmerr;
2413 }
2414
2415 dvp = ni.ni_dvp;
2416 vp = ni.ni_vp;
2417
2418 nfsm_chain_get_32(error, nmreq, nvtype);
2419 nfsmerr_if(error);
2420 vtyp = nfstov_type(nvtype, NFS_VER3);
2421 if (!error && (vtyp != VCHR) && (vtyp != VBLK) && (vtyp != VSOCK) && (vtyp != VFIFO)) {
2422 error = NFSERR_BADTYPE;
2423 goto out;
2424 }
2425
2426 VATTR_INIT(vap);
2427 error = nfsm_chain_get_sattr(nd, nmreq, vap);
2428 if ((vtyp == VCHR) || (vtyp == VBLK)) {
2429 nfsm_chain_get_32(error, nmreq, major);
2430 nfsm_chain_get_32(error, nmreq, minor);
2431 nfsmerr_if(error);
2432 VATTR_SET(vap, va_rdev, makedev(major, minor));
2433 }
2434 nfsmerr_if(error);
2435
2436 /*
2437 * If it doesn't exist, create it.
2438 */
2439 if (vp) {
2440 error = EEXIST;
2441 goto out;
2442 }
2443 VATTR_SET(vap, va_type, vtyp);
2444
2445 /* authorize before creating */
2446 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
2447
2448 /* construct ACL and handle inheritance */
2449 if (!error) {
2450 error = kauth_acl_inherit(dvp,
2451 NULL,
2452 &xacl,
2453 0 /* !isdir */,
2454 ctx);
2455
2456 if (!error && xacl != NULL) {
2457 VATTR_SET(vap, va_acl, xacl);
2458 }
2459 }
2460 VATTR_CLEAR_ACTIVE(vap, va_data_size);
2461 VATTR_CLEAR_ACTIVE(vap, va_access_time);
2462 /*
2463 * Server policy is to alway use the mapped rpc credential for
2464 * file system object creation. This has the nice side effect of
2465 * enforcing BSD creation semantics
2466 */
2467 VATTR_CLEAR_ACTIVE(vap, va_uid);
2468 VATTR_CLEAR_ACTIVE(vap, va_gid);
2469
2470 /* validate new-file security information */
2471 if (!error) {
2472 error = vnode_authattr_new(dvp, vap, 0, ctx);
2473 }
2474 if (!error) {
2475 error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
2476 if (error) {
2477 error = EACCES;
2478 }
2479 }
2480 if (error) {
2481 goto out1;
2482 }
2483
2484 if (vtyp == VSOCK) {
2485 error = VNOP_CREATE(dvp, &vp, &ni.ni_cnd, vap, ctx);
2486
2487 if (!error && !VATTR_ALL_SUPPORTED(vap)) {
2488 /*
2489 * If some of the requested attributes weren't handled by the VNOP,
2490 * use our fallback code.
2491 */
2492 error = vnode_setattr_fallback(vp, vap, ctx);
2493 }
2494 } else {
2495 if (vtyp != VFIFO && (error = suser(nd->nd_cr, (u_short *)0))) {
2496 goto out1;
2497 }
2498 if ((error = VNOP_MKNOD(dvp, &vp, &ni.ni_cnd, vap, ctx))) {
2499 goto out1;
2500 }
2501 if (vp) {
2502 vnode_recycle(vp);
2503 vnode_put(vp);
2504 vp = NULL;
2505 }
2506 ni.ni_cnd.cn_nameiop = LOOKUP;
2507 #if CONFIG_TRIGGERS
2508 ni.ni_op = OP_LOOKUP;
2509 #endif
2510 ni.ni_cnd.cn_flags &= ~LOCKPARENT;
2511 ni.ni_cnd.cn_context = vfs_context_current();
2512 ni.ni_startdir = dvp;
2513 ni.ni_usedvp = dvp;
2514 ni.ni_rootdir = rootvnode;
2515 cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
2516 while ((error = lookup(&ni)) == ERECYCLE) {
2517 ni.ni_cnd.cn_flags = cnflags;
2518 ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
2519 ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
2520 }
2521 if (!error) {
2522 vp = ni.ni_vp;
2523 if (ni.ni_cnd.cn_flags & ISSYMLINK) {
2524 error = EINVAL;
2525 }
2526 }
2527 }
2528 out1:
2529 if (xacl != NULL) {
2530 kauth_acl_free(xacl);
2531 }
2532 out:
2533 /*
2534 * nameidone has to happen before we vnode_put(dvp)
2535 * since it may need to release the fs_nodelock on the dvp
2536 */
2537 nameidone(&ni);
2538 ni.ni_cnd.cn_nameiop = 0;
2539
2540 vnode_put(dvp);
2541 dvp = NULL;
2542
2543 if (!error) {
2544 error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh);
2545 if (!error) {
2546 nfsm_srv_vattr_init(&postattr, NFS_VER3);
2547 postattrerr = vnode_getattr(vp, &postattr, ctx);
2548 }
2549 }
2550 if (vp) {
2551 vnode_put(vp);
2552 vp = NULL;
2553 }
2554
2555 nfsm_srv_vattr_init(&dpostattr, NFS_VER3);
2556 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
2557 vnode_put(dirp);
2558 dirp = NULL;
2559
2560 nfsmerr:
2561 /* assemble reply */
2562 nd->nd_repstat = error;
2563 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(NFS_VER3, &nfh) +
2564 NFSX_POSTOPATTR(NFS_VER3) + NFSX_WCCDATA(NFS_VER3));
2565 nfsmout_if(error);
2566 *mrepp = nmrep.nmc_mhead;
2567 nfsmout_on_status(nd, error);
2568 if (!nd->nd_repstat) {
2569 if (!nfh.nfh_fhp) {
2570 error = NFSERR_SERVERFAULT;
2571 goto nfsmerr;
2572 }
2573 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
2574 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
2575 }
2576 nfsm_chain_add_wcc_data(error, nd, &nmrep,
2577 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
2578 nfsmout:
2579 nfsm_chain_build_done(error, &nmrep);
2580 if (ni.ni_cnd.cn_nameiop) {
2581 /*
2582 * nameidone has to happen before we vnode_put(dvp)
2583 * since it may need to release the fs_nodelock on the dvp
2584 */
2585 nameidone(&ni);
2586
2587 if (vp) {
2588 vnode_put(vp);
2589 }
2590 vnode_put(dvp);
2591 }
2592 if (dvp) {
2593 vnode_put(dvp);
2594 }
2595 if (vp) {
2596 vnode_put(vp);
2597 }
2598 if (dirp) {
2599 vnode_put(dirp);
2600 }
2601 if (error) {
2602 nfsm_chain_cleanup(&nmrep);
2603 *mrepp = NULL;
2604 }
2605 return error;
2606 }
2607
2608 /*
2609 * nfs remove service
2610 */
2611 int
nfsrv_remove(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)2612 nfsrv_remove(
2613 struct nfsrv_descript *nd,
2614 struct nfsrv_sock *slp,
2615 vfs_context_t ctx,
2616 mbuf_t *mrepp)
2617 {
2618 struct nameidata ni;
2619 int error, dpreattrerr, dpostattrerr;
2620 uint32_t len = 0;
2621 uid_t saved_uid;
2622 vnode_t vp, dvp, dirp = NULL;
2623 struct vnode_attr dpreattr, dpostattr;
2624 struct nfs_filehandle nfh;
2625 struct nfs_export *nx = NULL;
2626 struct nfs_export_options *nxo = NULL;
2627 struct nfsm_chain *nmreq, nmrep;
2628
2629 error = 0;
2630 dpreattrerr = dpostattrerr = ENOENT;
2631 saved_uid = kauth_cred_getuid(nd->nd_cr);
2632 dvp = vp = dirp = NULL;
2633 nmreq = &nd->nd_nmreq;
2634 nfsm_chain_null(&nmrep);
2635
2636 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
2637 nfsm_chain_get_32(error, nmreq, len);
2638 nfsm_name_len_check(error, nd, len);
2639 nfsmerr_if(error);
2640
2641 NDINIT(&ni, DELETE, OP_UNLINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
2642 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
2643 if (!error) {
2644 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
2645 if (nx != NULL) {
2646 /* update export stats */
2647 NFSStatAdd64(&nx->nx_stats.ops, 1);
2648
2649 /* update active user stats */
2650 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
2651 }
2652 }
2653 if (dirp) {
2654 if (nd->nd_vers == NFS_VER3) {
2655 nfsm_srv_pre_vattr_init(&dpreattr);
2656 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
2657 } else {
2658 vnode_put(dirp);
2659 dirp = NULL;
2660 }
2661 }
2662
2663 if (!error) {
2664 dvp = ni.ni_dvp;
2665 vp = ni.ni_vp;
2666
2667 if (vnode_vtype(vp) == VDIR) {
2668 error = EPERM; /* POSIX */
2669 } else if (vnode_isvroot(vp)) {
2670 /*
2671 * The root of a mounted filesystem cannot be deleted.
2672 */
2673 error = EBUSY;
2674 } else {
2675 error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0);
2676 }
2677
2678 if (!error) {
2679 error = vn_authorize_unlink(dvp, vp, &ni.ni_cnd, ctx, NULL);
2680 if (error) {
2681 error = EACCES;
2682 }
2683 }
2684
2685 if (!error) {
2686 #if CONFIG_FSE
2687 char *path = NULL;
2688 int plen = 0;
2689 fse_info finfo;
2690
2691 if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) {
2692 plen = MAXPATHLEN;
2693 if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) {
2694 get_fse_info(vp, &finfo, ctx);
2695 } else if (path) {
2696 release_pathbuff(path);
2697 path = NULL;
2698 }
2699 }
2700 #endif
2701 error = VNOP_REMOVE(dvp, vp, &ni.ni_cnd, 0, ctx);
2702
2703 #if CONFIG_FSE
2704 if (path) {
2705 if (!error) {
2706 add_fsevent(FSE_DELETE, ctx,
2707 FSE_ARG_STRING, plen, path,
2708 FSE_ARG_FINFO, &finfo,
2709 FSE_ARG_DONE);
2710 }
2711 release_pathbuff(path);
2712 }
2713 #endif
2714 }
2715
2716 /*
2717 * nameidone has to happen before we vnode_put(dvp)
2718 * since it may need to release the fs_nodelock on the dvp
2719 */
2720 nameidone(&ni);
2721
2722 vnode_put(vp);
2723 vnode_put(dvp);
2724 }
2725
2726 nfsmerr:
2727 if (dirp) {
2728 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
2729 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
2730 vnode_put(dirp);
2731 }
2732
2733 /* assemble reply */
2734 nd->nd_repstat = error;
2735 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
2736 nfsmout_if(error);
2737 *mrepp = nmrep.nmc_mhead;
2738 nfsmout_on_status(nd, error);
2739 if (nd->nd_vers == NFS_VER3) {
2740 nfsm_chain_add_wcc_data(error, nd, &nmrep,
2741 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
2742 }
2743 nfsmout:
2744 nfsm_chain_build_done(error, &nmrep);
2745 if (error) {
2746 nfsm_chain_cleanup(&nmrep);
2747 *mrepp = NULL;
2748 }
2749 return error;
2750 }
2751
2752 /*
2753 * nfs rename service
2754 */
2755 int
nfsrv_rename(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)2756 nfsrv_rename(
2757 struct nfsrv_descript *nd,
2758 struct nfsrv_sock *slp,
2759 vfs_context_t ctx,
2760 mbuf_t *mrepp)
2761 {
2762 kauth_cred_t saved_cred = NULL;
2763 uid_t saved_uid;
2764 int error;
2765 uint32_t fromlen, tolen;
2766 int fdpreattrerr, fdpostattrerr;
2767 int tdpreattrerr, tdpostattrerr;
2768 char *frompath = NULL, *topath = NULL;
2769 struct nameidata fromni, toni;
2770 vnode_t fvp, tvp, tdvp, fdvp, fdirp, tdirp;
2771 struct vnode_attr fdpreattr, fdpostattr;
2772 struct vnode_attr tdpreattr, tdpostattr;
2773 struct nfs_filehandle fnfh, tnfh;
2774 struct nfs_export *fnx, *tnx;
2775 struct nfs_export_options *fnxo, *tnxo;
2776 enum vtype fvtype, tvtype;
2777 int holding_mntlock;
2778 mount_t locked_mp;
2779 struct nfsm_chain *nmreq, nmrep;
2780 char *from_name, *to_name;
2781 #if CONFIG_FSE
2782 int from_len = 0, to_len = 0;
2783 fse_info from_finfo, to_finfo;
2784 #endif
2785 u_char didstats = 0;
2786 const char *oname;
2787
2788 error = 0;
2789 fdpreattrerr = fdpostattrerr = ENOENT;
2790 tdpreattrerr = tdpostattrerr = ENOENT;
2791 saved_uid = kauth_cred_getuid(nd->nd_cr);
2792 fromlen = tolen = 0;
2793 frompath = topath = NULL;
2794 fdirp = tdirp = NULL;
2795 nmreq = &nd->nd_nmreq;
2796 nfsm_chain_null(&nmrep);
2797
2798 /*
2799 * these need to be set before calling any code
2800 * that they may take us out through the error path.
2801 */
2802 holding_mntlock = 0;
2803 fvp = tvp = NULL;
2804 fdvp = tdvp = NULL;
2805 locked_mp = NULL;
2806
2807 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, fnfh.nfh_fhp, fnfh.nfh_len);
2808 nfsm_chain_get_32(error, nmreq, fromlen);
2809 nfsm_name_len_check(error, nd, fromlen);
2810 nfsmerr_if(error);
2811 error = nfsm_chain_get_path_namei(nmreq, fromlen, &fromni);
2812 nfsmerr_if(error);
2813 frompath = fromni.ni_cnd.cn_pnbuf;
2814
2815 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, tnfh.nfh_fhp, tnfh.nfh_len);
2816 nfsm_chain_get_32(error, nmreq, tolen);
2817 nfsm_name_len_check(error, nd, tolen);
2818 nfsmerr_if(error);
2819 error = nfsm_chain_get_path_namei(nmreq, tolen, &toni);
2820 nfsmerr_if(error);
2821 topath = toni.ni_cnd.cn_pnbuf;
2822
2823 /*
2824 * Remember our original uid so that we can reset cr_uid before
2825 * the second nfsrv_namei() call, in case it is remapped.
2826 */
2827 saved_cred = nd->nd_cr;
2828 kauth_cred_ref(saved_cred);
2829 retry:
2830 NDINIT(&fromni, DELETE, OP_UNLINK, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(frompath), ctx);
2831 fromni.ni_cnd.cn_pnbuf = frompath;
2832 frompath = NULL;
2833 fromni.ni_cnd.cn_pnlen = MAXPATHLEN;
2834 fromni.ni_cnd.cn_flags |= HASBUF;
2835
2836 error = nfsrv_namei(nd, ctx, &fromni, &fnfh, &fdirp, &fnx, &fnxo);
2837 if (error) {
2838 goto out;
2839 }
2840 fdvp = fromni.ni_dvp;
2841 fvp = fromni.ni_vp;
2842
2843 if (fdirp) {
2844 if (nd->nd_vers == NFS_VER3) {
2845 nfsm_srv_pre_vattr_init(&fdpreattr);
2846 fdpreattrerr = vnode_getattr(fdirp, &fdpreattr, ctx);
2847 } else {
2848 vnode_put(fdirp);
2849 fdirp = NULL;
2850 }
2851 }
2852 fvtype = vnode_vtype(fvp);
2853
2854 /* reset credential if it was remapped */
2855 if (nd->nd_cr != saved_cred) {
2856 kauth_cred_ref(saved_cred);
2857 kauth_cred_unref(&nd->nd_cr);
2858 ctx->vc_ucred = nd->nd_cr = saved_cred;
2859 }
2860
2861 NDINIT(&toni, RENAME, OP_RENAME, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(topath), ctx);
2862 toni.ni_cnd.cn_pnbuf = topath;
2863 topath = NULL;
2864 toni.ni_cnd.cn_pnlen = MAXPATHLEN;
2865 toni.ni_cnd.cn_flags |= HASBUF;
2866
2867 if (fvtype == VDIR) {
2868 toni.ni_cnd.cn_flags |= WILLBEDIR;
2869 }
2870
2871 tnx = NULL;
2872 error = nfsrv_namei(nd, ctx, &toni, &tnfh, &tdirp, &tnx, &tnxo);
2873 if (error) {
2874 /*
2875 * Translate error code for rename("dir1", "dir2/.").
2876 */
2877 if (error == EISDIR && fvtype == VDIR) {
2878 if (nd->nd_vers == NFS_VER3) {
2879 error = EINVAL;
2880 } else {
2881 error = ENOTEMPTY;
2882 }
2883 }
2884 goto out;
2885 }
2886 tdvp = toni.ni_dvp;
2887 tvp = toni.ni_vp;
2888
2889 if (!didstats) {
2890 /* update export stats once only */
2891 if (tnx != NULL) {
2892 /* update export stats */
2893 NFSStatAdd64(&tnx->nx_stats.ops, 1);
2894
2895 /* update active user stats */
2896 nfsrv_update_user_stat(tnx, nd, saved_uid, 1, 0, 0);
2897 didstats = 1;
2898 }
2899 }
2900
2901 if (tdirp) {
2902 if (nd->nd_vers == NFS_VER3) {
2903 nfsm_srv_pre_vattr_init(&tdpreattr);
2904 tdpreattrerr = vnode_getattr(tdirp, &tdpreattr, ctx);
2905 } else {
2906 vnode_put(tdirp);
2907 tdirp = NULL;
2908 }
2909 }
2910
2911 if (tvp != NULL) {
2912 tvtype = vnode_vtype(tvp);
2913
2914 if (fvtype == VDIR && tvtype != VDIR) {
2915 if (nd->nd_vers == NFS_VER3) {
2916 error = EEXIST;
2917 } else {
2918 error = EISDIR;
2919 }
2920 goto out;
2921 } else if (fvtype != VDIR && tvtype == VDIR) {
2922 if (nd->nd_vers == NFS_VER3) {
2923 error = EEXIST;
2924 } else {
2925 error = ENOTDIR;
2926 }
2927 goto out;
2928 }
2929 if (tvtype == VDIR && vnode_mountedhere(tvp)) {
2930 if (nd->nd_vers == NFS_VER3) {
2931 error = EXDEV;
2932 } else {
2933 error = ENOTEMPTY;
2934 }
2935 goto out;
2936 }
2937 }
2938 if (fvp == tdvp) {
2939 if (nd->nd_vers == NFS_VER3) {
2940 error = EINVAL;
2941 } else {
2942 error = ENOTEMPTY;
2943 }
2944 goto out;
2945 }
2946
2947 /*
2948 * Authorization.
2949 *
2950 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2951 * the node is moving between directories and we need rights to remove from the
2952 * old and add to the new.
2953 *
2954 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2955 *
2956 * Note that we do not inherit when renaming. XXX this needs to be revisited to
2957 * implement the deferred-inherit bit.
2958 */
2959 {
2960 int moving = 0;
2961
2962 error = 0;
2963 if ((tvp != NULL) && vnode_isdir(tvp)) {
2964 if (tvp != fdvp) {
2965 moving = 1;
2966 }
2967 } else if (tdvp != fdvp) {
2968 moving = 1;
2969 }
2970 if (moving) {
2971 /* moving out of fdvp, must have delete rights */
2972 if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, ctx, fnxo, 0)) != 0) {
2973 goto auth_exit;
2974 }
2975 /* moving into tdvp or tvp, must have rights to add */
2976 if ((error = nfsrv_authorize(((tvp != NULL) && vnode_isdir(tvp)) ? tvp : tdvp,
2977 NULL,
2978 vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2979 ctx, tnxo, 0)) != 0) {
2980 goto auth_exit;
2981 }
2982 } else {
2983 /* node staying in same directory, must be allowed to add new name */
2984 if ((error = nfsrv_authorize(fdvp, NULL,
2985 vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2986 ctx, fnxo, 0)) != 0) {
2987 goto auth_exit;
2988 }
2989 }
2990 /* overwriting tvp */
2991 if ((tvp != NULL) && !vnode_isdir(tvp) &&
2992 ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, ctx, tnxo, 0)) != 0)) {
2993 goto auth_exit;
2994 }
2995
2996 if (!error &&
2997 ((error = vn_authorize_rename(fdvp, fvp, &fromni.ni_cnd, tdvp, tvp, &toni.ni_cnd, ctx, NULL)) != 0)) {
2998 if (error) {
2999 error = EACCES;
3000 }
3001 goto auth_exit;
3002 }
3003 /* XXX more checks? */
3004
3005 auth_exit:
3006 /* authorization denied */
3007 if (error != 0) {
3008 goto out;
3009 }
3010 }
3011
3012 if ((vnode_mount(fvp) != vnode_mount(tdvp)) ||
3013 (tvp && (vnode_mount(fvp) != vnode_mount(tvp)))) {
3014 if (nd->nd_vers == NFS_VER3) {
3015 error = EXDEV;
3016 } else {
3017 error = ENOTEMPTY;
3018 }
3019 goto out;
3020 }
3021 /*
3022 * The following edge case is caught here:
3023 * (to cannot be a descendent of from)
3024 *
3025 * o fdvp
3026 * /
3027 * /
3028 * o fvp
3029 * \
3030 * \
3031 * o tdvp
3032 * /
3033 * /
3034 * o tvp
3035 */
3036 if (tdvp->v_parent == fvp) {
3037 if (nd->nd_vers == NFS_VER3) {
3038 error = EXDEV;
3039 } else {
3040 error = ENOTEMPTY;
3041 }
3042 goto out;
3043 }
3044 if (fvtype == VDIR && vnode_mountedhere(fvp)) {
3045 if (nd->nd_vers == NFS_VER3) {
3046 error = EXDEV;
3047 } else {
3048 error = ENOTEMPTY;
3049 }
3050 goto out;
3051 }
3052 /*
3053 * If source is the same as the destination (that is the
3054 * same vnode) then there is nothing to do...
3055 * EXCEPT if the underlying file system supports case
3056 * insensitivity and is case preserving. In this case
3057 * the file system needs to handle the special case of
3058 * getting the same vnode as target (fvp) and source (tvp).
3059 *
3060 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
3061 * and _PC_CASE_PRESERVING can have this exception, and they need to
3062 * handle the special case of getting the same vnode as target and
3063 * source. NOTE: Then the target is unlocked going into vnop_rename,
3064 * so not to cause locking problems. There is a single reference on tvp.
3065 *
3066 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
3067 * that correct behaviour then is just to remove the source (link)
3068 */
3069 if ((fvp == tvp) && (fdvp == tdvp)) {
3070 if (fromni.ni_cnd.cn_namelen == toni.ni_cnd.cn_namelen &&
3071 !bcmp(fromni.ni_cnd.cn_nameptr, toni.ni_cnd.cn_nameptr,
3072 fromni.ni_cnd.cn_namelen)) {
3073 goto out;
3074 }
3075 }
3076
3077 if (holding_mntlock && vnode_mount(fvp) != locked_mp) {
3078 /*
3079 * we're holding a reference and lock
3080 * on locked_mp, but it no longer matches
3081 * what we want to do... so drop our hold
3082 */
3083 mount_unlock_renames(locked_mp);
3084 mount_drop(locked_mp, 0);
3085 holding_mntlock = 0;
3086 }
3087 if (tdvp != fdvp && fvtype == VDIR) {
3088 /*
3089 * serialize renames that re-shape
3090 * the tree... if holding_mntlock is
3091 * set, then we're ready to go...
3092 * otherwise we
3093 * first need to drop the iocounts
3094 * we picked up, second take the
3095 * lock to serialize the access,
3096 * then finally start the lookup
3097 * process over with the lock held
3098 */
3099 if (!holding_mntlock) {
3100 /*
3101 * need to grab a reference on
3102 * the mount point before we
3103 * drop all the iocounts... once
3104 * the iocounts are gone, the mount
3105 * could follow
3106 */
3107 locked_mp = vnode_mount(fvp);
3108 mount_ref(locked_mp, 0);
3109
3110 /* make a copy of to path to pass to nfsrv_namei() again */
3111 topath = zalloc(ZV_NAMEI);
3112 bcopy(toni.ni_cnd.cn_pnbuf, topath, tolen + 1);
3113
3114 /*
3115 * nameidone has to happen before we vnode_put(tdvp)
3116 * since it may need to release the fs_nodelock on the tdvp
3117 */
3118 nameidone(&toni);
3119
3120 if (tvp) {
3121 vnode_put(tvp);
3122 }
3123 vnode_put(tdvp);
3124
3125 /* make a copy of from path to pass to nfsrv_namei() again */
3126 frompath = zalloc(ZV_NAMEI);
3127 bcopy(fromni.ni_cnd.cn_pnbuf, frompath, fromlen + 1);
3128
3129 /*
3130 * nameidone has to happen before we vnode_put(fdvp)
3131 * since it may need to release the fs_nodelock on the fdvp
3132 */
3133 nameidone(&fromni);
3134
3135 vnode_put(fvp);
3136 vnode_put(fdvp);
3137
3138 if (fdirp) {
3139 vnode_put(fdirp);
3140 fdirp = NULL;
3141 }
3142 if (tdirp) {
3143 vnode_put(tdirp);
3144 tdirp = NULL;
3145 }
3146 mount_lock_renames(locked_mp);
3147 holding_mntlock = 1;
3148
3149 fvp = tvp = NULL;
3150 fdvp = tdvp = NULL;
3151
3152 fdpreattrerr = tdpreattrerr = ENOENT;
3153
3154 if (!topath || !frompath) {
3155 /* we couldn't allocate a path, so bail */
3156 error = ENOMEM;
3157 goto out;
3158 }
3159
3160 /* reset credential if it was remapped */
3161 if (nd->nd_cr != saved_cred) {
3162 kauth_cred_ref(saved_cred);
3163 kauth_cred_unref(&nd->nd_cr);
3164 ctx->vc_ucred = nd->nd_cr = saved_cred;
3165 }
3166
3167 goto retry;
3168 }
3169 } else {
3170 /*
3171 * when we dropped the iocounts to take
3172 * the lock, we allowed the identity of
3173 * the various vnodes to change... if they did,
3174 * we may no longer be dealing with a rename
3175 * that reshapes the tree... once we're holding
3176 * the iocounts, the vnodes can't change type
3177 * so we're free to drop the lock at this point
3178 * and continue on
3179 */
3180 if (holding_mntlock) {
3181 mount_unlock_renames(locked_mp);
3182 mount_drop(locked_mp, 0);
3183 holding_mntlock = 0;
3184 }
3185 }
3186
3187 // save these off so we can later verify that fvp is the same
3188 vnode_t oparent;
3189 oname = fvp->v_name;
3190 oparent = fvp->v_parent;
3191
3192 /*
3193 * If generating an fsevent, then
3194 * stash any pre-rename info we may need.
3195 */
3196 #if CONFIG_FSE
3197 if (nfsrv_fsevents_enabled && need_fsevent(FSE_RENAME, fvp)) {
3198 int from_truncated = 0, to_truncated = 0;
3199
3200 get_fse_info(fvp, &from_finfo, ctx);
3201 if (tvp) {
3202 get_fse_info(tvp, &to_finfo, ctx);
3203 }
3204
3205 from_name = get_pathbuff();
3206 if (from_name) {
3207 from_len = safe_getpath(fdvp, fromni.ni_cnd.cn_nameptr, from_name, MAXPATHLEN, &from_truncated);
3208 }
3209
3210 to_name = from_name ? get_pathbuff() : NULL;
3211 if (to_name) {
3212 to_len = safe_getpath(tdvp, toni.ni_cnd.cn_nameptr, to_name, MAXPATHLEN, &to_truncated);
3213 }
3214
3215 if (from_truncated || to_truncated) {
3216 from_finfo.mode |= FSE_TRUNCATED_PATH;
3217 }
3218 } else {
3219 from_name = NULL;
3220 to_name = NULL;
3221 }
3222 #else /* CONFIG_FSE */
3223 from_name = NULL;
3224 to_name = NULL;
3225 #endif /* CONFIG_FSE */
3226
3227 error = VNOP_RENAME(fromni.ni_dvp, fromni.ni_vp, &fromni.ni_cnd,
3228 toni.ni_dvp, toni.ni_vp, &toni.ni_cnd, ctx);
3229 /*
3230 * fix up name & parent pointers. note that we first
3231 * check that fvp has the same name/parent pointers it
3232 * had before the rename call... this is a 'weak' check
3233 * at best...
3234 */
3235 if (oname == fvp->v_name && oparent == fvp->v_parent) {
3236 int update_flags;
3237 update_flags = VNODE_UPDATE_NAME;
3238 if (fdvp != tdvp) {
3239 update_flags |= VNODE_UPDATE_PARENT;
3240 }
3241 vnode_update_identity(fvp, tdvp, toni.ni_cnd.cn_nameptr,
3242 toni.ni_cnd.cn_namelen, toni.ni_cnd.cn_hash, update_flags);
3243 }
3244
3245 /*
3246 * If the rename is OK and we've got the paths
3247 * then add an fsevent.
3248 */
3249 #if CONFIG_FSE
3250 if (nfsrv_fsevents_enabled && !error && from_name && to_name) {
3251 if (tvp) {
3252 add_fsevent(FSE_RENAME, ctx,
3253 FSE_ARG_STRING, from_len, from_name,
3254 FSE_ARG_FINFO, &from_finfo,
3255 FSE_ARG_STRING, to_len, to_name,
3256 FSE_ARG_FINFO, &to_finfo,
3257 FSE_ARG_DONE);
3258 } else {
3259 add_fsevent(FSE_RENAME, ctx,
3260 FSE_ARG_STRING, from_len, from_name,
3261 FSE_ARG_FINFO, &from_finfo,
3262 FSE_ARG_STRING, to_len, to_name,
3263 FSE_ARG_DONE);
3264 }
3265 }
3266 if (from_name) {
3267 release_pathbuff(from_name);
3268 }
3269 if (to_name) {
3270 release_pathbuff(to_name);
3271 }
3272 #endif /* CONFIG_FSE */
3273 from_name = to_name = NULL;
3274
3275 out:
3276 if (holding_mntlock) {
3277 mount_unlock_renames(locked_mp);
3278 mount_drop(locked_mp, 0);
3279 holding_mntlock = 0;
3280 }
3281 if (tdvp) {
3282 /*
3283 * nameidone has to happen before we vnode_put(tdvp)
3284 * since it may need to release the fs_nodelock on the tdvp
3285 */
3286 nameidone(&toni);
3287 if (tvp) {
3288 vnode_put(tvp);
3289 }
3290 vnode_put(tdvp);
3291
3292 tdvp = NULL;
3293 }
3294 if (fdvp) {
3295 /*
3296 * nameidone has to happen before we vnode_put(fdvp)
3297 * since it may need to release the fs_nodelock on the fdvp
3298 */
3299 nameidone(&fromni);
3300
3301 if (fvp) {
3302 vnode_put(fvp);
3303 }
3304 vnode_put(fdvp);
3305
3306 fdvp = NULL;
3307 }
3308 if (fdirp) {
3309 nfsm_srv_vattr_init(&fdpostattr, nd->nd_vers);
3310 fdpostattrerr = vnode_getattr(fdirp, &fdpostattr, ctx);
3311 vnode_put(fdirp);
3312 fdirp = NULL;
3313 }
3314 if (tdirp) {
3315 nfsm_srv_vattr_init(&tdpostattr, nd->nd_vers);
3316 tdpostattrerr = vnode_getattr(tdirp, &tdpostattr, ctx);
3317 vnode_put(tdirp);
3318 tdirp = NULL;
3319 }
3320
3321 nfsmerr:
3322 /* assemble reply */
3323 nd->nd_repstat = error;
3324 error = nfsrv_rephead(nd, slp, &nmrep, 2 * NFSX_WCCDATA(nd->nd_vers));
3325 nfsmout_if(error);
3326 *mrepp = nmrep.nmc_mhead;
3327 nfsmout_on_status(nd, error);
3328 if (nd->nd_vers == NFS_VER3) {
3329 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3330 fdpreattrerr, &fdpreattr, fdpostattrerr, &fdpostattr);
3331 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3332 tdpreattrerr, &tdpreattr, tdpostattrerr, &tdpostattr);
3333 }
3334 nfsmout:
3335 nfsm_chain_build_done(error, &nmrep);
3336 if (holding_mntlock) {
3337 mount_unlock_renames(locked_mp);
3338 mount_drop(locked_mp, 0);
3339 }
3340 if (tdvp) {
3341 /*
3342 * nameidone has to happen before we vnode_put(tdvp)
3343 * since it may need to release the fs_nodelock on the tdvp
3344 */
3345 nameidone(&toni);
3346
3347 if (tvp) {
3348 vnode_put(tvp);
3349 }
3350 vnode_put(tdvp);
3351 }
3352 if (fdvp) {
3353 /*
3354 * nameidone has to happen before we vnode_put(fdvp)
3355 * since it may need to release the fs_nodelock on the fdvp
3356 */
3357 nameidone(&fromni);
3358
3359 if (fvp) {
3360 vnode_put(fvp);
3361 }
3362 vnode_put(fdvp);
3363 }
3364 if (fdirp) {
3365 vnode_put(fdirp);
3366 }
3367 if (tdirp) {
3368 vnode_put(tdirp);
3369 }
3370 if (frompath) {
3371 NFS_ZFREE(ZV_NAMEI, frompath);
3372 }
3373 if (topath) {
3374 NFS_ZFREE(ZV_NAMEI, topath);
3375 }
3376 if (saved_cred) {
3377 kauth_cred_unref(&saved_cred);
3378 }
3379 if (error) {
3380 nfsm_chain_cleanup(&nmrep);
3381 *mrepp = NULL;
3382 }
3383 return error;
3384 }
3385
3386 /*
3387 * nfs link service
3388 */
3389 int
nfsrv_link(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3390 nfsrv_link(
3391 struct nfsrv_descript *nd,
3392 struct nfsrv_sock *slp,
3393 vfs_context_t ctx,
3394 mbuf_t *mrepp)
3395 {
3396 struct nameidata ni;
3397 int error, dpreattrerr, dpostattrerr, attrerr;
3398 uint32_t len = 0;
3399 vnode_t vp, xp, dvp, dirp;
3400 struct vnode_attr dpreattr, dpostattr, attr;
3401 struct nfs_filehandle nfh, dnfh;
3402 struct nfs_export *nx;
3403 struct nfs_export_options *nxo;
3404 struct nfsm_chain *nmreq, nmrep;
3405
3406 error = 0;
3407 dpreattrerr = dpostattrerr = attrerr = ENOENT;
3408 vp = xp = dvp = dirp = NULL;
3409 nmreq = &nd->nd_nmreq;
3410 nfsm_chain_null(&nmrep);
3411
3412 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3413 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
3414 nfsm_chain_get_32(error, nmreq, len);
3415 nfsm_name_len_check(error, nd, len);
3416 nfsmerr_if(error);
3417 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
3418 nfsmerr_if(error);
3419
3420 /* update export stats */
3421 NFSStatAdd64(&nx->nx_stats.ops, 1);
3422
3423 /* update active user stats */
3424 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
3425
3426 error = nfsrv_credcheck(nd, ctx, nx, nxo);
3427 nfsmerr_if(error);
3428
3429 /* we're not allowed to link to directories... */
3430 if (vnode_vtype(vp) == VDIR) {
3431 error = EPERM; /* POSIX */
3432 goto out;
3433 }
3434
3435 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3436 if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx, nxo, 0)) != 0) {
3437 goto out;
3438 }
3439
3440 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(vnode_getname(vp)), ctx);
3441 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3442 if (!error) {
3443 error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo);
3444 }
3445 if (dirp) {
3446 if (nd->nd_vers == NFS_VER3) {
3447 nfsm_srv_pre_vattr_init(&dpreattr);
3448 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3449 } else {
3450 vnode_put(dirp);
3451 dirp = NULL;
3452 }
3453 }
3454 if (error) {
3455 goto out;
3456 }
3457 dvp = ni.ni_dvp;
3458 xp = ni.ni_vp;
3459
3460 if (xp != NULL) {
3461 error = EEXIST;
3462 } else if (vnode_mount(vp) != vnode_mount(dvp)) {
3463 error = EXDEV;
3464 } else {
3465 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3466 }
3467
3468 #if CONFIG_MACF
3469 if (!error) {
3470 error = mac_vnode_check_link(ctx, dvp, vp, &ni.ni_cnd);
3471 if (error) {
3472 error = EACCES;
3473 }
3474 }
3475 #endif
3476 if (!error) {
3477 error = VNOP_LINK(vp, dvp, &ni.ni_cnd, ctx);
3478 }
3479
3480 #if CONFIG_FSE
3481 if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, dvp)) {
3482 char *target_path = NULL;
3483 int plen, truncated = 0;
3484 fse_info finfo;
3485
3486 /* build the path to the new link file */
3487 target_path = get_pathbuff();
3488 if (target_path) {
3489 plen = safe_getpath(dvp, ni.ni_cnd.cn_nameptr, target_path, MAXPATHLEN, &truncated);
3490
3491 if (get_fse_info(vp, &finfo, ctx) == 0) {
3492 if (truncated) {
3493 finfo.mode |= FSE_TRUNCATED_PATH;
3494 }
3495 add_fsevent(FSE_CREATE_FILE, ctx,
3496 FSE_ARG_STRING, plen, target_path,
3497 FSE_ARG_FINFO, &finfo,
3498 FSE_ARG_DONE);
3499 }
3500
3501 release_pathbuff(target_path);
3502 }
3503 }
3504 #endif
3505
3506 /*
3507 * nameidone has to happen before we vnode_put(dvp)
3508 * since it may need to release the fs_nodelock on the dvp
3509 */
3510 nameidone(&ni);
3511
3512 if (xp) {
3513 vnode_put(xp);
3514 }
3515 vnode_put(dvp);
3516 out:
3517 if (nd->nd_vers == NFS_VER3) {
3518 nfsm_srv_vattr_init(&attr, NFS_VER3);
3519 attrerr = vnode_getattr(vp, &attr, ctx);
3520 }
3521 if (dirp) {
3522 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3523 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3524 vnode_put(dirp);
3525 dirp = NULL;
3526 }
3527 vnode_put(vp);
3528 vp = NULL;
3529
3530 nfsmerr:
3531 /* assemble reply */
3532 nd->nd_repstat = error;
3533 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3534 nfsmout_if(error);
3535 *mrepp = nmrep.nmc_mhead;
3536 nfsmout_on_status(nd, error);
3537 if (nd->nd_vers == NFS_VER3) {
3538 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
3539 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3540 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3541 }
3542 nfsmout:
3543 nfsm_chain_build_done(error, &nmrep);
3544 if (vp) {
3545 vnode_put(vp);
3546 }
3547 if (error) {
3548 nfsm_chain_cleanup(&nmrep);
3549 *mrepp = NULL;
3550 }
3551 return error;
3552 }
3553
3554 /*
3555 * nfs symbolic link service
3556 */
3557 int
nfsrv_symlink(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3558 nfsrv_symlink(
3559 struct nfsrv_descript *nd,
3560 struct nfsrv_sock *slp,
3561 vfs_context_t ctx,
3562 mbuf_t *mrepp)
3563 {
3564 struct vnode_attr dpreattr, dpostattr, postattr;
3565 struct vnode_attr va, *vap = &va;
3566 struct nameidata ni;
3567 int error, dpreattrerr, dpostattrerr, postattrerr;
3568 uint32_t len = 0, linkdatalen = 0, cnflags;
3569 uid_t saved_uid;
3570 char *linkdata;
3571 vnode_t vp, dvp, dirp;
3572 struct nfs_filehandle nfh = {};
3573 struct nfs_export *nx = NULL;
3574 struct nfs_export_options *nxo = NULL;
3575 uio_t auio = NULL;
3576 uio_stackbuf_t uio_buf[UIO_SIZEOF(1)];
3577 struct nfsm_chain *nmreq, nmrep;
3578 __unused const int nfs_vers = nd->nd_vers;
3579 assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3580
3581 error = 0;
3582 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3583 nmreq = &nd->nd_nmreq;
3584 nfsm_chain_null(&nmrep);
3585 linkdata = NULL;
3586 dirp = NULL;
3587
3588 saved_uid = kauth_cred_getuid(nd->nd_cr);
3589
3590 ni.ni_cnd.cn_nameiop = 0;
3591 vp = dvp = NULL;
3592
3593 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3594 nfsm_chain_get_32(error, nmreq, len);
3595 nfsm_name_len_check(error, nd, len);
3596 nfsmerr_if(error);
3597
3598 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, 0, ctx);
3599 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3600 if (!error) {
3601 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3602 if (nx != NULL) {
3603 /* update export stats */
3604 NFSStatAdd64(&nx->nx_stats.ops, 1);
3605
3606 /* update active user stats */
3607 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3608 }
3609 }
3610 if (dirp) {
3611 if (nd->nd_vers == NFS_VER3) {
3612 nfsm_srv_pre_vattr_init(&dpreattr);
3613 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3614 } else {
3615 vnode_put(dirp);
3616 dirp = NULL;
3617 }
3618 }
3619 if (error) {
3620 ni.ni_cnd.cn_nameiop = 0;
3621 goto out1;
3622 }
3623 dvp = ni.ni_dvp;
3624 vp = ni.ni_vp;
3625
3626 VATTR_INIT(vap);
3627 if (nd->nd_vers == NFS_VER3) {
3628 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3629 }
3630 nfsm_chain_get_32(error, nmreq, linkdatalen);
3631 if (!error && (((nd->nd_vers == NFS_VER2) && (linkdatalen > NFS_MAXPATHLEN)) ||
3632 ((nd->nd_vers == NFS_VER3) && (linkdatalen > MAXPATHLEN)))) {
3633 error = NFSERR_NAMETOL;
3634 }
3635 nfsmerr_if(error);
3636 linkdata = kalloc_data(linkdatalen + 1, Z_WAITOK);
3637 if (linkdata) {
3638 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
3639 &uio_buf[0], sizeof(uio_buf));
3640 }
3641 if (!linkdata || !auio) {
3642 error = ENOMEM;
3643 goto out;
3644 }
3645 uio_addiov(auio, CAST_USER_ADDR_T(linkdata), linkdatalen);
3646 error = nfsm_chain_get_uio(nmreq, linkdatalen, auio);
3647 if (!error && (nd->nd_vers == NFS_VER2)) {
3648 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3649 }
3650 nfsmerr_if(error);
3651 *(linkdata + linkdatalen) = '\0';
3652 if (vp) {
3653 error = EEXIST;
3654 goto out;
3655 }
3656
3657 VATTR_SET(vap, va_type, VLNK);
3658 VATTR_CLEAR_ACTIVE(vap, va_data_size);
3659 VATTR_CLEAR_ACTIVE(vap, va_access_time);
3660 /*
3661 * Server policy is to alway use the mapped rpc credential for
3662 * file system object creation. This has the nice side effect of
3663 * enforcing BSD creation semantics
3664 */
3665 VATTR_CLEAR_ACTIVE(vap, va_uid);
3666 VATTR_CLEAR_ACTIVE(vap, va_gid);
3667
3668 /* authorize before creating */
3669 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3670
3671 /* validate given attributes */
3672 if (!error) {
3673 error = vnode_authattr_new(dvp, vap, 0, ctx);
3674 }
3675 if (!error) {
3676 error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
3677 if (error) {
3678 error = EACCES;
3679 }
3680 }
3681
3682 if (!error) {
3683 error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx);
3684 }
3685
3686 if (!error && (nd->nd_vers == NFS_VER3)) {
3687 if (vp == NULL) {
3688 ni.ni_cnd.cn_nameiop = LOOKUP;
3689 #if CONFIG_TRIGGERS
3690 ni.ni_op = OP_LOOKUP;
3691 #endif
3692 ni.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
3693 ni.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
3694 ni.ni_cnd.cn_context = ctx;
3695 ni.ni_startdir = dvp;
3696 ni.ni_usedvp = dvp;
3697 ni.ni_rootdir = rootvnode;
3698 cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
3699 while ((error = lookup(&ni)) == ERECYCLE) {
3700 ni.ni_cnd.cn_flags = cnflags;
3701 ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
3702 ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
3703 }
3704 if (!error) {
3705 vp = ni.ni_vp;
3706 }
3707 }
3708 if (!error) {
3709 error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh);
3710 if (!error) {
3711 nfsm_srv_vattr_init(&postattr, NFS_VER3);
3712 postattrerr = vnode_getattr(vp, &postattr, ctx);
3713 }
3714 }
3715 }
3716
3717 #if CONFIG_FSE
3718 if (nfsrv_fsevents_enabled && !error && vp) {
3719 add_fsevent(FSE_CREATE_FILE, ctx,
3720 FSE_ARG_VNODE, vp,
3721 FSE_ARG_DONE);
3722 }
3723 #endif
3724 out:
3725 /*
3726 * nameidone has to happen before we vnode_put(dvp)
3727 * since it may need to release the fs_nodelock on the dvp
3728 */
3729 nameidone(&ni);
3730 ni.ni_cnd.cn_nameiop = 0;
3731 if (vp) {
3732 vnode_put(vp);
3733 }
3734 vnode_put(dvp);
3735 out1:
3736 if (linkdata) {
3737 kfree_data(linkdata, linkdatalen + 1);
3738 }
3739 if (dirp) {
3740 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3741 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3742 vnode_put(dirp);
3743 dirp = NULL;
3744 }
3745
3746 nfsmerr:
3747 /* assemble reply */
3748 nd->nd_repstat = error;
3749 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3750 NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3751 assert(nfs_vers == nd->nd_vers);
3752 nfsmout_if(error);
3753 *mrepp = nmrep.nmc_mhead;
3754 nfsmout_on_status(nd, error);
3755 if (nd->nd_vers == NFS_VER3) {
3756 if (!nd->nd_repstat) {
3757 if (!nfh.nfh_fhp) {
3758 error = NFSERR_SERVERFAULT;
3759 goto nfsmerr;
3760 }
3761 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3762 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3763 }
3764 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3765 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3766 }
3767 nfsmout:
3768 nfsm_chain_build_done(error, &nmrep);
3769 if (ni.ni_cnd.cn_nameiop) {
3770 /*
3771 * nameidone has to happen before we vnode_put(dvp)
3772 * since it may need to release the fs_nodelock on the dvp
3773 */
3774 nameidone(&ni);
3775
3776 if (vp) {
3777 vnode_put(vp);
3778 }
3779 vnode_put(dvp);
3780 }
3781 if (dirp) {
3782 vnode_put(dirp);
3783 }
3784 if (linkdata) {
3785 kfree_data(linkdata, linkdatalen + 1);
3786 }
3787 if (error) {
3788 nfsm_chain_cleanup(&nmrep);
3789 *mrepp = NULL;
3790 }
3791 return error;
3792 }
3793
3794 /*
3795 * nfs mkdir service
3796 */
3797
3798 int
nfsrv_mkdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3799 nfsrv_mkdir(
3800 struct nfsrv_descript *nd,
3801 struct nfsrv_sock *slp,
3802 vfs_context_t ctx,
3803 mbuf_t *mrepp)
3804 {
3805 struct vnode_attr dpreattr, dpostattr, postattr;
3806 struct vnode_attr va, *vap = &va;
3807 struct nameidata ni;
3808 int error, dpreattrerr, dpostattrerr, postattrerr;
3809 uint32_t len = 0;
3810 vnode_t vp, dvp, dirp;
3811 struct nfs_filehandle nfh = {};
3812 struct nfs_export *nx = NULL;
3813 struct nfs_export_options *nxo = NULL;
3814 uid_t saved_uid;
3815 kauth_acl_t xacl = NULL;
3816 struct nfsm_chain *nmreq, nmrep;
3817 __unused const int nfs_vers = nd->nd_vers;
3818 assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3819
3820 error = 0;
3821 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3822 nmreq = &nd->nd_nmreq;
3823 nfsm_chain_null(&nmrep);
3824
3825 saved_uid = kauth_cred_getuid(nd->nd_cr);
3826
3827 ni.ni_cnd.cn_nameiop = 0;
3828 vp = dvp = dirp = NULL;
3829
3830 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3831 nfsm_chain_get_32(error, nmreq, len);
3832 nfsm_name_len_check(error, nd, len);
3833 nfsmerr_if(error);
3834
3835 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | WILLBEDIR, UIO_SYSSPACE, 0, ctx);
3836 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3837 if (!error) {
3838 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3839 if (nx != NULL) {
3840 /* update export stats */
3841 NFSStatAdd64(&nx->nx_stats.ops, 1);
3842
3843 /* update active user stats */
3844 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3845 }
3846 }
3847 if (dirp) {
3848 if (nd->nd_vers == NFS_VER3) {
3849 nfsm_srv_pre_vattr_init(&dpreattr);
3850 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3851 } else {
3852 vnode_put(dirp);
3853 dirp = NULL;
3854 }
3855 }
3856 if (error) {
3857 ni.ni_cnd.cn_nameiop = 0;
3858 goto nfsmerr;
3859 }
3860 dvp = ni.ni_dvp;
3861 vp = ni.ni_vp;
3862
3863 VATTR_INIT(vap);
3864 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3865 nfsmerr_if(error);
3866 VATTR_SET(vap, va_type, VDIR);
3867
3868 if (vp != NULL) {
3869 /*
3870 * nameidone has to happen before we vnode_put(dvp)
3871 * since it may need to release the fs_nodelock on the dvp
3872 */
3873 nameidone(&ni);
3874 vnode_put(dvp);
3875 vnode_put(vp);
3876 error = EEXIST;
3877 goto out;
3878 }
3879
3880 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx, nxo, 0);
3881
3882 /* construct ACL and handle inheritance */
3883 if (!error) {
3884 error = kauth_acl_inherit(dvp,
3885 NULL,
3886 &xacl, /* isdir */
3887 1,
3888 ctx);
3889
3890 if (!error && xacl != NULL) {
3891 VATTR_SET(vap, va_acl, xacl);
3892 }
3893 }
3894
3895 VATTR_CLEAR_ACTIVE(vap, va_data_size);
3896 VATTR_CLEAR_ACTIVE(vap, va_access_time);
3897 /*
3898 * We don't support the S_ISGID bit for directories. Solaris and other
3899 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3900 * any ways.
3901 */
3902 if (VATTR_IS_ACTIVE(vap, va_mode)) {
3903 vap->va_mode &= ~S_ISGID;
3904 }
3905 /*
3906 * Server policy is to alway use the mapped rpc credential for
3907 * file system object creation. This has the nice side effect of
3908 * enforcing BSD creation semantics
3909 */
3910 VATTR_CLEAR_ACTIVE(vap, va_uid);
3911 VATTR_CLEAR_ACTIVE(vap, va_gid);
3912
3913 /* validate new-file security information */
3914 if (!error) {
3915 error = vnode_authattr_new(dvp, vap, 0, ctx);
3916 }
3917 /*
3918 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3919 * sit well with our clients so we map all errors to EPERM.
3920 */
3921 if (error) {
3922 error = EPERM;
3923 }
3924
3925 if (!error) {
3926 error = vn_authorize_mkdir(dvp, &ni.ni_cnd, vap, ctx, NULL);
3927 if (error) {
3928 error = EACCES;
3929 }
3930 }
3931
3932 if (!error) {
3933 error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx);
3934 }
3935
3936 #if CONFIG_FSE
3937 if (nfsrv_fsevents_enabled && !error) {
3938 add_fsevent(FSE_CREATE_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
3939 }
3940 #endif
3941
3942 if (!error && !VATTR_ALL_SUPPORTED(vap)) {
3943 /*
3944 * If some of the requested attributes weren't handled by the VNOP,
3945 * use our fallback code.
3946 */
3947 error = vnode_setattr_fallback(vp, vap, ctx);
3948 }
3949
3950 if (xacl != NULL) {
3951 kauth_acl_free(xacl);
3952 }
3953
3954 if (!error) {
3955 error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh);
3956 if (!error) {
3957 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
3958 postattrerr = vnode_getattr(vp, &postattr, ctx);
3959 if (nd->nd_vers == NFS_VER2) {
3960 error = postattrerr;
3961 }
3962 }
3963 vnode_put(vp);
3964 vp = NULL;
3965 }
3966 /*
3967 * nameidone has to happen before we vnode_put(dvp)
3968 * since it may need to release the fs_nodelock on the dvp
3969 */
3970 nameidone(&ni);
3971 vnode_put(dvp);
3972 out:
3973 ni.ni_cnd.cn_nameiop = 0;
3974
3975 if (dirp) {
3976 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3977 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3978 vnode_put(dirp);
3979 dirp = NULL;
3980 }
3981
3982 nfsmerr:
3983 /* assemble reply */
3984 nd->nd_repstat = error;
3985 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3986 NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3987 assert(nfs_vers == nd->nd_vers);
3988 nfsmout_if(error);
3989 *mrepp = nmrep.nmc_mhead;
3990 nfsmout_on_status(nd, error);
3991 if (nd->nd_vers == NFS_VER3) {
3992 if (!nd->nd_repstat) {
3993 if (!nfh.nfh_fhp) {
3994 error = NFSERR_SERVERFAULT;
3995 goto nfsmerr;
3996 }
3997 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3998 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3999 }
4000 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4001 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4002 } else {
4003 nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len);
4004 if (!error) {
4005 error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
4006 }
4007 }
4008 nfsmout:
4009 nfsm_chain_build_done(error, &nmrep);
4010 if (ni.ni_cnd.cn_nameiop) {
4011 /*
4012 * nameidone has to happen before we vnode_put(dvp)
4013 * since it may need to release the fs_nodelock on the dvp
4014 */
4015 nameidone(&ni);
4016 vnode_put(dvp);
4017 if (vp) {
4018 vnode_put(vp);
4019 }
4020 }
4021 if (dirp) {
4022 vnode_put(dirp);
4023 }
4024 if (error) {
4025 nfsm_chain_cleanup(&nmrep);
4026 *mrepp = NULL;
4027 }
4028 return error;
4029 }
4030
4031 /*
4032 * nfs rmdir service
4033 */
4034 int
nfsrv_rmdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4035 nfsrv_rmdir(
4036 struct nfsrv_descript *nd,
4037 struct nfsrv_sock *slp,
4038 vfs_context_t ctx,
4039 mbuf_t *mrepp)
4040 {
4041 int error, dpreattrerr, dpostattrerr;
4042 uint32_t len = 0;
4043 uid_t saved_uid;
4044 vnode_t vp, dvp, dirp;
4045 struct vnode_attr dpreattr, dpostattr;
4046 struct nfs_filehandle nfh;
4047 struct nfs_export *nx = NULL;
4048 struct nfs_export_options *nxo = NULL;
4049 struct nameidata ni;
4050 struct nfsm_chain *nmreq, nmrep;
4051
4052 error = 0;
4053 dpreattrerr = dpostattrerr = ENOENT;
4054 saved_uid = kauth_cred_getuid(nd->nd_cr);
4055 nmreq = &nd->nd_nmreq;
4056 nfsm_chain_null(&nmrep);
4057
4058 vp = dvp = dirp = NULL;
4059
4060 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4061 nfsm_chain_get_32(error, nmreq, len);
4062 nfsm_name_len_check(error, nd, len);
4063 nfsmerr_if(error);
4064
4065 NDINIT(&ni, DELETE, OP_UNLINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
4066 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
4067 if (!error) {
4068 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
4069 if (nx != NULL) {
4070 /* update export stats */
4071 NFSStatAdd64(&nx->nx_stats.ops, 1);
4072
4073 /* update active user stats */
4074 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
4075 }
4076 }
4077 if (dirp) {
4078 if (nd->nd_vers == NFS_VER3) {
4079 nfsm_srv_pre_vattr_init(&dpreattr);
4080 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
4081 } else {
4082 vnode_put(dirp);
4083 dirp = NULL;
4084 }
4085 }
4086 nfsmerr_if(error);
4087
4088 dvp = ni.ni_dvp;
4089 vp = ni.ni_vp;
4090
4091 if (vnode_vtype(vp) != VDIR) {
4092 error = ENOTDIR;
4093 goto out;
4094 }
4095 /*
4096 * No rmdir "." please.
4097 */
4098 if (dvp == vp) {
4099 error = EINVAL;
4100 goto out;
4101 }
4102 /*
4103 * The root of a mounted filesystem cannot be deleted.
4104 */
4105 if (vnode_isvroot(vp)) {
4106 error = EBUSY;
4107 }
4108 if (!error) {
4109 error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0);
4110 }
4111 if (!error) {
4112 error = vn_authorize_rmdir(dvp, vp, &ni.ni_cnd, ctx, NULL);
4113 if (error) {
4114 error = EACCES;
4115 }
4116 }
4117
4118 if (!error) {
4119 #if CONFIG_FSE
4120 char *path = NULL;
4121 int plen = 0;
4122 fse_info finfo;
4123
4124 if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) {
4125 plen = MAXPATHLEN;
4126 if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) {
4127 get_fse_info(vp, &finfo, ctx);
4128 } else if (path) {
4129 release_pathbuff(path);
4130 path = NULL;
4131 }
4132 }
4133 #endif /* CONFIG_FSE */
4134
4135 error = VNOP_RMDIR(dvp, vp, &ni.ni_cnd, ctx);
4136
4137 #if CONFIG_FSE
4138 if (path) {
4139 if (!error) {
4140 add_fsevent(FSE_DELETE, ctx,
4141 FSE_ARG_STRING, plen, path,
4142 FSE_ARG_FINFO, &finfo,
4143 FSE_ARG_DONE);
4144 }
4145 release_pathbuff(path);
4146 }
4147 #endif /* CONFIG_FSE */
4148 }
4149 out:
4150 /*
4151 * nameidone has to happen before we vnode_put(dvp)
4152 * since it may need to release the fs_nodelock on the dvp
4153 */
4154 nameidone(&ni);
4155
4156 vnode_put(dvp);
4157 vnode_put(vp);
4158
4159 if (dirp) {
4160 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
4161 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
4162 vnode_put(dirp);
4163 dirp = NULL;
4164 }
4165
4166 nfsmerr:
4167 /* assemble reply */
4168 nd->nd_repstat = error;
4169 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
4170 nfsmout_if(error);
4171 *mrepp = nmrep.nmc_mhead;
4172 nfsmout_on_status(nd, error);
4173 if (nd->nd_vers == NFS_VER3) {
4174 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4175 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4176 }
4177 nfsmout:
4178 nfsm_chain_build_done(error, &nmrep);
4179 if (dirp) {
4180 vnode_put(dirp);
4181 }
4182 if (error) {
4183 nfsm_chain_cleanup(&nmrep);
4184 *mrepp = NULL;
4185 }
4186 return error;
4187 }
4188
4189 /*
4190 * nfs readdir service
4191 * - mallocs what it thinks is enough to read
4192 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4193 * - calls VNOP_READDIR()
4194 * - loops around building the reply
4195 * if the output generated exceeds count break out of loop
4196 * The nfsm_clget macro is used here so that the reply will be packed
4197 * tightly in mbuf clusters.
4198 * - it only knows that it has encountered eof when the VNOP_READDIR()
4199 * reads nothing
4200 * - as such one readdir rpc will return eof false although you are there
4201 * and then the next will return eof
4202 * - it trims out records with d_fileno == 0
4203 * this doesn't matter for Unix clients, but they might confuse clients
4204 * for other os'.
4205 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4206 * than requested, but this may not apply to all filesystems. For
4207 * example, client NFS does not { although it is never remote mounted
4208 * anyhow }
4209 * The alternate call nfsrv_readdirplus() does lookups as well.
4210 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
4211 * are supposed to cover. For readdir, the count is the total number of
4212 * bytes included in everything from the directory's postopattr through
4213 * the EOF flag. For readdirplus, the maxcount is the same, and the
4214 * dircount includes all that except for the entry attributes and handles.
4215 */
4216 int
nfsrv_readdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4217 nfsrv_readdir(
4218 struct nfsrv_descript *nd,
4219 struct nfsrv_sock *slp,
4220 vfs_context_t ctx,
4221 mbuf_t *mrepp)
4222 {
4223 struct direntry *dp;
4224 char *cpos, *cend, *rbuf;
4225 size_t rbuf_siz;
4226 vnode_t vp;
4227 struct vnode_attr attr = {};
4228 struct nfs_filehandle nfh;
4229 struct nfs_export *nx;
4230 struct nfs_export_options *nxo;
4231 uio_t auio = NULL;
4232 uio_stackbuf_t uio_buf[UIO_SIZEOF(1)];
4233 int len, nlen, rem, xfer, error, attrerr;
4234 int siz, count, fullsiz, eofflag, nentries;
4235 u_quad_t off, toff, verf = 0;
4236 int vnopflag;
4237 struct nfsm_chain *nmreq, nmrep;
4238
4239 error = 0;
4240 attrerr = ENOENT;
4241 count = nentries = 0;
4242 nmreq = &nd->nd_nmreq;
4243 nfsm_chain_null(&nmrep);
4244 rbuf = NULL;
4245 rbuf_siz = 0;
4246 vp = NULL;
4247
4248 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4249
4250 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4251 if (nd->nd_vers == NFS_VER3) {
4252 nfsm_chain_get_64(error, nmreq, toff);
4253 nfsm_chain_get_64(error, nmreq, verf);
4254 } else {
4255 nfsm_chain_get_32(error, nmreq, toff);
4256 }
4257 nfsm_chain_get_32(error, nmreq, count);
4258 nfsmerr_if(error);
4259
4260 off = toff;
4261 siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4262 xfer = NFSRV_NDMAXDATA(nd);
4263 if (siz > xfer) {
4264 siz = xfer;
4265 }
4266 fullsiz = siz;
4267
4268 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4269 nfsmerr_if(error);
4270
4271 /* update export stats */
4272 NFSStatAdd64(&nx->nx_stats.ops, 1);
4273
4274 /* update active user stats */
4275 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4276
4277 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4278 nfsmerr_if(error);
4279
4280 if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2) {
4281 vnopflag |= VNODE_READDIR_NAMEMAX;
4282 }
4283
4284 if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS)) {
4285 vnopflag |= VNODE_READDIR_SEEKOFF32;
4286 }
4287
4288 if (nd->nd_vers == NFS_VER3) {
4289 nfsm_srv_vattr_init(&attr, NFS_VER3);
4290 error = attrerr = vnode_getattr(vp, &attr, ctx);
4291 if (!error && toff && verf && (verf != attr.va_filerev)) {
4292 error = NFSERR_BAD_COOKIE;
4293 }
4294 }
4295 if (!error) {
4296 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4297 }
4298 #if CONFIG_MACF
4299 if (!error) {
4300 if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4301 error = EACCES;
4302 }
4303
4304 if (!error) {
4305 error = mac_vnode_check_readdir(ctx, vp);
4306 }
4307 }
4308 #endif
4309 nfsmerr_if(error);
4310
4311 rbuf_siz = siz;
4312 rbuf = kalloc_data(rbuf_siz, Z_WAITOK);
4313 if (rbuf) {
4314 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4315 &uio_buf[0], sizeof(uio_buf));
4316 }
4317 if (!rbuf || !auio) {
4318 error = ENOMEM;
4319 goto nfsmerr;
4320 }
4321 again:
4322 uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4323 uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4324 eofflag = 0;
4325 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4326 off = uio_offset(auio);
4327
4328 if (nd->nd_vers == NFS_VER3) {
4329 nfsm_srv_vattr_init(&attr, NFS_VER3);
4330 attrerr = vnode_getattr(vp, &attr, ctx);
4331 }
4332 nfsmerr_if(error);
4333
4334 if (uio_resid(auio) != 0) {
4335 siz -= uio_resid(auio);
4336
4337 /* If nothing read, return empty reply with eof set */
4338 if (siz == 0) {
4339 vnode_put(vp);
4340 vp = NULL;
4341 kfree_data(rbuf, rbuf_siz);
4342 /* assemble reply */
4343 nd->nd_repstat = error;
4344 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4345 NFSX_COOKIEVERF(nd->nd_vers) + 2 * NFSX_UNSIGNED);
4346 nfsmout_if(error);
4347 *mrepp = nmrep.nmc_mhead;
4348 nfsmout_on_status(nd, error);
4349 if (nd->nd_vers == NFS_VER3) {
4350 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4351 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4352 }
4353 nfsm_chain_add_32(error, &nmrep, FALSE);
4354 nfsm_chain_add_32(error, &nmrep, TRUE);
4355 nfsm_chain_build_done(error, &nmrep);
4356 return error;
4357 }
4358 }
4359
4360 /*
4361 * Check for degenerate cases of nothing useful read.
4362 * If so go try again
4363 */
4364 cpos = rbuf;
4365 cend = rbuf + siz;
4366 dp = (struct direntry *)cpos;
4367 while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4368 cpos += dp->d_reclen;
4369 dp = (struct direntry *)cpos;
4370 nentries--;
4371 }
4372 if ((cpos >= cend) || (nentries == 0)) {
4373 toff = off;
4374 siz = fullsiz;
4375 goto again;
4376 }
4377
4378 vnode_put(vp);
4379 vp = NULL;
4380
4381 /* assemble reply */
4382 nd->nd_repstat = error;
4383 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4384 NFSX_COOKIEVERF(nd->nd_vers) + siz);
4385 nfsmout_if(error);
4386 *mrepp = nmrep.nmc_mhead;
4387 nfsmout_on_status(nd, error);
4388 nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4389
4390 len = 2 * NFSX_UNSIGNED;
4391 if (nd->nd_vers == NFS_VER3) {
4392 len += NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF;
4393 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4394 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4395 nfsmerr_if(error);
4396 }
4397
4398 /* Loop through the records and build reply */
4399 while ((cpos < cend) && (nentries > 0)) {
4400 if (dp->d_fileno != 0) {
4401 nlen = dp->d_namlen;
4402 if ((nd->nd_vers == NFS_VER2) && (nlen > NFS_MAXNAMLEN)) {
4403 nlen = NFS_MAXNAMLEN;
4404 }
4405 rem = nfsm_rndup(nlen) - nlen;
4406 len += (4 * NFSX_UNSIGNED + nlen + rem);
4407 if (nd->nd_vers == NFS_VER3) {
4408 len += 2 * NFSX_UNSIGNED;
4409 }
4410 if (len > count) {
4411 eofflag = 0;
4412 break;
4413 }
4414 /* Build the directory record xdr from the direntry. */
4415 nfsm_chain_add_32(error, &nmrep, TRUE);
4416 if (nd->nd_vers == NFS_VER3) {
4417 nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4418 } else {
4419 nfsm_chain_add_32(error, &nmrep, dp->d_fileno);
4420 }
4421 nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4422 if (nd->nd_vers == NFS_VER3) {
4423 if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4424 dp->d_seekoff &= 0x00000000ffffffffULL;
4425 }
4426 nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4427 } else {
4428 nfsm_chain_add_32(error, &nmrep, dp->d_seekoff);
4429 }
4430 nfsmerr_if(error);
4431 }
4432 cpos += dp->d_reclen;
4433 dp = (struct direntry *)cpos;
4434 nentries--;
4435 }
4436 nfsm_chain_add_32(error, &nmrep, FALSE);
4437 nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4438 kfree_data(rbuf, rbuf_siz);
4439 goto nfsmout;
4440 nfsmerr:
4441 if (rbuf) {
4442 kfree_data(rbuf, rbuf_siz);
4443 }
4444 if (vp) {
4445 vnode_put(vp);
4446 }
4447 nd->nd_repstat = error;
4448 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers));
4449 nfsmout_if(error);
4450 *mrepp = nmrep.nmc_mhead;
4451 nfsmout_on_status(nd, error);
4452 if (nd->nd_vers == NFS_VER3) {
4453 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4454 }
4455 nfsmout:
4456 nfsm_chain_build_done(error, &nmrep);
4457 if (error) {
4458 nfsm_chain_cleanup(&nmrep);
4459 *mrepp = NULL;
4460 }
4461 return error;
4462 }
4463
4464 int
nfsrv_readdirplus(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4465 nfsrv_readdirplus(
4466 struct nfsrv_descript *nd,
4467 struct nfsrv_sock *slp,
4468 vfs_context_t ctx,
4469 mbuf_t *mrepp)
4470 {
4471 struct direntry *dp;
4472 char *cpos, *cend, *rbuf;
4473 size_t rbuf_siz;
4474 vnode_t vp, nvp;
4475 struct nfs_filehandle dnfh, nfh;
4476 struct nfs_export *nx;
4477 struct nfs_export_options *nxo;
4478 uio_t auio = NULL;
4479 uio_stackbuf_t uio_buf[UIO_SIZEOF(1)];
4480 struct vnode_attr attr, va, *vap = &va;
4481 int len, nlen, rem, xfer, error, attrerr, gotfh, gotattr;
4482 int siz, dircount, maxcount, fullsiz, eofflag, dirlen, nentries, isdotdot;
4483 u_quad_t off, toff, verf;
4484 int vnopflag;
4485 struct nfsm_chain *nmreq, nmrep;
4486
4487 error = 0;
4488 attrerr = ENOENT;
4489 nentries = 0;
4490 nmreq = &nd->nd_nmreq;
4491 nfsm_chain_null(&nmrep);
4492 rbuf = NULL;
4493 rbuf_siz = 0;
4494 vp = NULL;
4495 dircount = maxcount = 0;
4496
4497 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4498
4499 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
4500 nfsm_chain_get_64(error, nmreq, toff);
4501 nfsm_chain_get_64(error, nmreq, verf);
4502 nfsm_chain_get_32(error, nmreq, dircount);
4503 nfsm_chain_get_32(error, nmreq, maxcount);
4504 nfsmerr_if(error);
4505
4506 off = toff;
4507 xfer = NFSRV_NDMAXDATA(nd);
4508 dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4509 if (dircount > xfer) {
4510 dircount = xfer;
4511 }
4512 fullsiz = siz = dircount;
4513 maxcount = ((maxcount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4514 if (maxcount > xfer) {
4515 maxcount = xfer;
4516 }
4517
4518 error = nfsrv_fhtovp(&dnfh, nd, &vp, &nx, &nxo);
4519 nfsmerr_if(error);
4520
4521 /* update export stats */
4522 NFSStatAdd64(&nx->nx_stats.ops, 1);
4523
4524 /* update active user stats */
4525 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4526
4527 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4528 nfsmerr_if(error);
4529
4530 if (nxo->nxo_flags & NX_32BITCLIENTS) {
4531 vnopflag |= VNODE_READDIR_SEEKOFF32;
4532 }
4533
4534 if (nxo->nxo_flags & NX_MANGLEDNAMES) {
4535 vnopflag |= VNODE_READDIR_NAMEMAX;
4536 }
4537
4538 nfsm_srv_vattr_init(&attr, NFS_VER3);
4539 error = attrerr = vnode_getattr(vp, &attr, ctx);
4540 if (!error && toff && verf && (verf != attr.va_filerev)) {
4541 error = NFSERR_BAD_COOKIE;
4542 }
4543 if (!error) {
4544 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4545 }
4546 #if CONFIG_MACF
4547 if (!error) {
4548 if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4549 error = EACCES;
4550 }
4551
4552 if (!error) {
4553 error = mac_vnode_check_readdir(ctx, vp);
4554 }
4555 }
4556 #endif
4557 nfsmerr_if(error);
4558
4559 rbuf_siz = siz;
4560 rbuf = kalloc_data(siz, Z_WAITOK);
4561 if (rbuf) {
4562 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4563 &uio_buf[0], sizeof(uio_buf));
4564 }
4565 if (!rbuf || !auio) {
4566 error = ENOMEM;
4567 goto nfsmerr;
4568 }
4569
4570 again:
4571 uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4572 uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4573 eofflag = 0;
4574 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4575 off = uio_offset(auio);
4576 nfsm_srv_vattr_init(&attr, NFS_VER3);
4577 attrerr = vnode_getattr(vp, &attr, ctx);
4578 nfsmerr_if(error);
4579
4580 if (uio_resid(auio) != 0) {
4581 siz -= uio_resid(auio);
4582
4583 /* If nothing read, return empty reply with eof set */
4584 if (siz == 0) {
4585 vnode_put(vp);
4586 vp = NULL;
4587 kfree_data(rbuf, rbuf_siz);
4588 /* assemble reply */
4589 nd->nd_repstat = error;
4590 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR +
4591 NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED);
4592 nfsmout_if(error);
4593 *mrepp = nmrep.nmc_mhead;
4594 nfsmout_on_status(nd, error);
4595 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4596 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4597 nfsm_chain_add_32(error, &nmrep, FALSE);
4598 nfsm_chain_add_32(error, &nmrep, TRUE);
4599 nfsm_chain_build_done(error, &nmrep);
4600 return error;
4601 }
4602 }
4603
4604 /*
4605 * Check for degenerate cases of nothing useful read.
4606 * If so go try again
4607 */
4608 cpos = rbuf;
4609 cend = rbuf + siz;
4610 dp = (struct direntry *)cpos;
4611 while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4612 cpos += dp->d_reclen;
4613 dp = (struct direntry *)cpos;
4614 nentries--;
4615 }
4616 if ((cpos >= cend) || (nentries == 0)) {
4617 toff = off;
4618 siz = fullsiz;
4619 goto again;
4620 }
4621
4622 /*
4623 * Probe one of the directory entries to see if the filesystem
4624 * supports VGET.
4625 */
4626 if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, ctx))) {
4627 if (error == ENOTSUP) { /* let others get passed back */
4628 error = NFSERR_NOTSUPP;
4629 }
4630 goto nfsmerr;
4631 }
4632 vnode_put(nvp);
4633
4634 /* assemble reply */
4635 nd->nd_repstat = error;
4636 error = nfsrv_rephead(nd, slp, &nmrep, maxcount);
4637 nfsmout_if(error);
4638 *mrepp = nmrep.nmc_mhead;
4639 nfsmout_on_status(nd, error);
4640 nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4641
4642 dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
4643 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4644 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4645 nfsmerr_if(error);
4646
4647 /* Loop through the records and build reply */
4648 while ((cpos < cend) && (nentries > 0)) {
4649 if (dp->d_fileno != 0) {
4650 nlen = dp->d_namlen;
4651 rem = nfsm_rndup(nlen) - nlen;
4652 gotfh = gotattr = 1;
4653
4654 /* Got to get the vnode for lookup per entry. */
4655 if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, ctx)) {
4656 /* Can't get the vnode... so no fh or attrs */
4657 gotfh = gotattr = 0;
4658 } else {
4659 isdotdot = ((dp->d_namlen == 2) &&
4660 (dp->d_name[0] == '.') && (dp->d_name[1] == '.'));
4661 if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, ctx, &nfh)) {
4662 gotfh = 0;
4663 }
4664 nfsm_srv_vattr_init(vap, NFS_VER3);
4665 if (vnode_getattr(nvp, vap, ctx)) {
4666 gotattr = 0;
4667 }
4668 vnode_put(nvp);
4669 }
4670
4671 /*
4672 * If either the dircount or maxcount will be
4673 * exceeded, get out now. Both of these lengths
4674 * are calculated conservatively, including all
4675 * XDR overheads.
4676 */
4677 len += 8 * NFSX_UNSIGNED + nlen + rem;
4678 if (gotattr) {
4679 len += NFSX_V3FATTR;
4680 }
4681 if (gotfh) {
4682 len += NFSX_UNSIGNED + nfsm_rndup(nfh.nfh_len);
4683 }
4684 dirlen += 6 * NFSX_UNSIGNED + nlen + rem;
4685 if ((len > maxcount) || (dirlen > dircount)) {
4686 eofflag = 0;
4687 break;
4688 }
4689
4690 /* Build the directory record xdr from the direntry. */
4691 nfsm_chain_add_32(error, &nmrep, TRUE);
4692 nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4693 nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4694 if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4695 dp->d_seekoff &= 0x00000000ffffffffULL;
4696 }
4697 nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4698 nfsm_chain_add_postop_attr(error, nd, &nmrep, (gotattr ? 0 : ENOENT), vap);
4699 if (gotfh) {
4700 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
4701 } else {
4702 nfsm_chain_add_32(error, &nmrep, FALSE);
4703 }
4704 nfsmerr_if(error);
4705 }
4706 cpos += dp->d_reclen;
4707 dp = (struct direntry *)cpos;
4708 nentries--;
4709 }
4710 vnode_put(vp);
4711 vp = NULL;
4712 nfsm_chain_add_32(error, &nmrep, FALSE);
4713 nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4714 kfree_data(rbuf, rbuf_siz);
4715 goto nfsmout;
4716 nfsmerr:
4717 if (rbuf) {
4718 kfree_data(rbuf, rbuf_siz);
4719 }
4720 nd->nd_repstat = error;
4721 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR);
4722 nfsmout_if(error);
4723 *mrepp = nmrep.nmc_mhead;
4724 nfsmout_on_status(nd, error);
4725 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4726 nfsmout:
4727 nfsm_chain_build_done(error, &nmrep);
4728 if (vp) {
4729 vnode_put(vp);
4730 }
4731 if (error) {
4732 nfsm_chain_cleanup(&nmrep);
4733 *mrepp = NULL;
4734 }
4735 return error;
4736 }
4737
4738 /*
4739 * nfs commit service
4740 */
4741 int
nfsrv_commit(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4742 nfsrv_commit(
4743 struct nfsrv_descript *nd,
4744 struct nfsrv_sock *slp,
4745 vfs_context_t ctx,
4746 mbuf_t *mrepp)
4747 {
4748 vnode_t vp;
4749 struct nfs_filehandle nfh;
4750 struct nfs_export *nx = NULL;
4751 struct nfs_export_options *nxo;
4752 int error, preattrerr, postattrerr, count;
4753 struct vnode_attr preattr, postattr;
4754 u_quad_t off;
4755 struct nfsm_chain *nmreq, nmrep;
4756
4757 error = 0;
4758 preattrerr = postattrerr = ENOENT;
4759 nmreq = &nd->nd_nmreq;
4760 nfsm_chain_null(&nmrep);
4761 vp = NULL;
4762
4763 /*
4764 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4765 * count parameters, so those arguments are useless (someday maybe).
4766 */
4767
4768 nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
4769 nfsm_chain_get_64(error, nmreq, off);
4770 nfsm_chain_get_32(error, nmreq, count);
4771 nfsmerr_if(error);
4772
4773 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4774 nfsmerr_if(error);
4775
4776 /* update export stats */
4777 NFSStatAdd64(&nx->nx_stats.ops, 1);
4778
4779 /* update active user stats */
4780 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4781
4782 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4783 nfsmerr_if(error);
4784
4785 nfsm_srv_pre_vattr_init(&preattr);
4786 preattrerr = vnode_getattr(vp, &preattr, ctx);
4787
4788 error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
4789
4790 nfsm_srv_vattr_init(&postattr, 1);
4791 postattrerr = vnode_getattr(vp, &postattr, ctx);
4792
4793 nfsmerr:
4794 if (vp) {
4795 vnode_put(vp);
4796 }
4797
4798 /* assemble reply */
4799 nd->nd_repstat = error;
4800 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
4801 nfsmout_if(error);
4802 *mrepp = nmrep.nmc_mhead;
4803 nfsmout_on_status(nd, error);
4804 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4805 preattrerr, &preattr, postattrerr, &postattr);
4806 if (!nd->nd_repstat) {
4807 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
4808 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
4809 }
4810 nfsmout:
4811 nfsm_chain_build_done(error, &nmrep);
4812 if (error) {
4813 nfsm_chain_cleanup(&nmrep);
4814 *mrepp = NULL;
4815 }
4816 return error;
4817 }
4818
4819 /*
4820 * nfs statfs service
4821 */
4822 int
nfsrv_statfs(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4823 nfsrv_statfs(
4824 struct nfsrv_descript *nd,
4825 struct nfsrv_sock *slp,
4826 vfs_context_t ctx,
4827 mbuf_t *mrepp)
4828 {
4829 struct vfs_attr va = {};
4830 int error, attrerr;
4831 vnode_t vp;
4832 struct vnode_attr attr;
4833 struct nfs_filehandle nfh;
4834 struct nfs_export *nx;
4835 struct nfs_export_options *nxo;
4836 off_t blksize;
4837 struct nfsm_chain *nmreq, nmrep;
4838
4839 error = 0;
4840 attrerr = ENOENT;
4841 nmreq = &nd->nd_nmreq;
4842 nfsm_chain_null(&nmrep);
4843 vp = NULL;
4844 blksize = 512;
4845
4846 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4847 nfsmerr_if(error);
4848 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4849 nfsmerr_if(error);
4850
4851 /* update export stats */
4852 NFSStatAdd64(&nx->nx_stats.ops, 1);
4853
4854 /* update active user stats */
4855 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4856
4857 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4858 nfsmerr_if(error);
4859
4860 VFSATTR_INIT(&va);
4861 VFSATTR_WANTED(&va, f_blocks);
4862 VFSATTR_WANTED(&va, f_bfree);
4863 VFSATTR_WANTED(&va, f_bavail);
4864 VFSATTR_WANTED(&va, f_files);
4865 VFSATTR_WANTED(&va, f_ffree);
4866 error = vfs_getattr(vnode_mount(vp), &va, ctx);
4867 blksize = vfs_statfs(vnode_mount(vp))->f_bsize;
4868
4869 if (nd->nd_vers == NFS_VER3) {
4870 nfsm_srv_vattr_init(&attr, nd->nd_vers);
4871 attrerr = vnode_getattr(vp, &attr, ctx);
4872 }
4873
4874 nfsmerr:
4875 if (vp) {
4876 vnode_put(vp);
4877 }
4878
4879 /* assemble reply */
4880 nd->nd_repstat = error;
4881 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_STATFS(nd->nd_vers));
4882 nfsmout_if(error);
4883 *mrepp = nmrep.nmc_mhead;
4884 nfsmout_on_status(nd, error);
4885 if (nd->nd_vers == NFS_VER3) {
4886 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4887 }
4888 nfsmout_if(nd->nd_repstat);
4889
4890 if (nd->nd_vers == NFS_VER3) {
4891 nfsm_chain_add_64(error, &nmrep, va.f_blocks * blksize);
4892 nfsm_chain_add_64(error, &nmrep, va.f_bfree * blksize);
4893 nfsm_chain_add_64(error, &nmrep, va.f_bavail * blksize);
4894 nfsm_chain_add_64(error, &nmrep, va.f_files);
4895 nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4896 nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4897 nfsm_chain_add_32(error, &nmrep, 0); /* invarsec */
4898 } else {
4899 nfsm_chain_add_32(error, &nmrep, NFS_V2MAXDATA);
4900 nfsm_chain_add_32(error, &nmrep, blksize);
4901 nfsm_chain_add_32(error, &nmrep, va.f_blocks);
4902 nfsm_chain_add_32(error, &nmrep, va.f_bfree);
4903 nfsm_chain_add_32(error, &nmrep, va.f_bavail);
4904 }
4905 nfsmout:
4906 nfsm_chain_build_done(error, &nmrep);
4907 if (error) {
4908 nfsm_chain_cleanup(&nmrep);
4909 *mrepp = NULL;
4910 }
4911 return error;
4912 }
4913
4914 /*
4915 * nfs fsinfo service
4916 */
4917 int
nfsrv_fsinfo(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4918 nfsrv_fsinfo(
4919 struct nfsrv_descript *nd,
4920 struct nfsrv_sock *slp,
4921 vfs_context_t ctx,
4922 mbuf_t *mrepp)
4923 {
4924 int error, attrerr, prefsize, maxsize;
4925 vnode_t vp;
4926 struct vnode_attr attr;
4927 struct nfs_filehandle nfh;
4928 struct nfs_export *nx;
4929 struct nfs_export_options *nxo;
4930 struct nfsm_chain *nmreq, nmrep;
4931
4932 error = 0;
4933 attrerr = ENOENT;
4934 nmreq = &nd->nd_nmreq;
4935 nfsm_chain_null(&nmrep);
4936 vp = NULL;
4937
4938 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4939 nfsmerr_if(error);
4940 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4941 nfsmerr_if(error);
4942
4943 /* update export stats */
4944 NFSStatAdd64(&nx->nx_stats.ops, 1);
4945
4946 /* update active user stats */
4947 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4948
4949 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4950 nfsmerr_if(error);
4951
4952 nfsm_srv_vattr_init(&attr, NFS_VER3);
4953 attrerr = vnode_getattr(vp, &attr, ctx);
4954
4955 nfsmerr:
4956 if (vp) {
4957 vnode_put(vp);
4958 }
4959
4960 /* assemble reply */
4961 nd->nd_repstat = error;
4962 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
4963 nfsmout_if(error);
4964 *mrepp = nmrep.nmc_mhead;
4965 nfsmout_on_status(nd, error);
4966 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4967 nfsmout_if(nd->nd_repstat);
4968
4969 /*
4970 * XXX There should be file system VFS OP(s) to get this information.
4971 * For now, assume our usual NFS defaults.
4972 */
4973 if (slp->ns_sotype == SOCK_DGRAM) {
4974 maxsize = NFS_MAXDGRAMDATA;
4975 prefsize = NFS_PREFDGRAMDATA;
4976 } else {
4977 maxsize = prefsize = slp->ns_sobufsize ? slp->ns_sobufsize / 2 : NFSRV_MAXDATA;
4978 }
4979
4980 nfsm_chain_add_32(error, &nmrep, maxsize);
4981 nfsm_chain_add_32(error, &nmrep, prefsize);
4982 nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
4983 nfsm_chain_add_32(error, &nmrep, maxsize);
4984 nfsm_chain_add_32(error, &nmrep, prefsize);
4985 nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
4986 nfsm_chain_add_32(error, &nmrep, prefsize);
4987 nfsm_chain_add_64(error, &nmrep, 0xffffffffffffffffULL);
4988 nfsm_chain_add_32(error, &nmrep, 0);
4989 nfsm_chain_add_32(error, &nmrep, 1);
4990 /* XXX link/symlink support should be taken from volume capabilities */
4991 nfsm_chain_add_32(error, &nmrep,
4992 NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK |
4993 NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME);
4994
4995 nfsmout:
4996 nfsm_chain_build_done(error, &nmrep);
4997 if (error) {
4998 nfsm_chain_cleanup(&nmrep);
4999 *mrepp = NULL;
5000 }
5001 return error;
5002 }
5003
5004 /*
5005 * nfs pathconf service
5006 */
5007 int
nfsrv_pathconf(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)5008 nfsrv_pathconf(
5009 struct nfsrv_descript *nd,
5010 struct nfsrv_sock *slp,
5011 vfs_context_t ctx,
5012 mbuf_t *mrepp)
5013 {
5014 int error, attrerr, linkmax = 0, namemax = 0;
5015 int chownres = 0, notrunc = 0, case_sensitive = 0, case_preserving = 0;
5016 vnode_t vp;
5017 struct vnode_attr attr;
5018 struct nfs_filehandle nfh;
5019 struct nfs_export *nx;
5020 struct nfs_export_options *nxo;
5021 struct nfsm_chain *nmreq, nmrep;
5022
5023 error = 0;
5024 attrerr = ENOENT;
5025 nmreq = &nd->nd_nmreq;
5026 nfsm_chain_null(&nmrep);
5027 vp = NULL;
5028
5029 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
5030 nfsmerr_if(error);
5031 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
5032 nfsmerr_if(error);
5033
5034 /* update export stats */
5035 NFSStatAdd64(&nx->nx_stats.ops, 1);
5036
5037 /* update active user stats */
5038 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
5039
5040 error = nfsrv_credcheck(nd, ctx, nx, nxo);
5041 nfsmerr_if(error);
5042
5043 error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, ctx);
5044 if (!error) {
5045 error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, ctx);
5046 }
5047 if (!error) {
5048 error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, ctx);
5049 }
5050 if (!error) {
5051 error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc, ctx);
5052 }
5053 if (!error) {
5054 error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, ctx);
5055 }
5056 if (!error) {
5057 error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, ctx);
5058 }
5059
5060 nfsm_srv_vattr_init(&attr, NFS_VER3);
5061 attrerr = vnode_getattr(vp, &attr, ctx);
5062
5063 nfsmerr:
5064 if (vp) {
5065 vnode_put(vp);
5066 }
5067
5068 /* assemble reply */
5069 nd->nd_repstat = error;
5070 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
5071 nfsmout_if(error);
5072 *mrepp = nmrep.nmc_mhead;
5073 nfsmout_on_status(nd, error);
5074 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
5075 nfsmout_if(nd->nd_repstat);
5076
5077 nfsm_chain_add_32(error, &nmrep, linkmax);
5078 nfsm_chain_add_32(error, &nmrep, namemax);
5079 nfsm_chain_add_32(error, &nmrep, notrunc);
5080 nfsm_chain_add_32(error, &nmrep, chownres);
5081 nfsm_chain_add_32(error, &nmrep, !case_sensitive);
5082 nfsm_chain_add_32(error, &nmrep, case_preserving);
5083
5084 nfsmout:
5085 nfsm_chain_build_done(error, &nmrep);
5086 if (error) {
5087 nfsm_chain_cleanup(&nmrep);
5088 *mrepp = NULL;
5089 }
5090 return error;
5091 }
5092
5093 /*
5094 * Null operation, used by clients to ping server
5095 */
5096 /* ARGSUSED */
5097 int
nfsrv_null(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5098 nfsrv_null(
5099 struct nfsrv_descript *nd,
5100 struct nfsrv_sock *slp,
5101 __unused vfs_context_t ctx,
5102 mbuf_t *mrepp)
5103 {
5104 int error = NFSERR_RETVOID;
5105 struct nfsm_chain nmrep;
5106
5107 /*
5108 * RPCSEC_GSS context setup ?
5109 */
5110 if (nd->nd_gss_context) {
5111 return nfs_gss_svc_ctx_init(nd, slp, mrepp);
5112 }
5113
5114 nfsm_chain_null(&nmrep);
5115
5116 /* assemble reply */
5117 nd->nd_repstat = error;
5118 error = nfsrv_rephead(nd, slp, &nmrep, 0);
5119 nfsmout_if(error);
5120 *mrepp = nmrep.nmc_mhead;
5121 nfsmout:
5122 nfsm_chain_build_done(error, &nmrep);
5123 if (error) {
5124 nfsm_chain_cleanup(&nmrep);
5125 *mrepp = NULL;
5126 }
5127 return error;
5128 }
5129
5130 /*
5131 * No operation, used for obsolete procedures
5132 */
5133 /* ARGSUSED */
5134 int
nfsrv_noop(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5135 nfsrv_noop(
5136 struct nfsrv_descript *nd,
5137 struct nfsrv_sock *slp,
5138 __unused vfs_context_t ctx,
5139 mbuf_t *mrepp)
5140 {
5141 int error;
5142 struct nfsm_chain nmrep;
5143
5144 nfsm_chain_null(&nmrep);
5145
5146 if (nd->nd_repstat) {
5147 error = nd->nd_repstat;
5148 } else {
5149 error = EPROCUNAVAIL;
5150 }
5151
5152 /* assemble reply */
5153 nd->nd_repstat = error;
5154 error = nfsrv_rephead(nd, slp, &nmrep, 0);
5155 nfsmout_if(error);
5156 *mrepp = nmrep.nmc_mhead;
5157 nfsmout:
5158 nfsm_chain_build_done(error, &nmrep);
5159 if (error) {
5160 nfsm_chain_cleanup(&nmrep);
5161 *mrepp = NULL;
5162 }
5163 return error;
5164 }
5165
5166 const nfsrv_proc_t nfsrv_procs[NFS_NPROCS] = {
5167 nfsrv_null,
5168 nfsrv_getattr,
5169 nfsrv_setattr,
5170 nfsrv_lookup,
5171 nfsrv_access,
5172 nfsrv_readlink,
5173 nfsrv_read,
5174 nfsrv_write,
5175 nfsrv_create,
5176 nfsrv_mkdir,
5177 nfsrv_symlink,
5178 nfsrv_mknod,
5179 nfsrv_remove,
5180 nfsrv_rmdir,
5181 nfsrv_rename,
5182 nfsrv_link,
5183 nfsrv_readdir,
5184 nfsrv_readdirplus,
5185 nfsrv_statfs,
5186 nfsrv_fsinfo,
5187 nfsrv_pathconf,
5188 nfsrv_commit,
5189 nfsrv_noop
5190 };
5191
5192 /*
5193 * Perform access checking for vnodes obtained from file handles that would
5194 * refer to files already opened by a Unix client. You cannot just use
5195 * vnode_authorize() for two reasons.
5196 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5197 * 2 - The owner is to be given access irrespective of mode bits so that
5198 * processes that chmod after opening a file don't break. I don't like
5199 * this because it opens a security hole, but since the nfs server opens
5200 * a security hole the size of a barn door anyhow, what the heck.
5201 *
5202 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5203 * will return EPERM instead of EACCESS. EPERM is always an error.
5204 */
5205
5206 int
nfsrv_authorize(vnode_t vp,vnode_t dvp,kauth_action_t action,vfs_context_t ctx,struct nfs_export_options * nxo,int override)5207 nfsrv_authorize(
5208 vnode_t vp,
5209 vnode_t dvp,
5210 kauth_action_t action,
5211 vfs_context_t ctx,
5212 struct nfs_export_options *nxo,
5213 int override)
5214 {
5215 struct vnode_attr vattr;
5216 int error;
5217
5218 if (action & KAUTH_VNODE_WRITE_RIGHTS) {
5219 /*
5220 * Disallow write attempts on read-only exports;
5221 * unless the file is a socket or a block or character
5222 * device resident on the file system.
5223 */
5224 if (nxo->nxo_flags & NX_READONLY) {
5225 switch (vnode_vtype(vp)) {
5226 case VREG: case VDIR: case VLNK: case VCPLX:
5227 return EROFS;
5228 default:
5229 break;
5230 }
5231 }
5232 }
5233 error = vnode_authorize(vp, dvp, action, ctx);
5234 /*
5235 * Allow certain operations for the owner (reads and writes
5236 * on files that are already open). Picking up from FreeBSD.
5237 */
5238 if (override && (error == EACCES)) {
5239 VATTR_INIT(&vattr);
5240 VATTR_WANTED(&vattr, va_uid);
5241 if ((vnode_getattr(vp, &vattr, ctx) == 0) &&
5242 (kauth_cred_getuid(vfs_context_ucred(ctx)) == vattr.va_uid)) {
5243 error = 0;
5244 }
5245 }
5246 return error;
5247 }
5248
5249 #endif /* CONFIG_NFS_SERVER */
5250