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