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
3414 error = 0;
3415 dpreattrerr = dpostattrerr = attrerr = ENOENT;
3416 vp = xp = dvp = dirp = NULL;
3417 nmreq = &nd->nd_nmreq;
3418 nfsm_chain_null(&nmrep);
3419
3420 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3421 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
3422 nfsm_chain_get_32(error, nmreq, len);
3423 nfsm_name_len_check(error, nd, len);
3424 nfsmerr_if(error);
3425 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
3426 nfsmerr_if(error);
3427
3428 /* update export stats */
3429 NFSStatAdd64(&nx->nx_stats.ops, 1);
3430
3431 /* update active user stats */
3432 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
3433
3434 error = nfsrv_credcheck(nd, ctx, nx, nxo);
3435 nfsmerr_if(error);
3436
3437 /* we're not allowed to link to directories... */
3438 if (vnode_vtype(vp) == VDIR) {
3439 error = NFSERR_ISDIR;
3440 goto out;
3441 }
3442
3443 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3444 if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx, nxo, 0)) != 0) {
3445 goto out;
3446 }
3447
3448 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(vnode_getname(vp)), ctx);
3449 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3450 if (!error) {
3451 error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo);
3452 }
3453 if (dirp) {
3454 if (nd->nd_vers == NFS_VER3) {
3455 nfsm_srv_pre_vattr_init(&dpreattr);
3456 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3457 } else {
3458 vnode_put(dirp);
3459 dirp = NULL;
3460 }
3461 }
3462 if (error) {
3463 goto out;
3464 }
3465 dvp = ni.ni_dvp;
3466 xp = ni.ni_vp;
3467
3468 if (xp != NULL) {
3469 error = EEXIST;
3470 } else if (vnode_mount(vp) != vnode_mount(dvp)) {
3471 error = EXDEV;
3472 } else {
3473 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3474 }
3475
3476 #if CONFIG_MACF
3477 if (!error) {
3478 error = mac_vnode_check_link(ctx, dvp, vp, &ni.ni_cnd);
3479 if (error) {
3480 error = EACCES;
3481 }
3482 }
3483 #endif
3484 if (!error) {
3485 error = VNOP_LINK(vp, dvp, &ni.ni_cnd, ctx);
3486 }
3487
3488 #if CONFIG_FSE
3489 if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, dvp)) {
3490 char *target_path = NULL;
3491 int plen, truncated = 0;
3492 fse_info finfo;
3493
3494 /* build the path to the new link file */
3495 target_path = get_pathbuff();
3496 if (target_path) {
3497 plen = safe_getpath(dvp, ni.ni_cnd.cn_nameptr, target_path, MAXPATHLEN, &truncated);
3498
3499 if (get_fse_info(vp, &finfo, ctx) == 0) {
3500 if (truncated) {
3501 finfo.mode |= FSE_TRUNCATED_PATH;
3502 }
3503 add_fsevent(FSE_CREATE_FILE, ctx,
3504 FSE_ARG_STRING, plen, target_path,
3505 FSE_ARG_FINFO, &finfo,
3506 FSE_ARG_DONE);
3507 }
3508
3509 release_pathbuff(target_path);
3510 }
3511 }
3512 #endif
3513
3514 /*
3515 * nameidone has to happen before we vnode_put(dvp)
3516 * since it may need to release the fs_nodelock on the dvp
3517 */
3518 nameidone(&ni);
3519
3520 if (xp) {
3521 vnode_put(xp);
3522 }
3523 vnode_put(dvp);
3524 out:
3525 if (nd->nd_vers == NFS_VER3) {
3526 nfsm_srv_vattr_init(&attr, NFS_VER3);
3527 attrerr = vnode_getattr(vp, &attr, ctx);
3528 }
3529 if (dirp) {
3530 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3531 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3532 vnode_put(dirp);
3533 dirp = NULL;
3534 }
3535 vnode_put(vp);
3536 vp = NULL;
3537
3538 nfsmerr:
3539 /* assemble reply */
3540 nd->nd_repstat = error;
3541 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3542 nfsmout_if(error);
3543 *mrepp = nmrep.nmc_mhead;
3544 nfsmout_on_status(nd, error);
3545 if (nd->nd_vers == NFS_VER3) {
3546 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
3547 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3548 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3549 }
3550 nfsmout:
3551 nfsm_chain_build_done(error, &nmrep);
3552 if (vp) {
3553 vnode_put(vp);
3554 }
3555 if (error) {
3556 nfsm_chain_cleanup(&nmrep);
3557 *mrepp = NULL;
3558 }
3559 return error;
3560 }
3561
3562 /*
3563 * nfs symbolic link service
3564 */
3565 int
nfsrv_symlink(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3566 nfsrv_symlink(
3567 struct nfsrv_descript *nd,
3568 struct nfsrv_sock *slp,
3569 vfs_context_t ctx,
3570 mbuf_t *mrepp)
3571 {
3572 struct vnode_attr dpreattr, dpostattr, postattr;
3573 struct vnode_attr va, *vap = &va;
3574 struct nameidata ni;
3575 int error, dpreattrerr, dpostattrerr, postattrerr;
3576 uint32_t len = 0, linkdatalen = 0, cnflags;
3577 uid_t saved_uid;
3578 char *linkdata;
3579 vnode_t vp, dvp, dirp;
3580 struct nfs_filehandle nfh = {};
3581 struct nfs_export *nx = NULL;
3582 struct nfs_export_options *nxo = NULL;
3583 uio_t auio = NULL;
3584 UIO_STACKBUF(uio_buf, 1);
3585 struct nfsm_chain *nmreq, nmrep;
3586 __unused const int nfs_vers = nd->nd_vers;
3587 assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3588
3589 error = 0;
3590 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3591 nmreq = &nd->nd_nmreq;
3592 nfsm_chain_null(&nmrep);
3593 linkdata = NULL;
3594 dirp = NULL;
3595
3596 saved_uid = kauth_cred_getuid(nd->nd_cr);
3597
3598 ni.ni_cnd.cn_nameiop = 0;
3599 vp = dvp = NULL;
3600
3601 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3602 nfsm_chain_get_32(error, nmreq, len);
3603 nfsm_name_len_check(error, nd, len);
3604 nfsmerr_if(error);
3605
3606 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, 0, ctx);
3607 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3608 if (!error) {
3609 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3610 if (nx != NULL) {
3611 /* update export stats */
3612 NFSStatAdd64(&nx->nx_stats.ops, 1);
3613
3614 /* update active user stats */
3615 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3616 }
3617 }
3618 if (dirp) {
3619 if (nd->nd_vers == NFS_VER3) {
3620 nfsm_srv_pre_vattr_init(&dpreattr);
3621 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3622 } else {
3623 vnode_put(dirp);
3624 dirp = NULL;
3625 }
3626 }
3627 if (error) {
3628 ni.ni_cnd.cn_nameiop = 0;
3629 goto out1;
3630 }
3631 dvp = ni.ni_dvp;
3632 vp = ni.ni_vp;
3633
3634 VATTR_INIT(vap);
3635 if (nd->nd_vers == NFS_VER3) {
3636 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3637 }
3638 nfsm_chain_get_32(error, nmreq, linkdatalen);
3639 if (!error && (((nd->nd_vers == NFS_VER2) && (linkdatalen > NFS_MAXPATHLEN)) ||
3640 ((nd->nd_vers == NFS_VER3) && (linkdatalen > MAXPATHLEN)))) {
3641 error = NFSERR_NAMETOL;
3642 }
3643 nfsmerr_if(error);
3644 linkdata = kalloc_data(linkdatalen + 1, Z_WAITOK);
3645 if (linkdata) {
3646 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
3647 &uio_buf[0], sizeof(uio_buf));
3648 }
3649 if (!linkdata || !auio) {
3650 error = ENOMEM;
3651 goto out;
3652 }
3653 uio_addiov(auio, CAST_USER_ADDR_T(linkdata), linkdatalen);
3654 error = nfsm_chain_get_uio(nmreq, linkdatalen, auio);
3655 if (!error && (nd->nd_vers == NFS_VER2)) {
3656 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3657 }
3658 nfsmerr_if(error);
3659 *(linkdata + linkdatalen) = '\0';
3660 if (vp) {
3661 error = EEXIST;
3662 goto out;
3663 }
3664
3665 VATTR_SET(vap, va_type, VLNK);
3666 VATTR_CLEAR_ACTIVE(vap, va_data_size);
3667 VATTR_CLEAR_ACTIVE(vap, va_access_time);
3668 /*
3669 * Server policy is to alway use the mapped rpc credential for
3670 * file system object creation. This has the nice side effect of
3671 * enforcing BSD creation semantics
3672 */
3673 VATTR_CLEAR_ACTIVE(vap, va_uid);
3674 VATTR_CLEAR_ACTIVE(vap, va_gid);
3675
3676 /* authorize before creating */
3677 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3678
3679 /* validate given attributes */
3680 if (!error) {
3681 error = vnode_authattr_new(dvp, vap, 0, ctx);
3682 }
3683 if (!error) {
3684 error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
3685 if (error) {
3686 error = EACCES;
3687 }
3688 }
3689
3690 if (!error) {
3691 error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx);
3692 }
3693
3694 if (!error && (nd->nd_vers == NFS_VER3)) {
3695 if (vp == NULL) {
3696 ni.ni_cnd.cn_nameiop = LOOKUP;
3697 #if CONFIG_TRIGGERS
3698 ni.ni_op = OP_LOOKUP;
3699 #endif
3700 ni.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
3701 ni.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
3702 ni.ni_cnd.cn_context = ctx;
3703 ni.ni_startdir = dvp;
3704 ni.ni_usedvp = dvp;
3705 ni.ni_rootdir = rootvnode;
3706 cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
3707 while ((error = lookup(&ni)) == ERECYCLE) {
3708 ni.ni_cnd.cn_flags = cnflags;
3709 ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
3710 ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
3711 }
3712 if (!error) {
3713 vp = ni.ni_vp;
3714 }
3715 }
3716 if (!error) {
3717 error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh);
3718 if (!error) {
3719 nfsm_srv_vattr_init(&postattr, NFS_VER3);
3720 postattrerr = vnode_getattr(vp, &postattr, ctx);
3721 }
3722 }
3723 }
3724
3725 #if CONFIG_FSE
3726 if (nfsrv_fsevents_enabled && !error && vp) {
3727 add_fsevent(FSE_CREATE_FILE, ctx,
3728 FSE_ARG_VNODE, vp,
3729 FSE_ARG_DONE);
3730 }
3731 #endif
3732 out:
3733 /*
3734 * nameidone has to happen before we vnode_put(dvp)
3735 * since it may need to release the fs_nodelock on the dvp
3736 */
3737 nameidone(&ni);
3738 ni.ni_cnd.cn_nameiop = 0;
3739 if (vp) {
3740 vnode_put(vp);
3741 }
3742 vnode_put(dvp);
3743 out1:
3744 if (linkdata) {
3745 kfree_data(linkdata, linkdatalen + 1);
3746 }
3747 if (dirp) {
3748 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3749 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3750 vnode_put(dirp);
3751 dirp = NULL;
3752 }
3753
3754 nfsmerr:
3755 /* assemble reply */
3756 nd->nd_repstat = error;
3757 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3758 NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3759 assert(nfs_vers == nd->nd_vers);
3760 nfsmout_if(error);
3761 *mrepp = nmrep.nmc_mhead;
3762 nfsmout_on_status(nd, error);
3763 if (nd->nd_vers == NFS_VER3) {
3764 if (!nd->nd_repstat) {
3765 if (!nfh.nfh_fhp) {
3766 error = NFSERR_SERVERFAULT;
3767 goto nfsmerr;
3768 }
3769 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3770 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3771 }
3772 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3773 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3774 }
3775 nfsmout:
3776 nfsm_chain_build_done(error, &nmrep);
3777 if (ni.ni_cnd.cn_nameiop) {
3778 /*
3779 * nameidone has to happen before we vnode_put(dvp)
3780 * since it may need to release the fs_nodelock on the dvp
3781 */
3782 nameidone(&ni);
3783
3784 if (vp) {
3785 vnode_put(vp);
3786 }
3787 vnode_put(dvp);
3788 }
3789 if (dirp) {
3790 vnode_put(dirp);
3791 }
3792 if (linkdata) {
3793 kfree_data(linkdata, linkdatalen + 1);
3794 }
3795 if (error) {
3796 nfsm_chain_cleanup(&nmrep);
3797 *mrepp = NULL;
3798 }
3799 return error;
3800 }
3801
3802 /*
3803 * nfs mkdir service
3804 */
3805
3806 int
nfsrv_mkdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)3807 nfsrv_mkdir(
3808 struct nfsrv_descript *nd,
3809 struct nfsrv_sock *slp,
3810 vfs_context_t ctx,
3811 mbuf_t *mrepp)
3812 {
3813 struct vnode_attr dpreattr, dpostattr, postattr;
3814 struct vnode_attr va, *vap = &va;
3815 struct nameidata ni;
3816 int error, dpreattrerr, dpostattrerr, postattrerr;
3817 uint32_t len = 0;
3818 vnode_t vp, dvp, dirp;
3819 struct nfs_filehandle nfh = {};
3820 struct nfs_export *nx = NULL;
3821 struct nfs_export_options *nxo = NULL;
3822 uid_t saved_uid;
3823 kauth_acl_t xacl = NULL;
3824 struct nfsm_chain *nmreq, nmrep;
3825 __unused const int nfs_vers = nd->nd_vers;
3826 assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3827
3828 error = 0;
3829 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3830 nmreq = &nd->nd_nmreq;
3831 nfsm_chain_null(&nmrep);
3832
3833 saved_uid = kauth_cred_getuid(nd->nd_cr);
3834
3835 ni.ni_cnd.cn_nameiop = 0;
3836 vp = dvp = dirp = NULL;
3837
3838 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3839 nfsm_chain_get_32(error, nmreq, len);
3840 nfsm_name_len_check(error, nd, len);
3841 nfsmerr_if(error);
3842
3843 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | WILLBEDIR, UIO_SYSSPACE, 0, ctx);
3844 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3845 if (!error) {
3846 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3847 if (nx != NULL) {
3848 /* update export stats */
3849 NFSStatAdd64(&nx->nx_stats.ops, 1);
3850
3851 /* update active user stats */
3852 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3853 }
3854 }
3855 if (dirp) {
3856 if (nd->nd_vers == NFS_VER3) {
3857 nfsm_srv_pre_vattr_init(&dpreattr);
3858 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
3859 } else {
3860 vnode_put(dirp);
3861 dirp = NULL;
3862 }
3863 }
3864 if (error) {
3865 ni.ni_cnd.cn_nameiop = 0;
3866 goto nfsmerr;
3867 }
3868 dvp = ni.ni_dvp;
3869 vp = ni.ni_vp;
3870
3871 VATTR_INIT(vap);
3872 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3873 nfsmerr_if(error);
3874 VATTR_SET(vap, va_type, VDIR);
3875
3876 if (vp != NULL) {
3877 /*
3878 * nameidone has to happen before we vnode_put(dvp)
3879 * since it may need to release the fs_nodelock on the dvp
3880 */
3881 nameidone(&ni);
3882 vnode_put(dvp);
3883 vnode_put(vp);
3884 error = EEXIST;
3885 goto out;
3886 }
3887
3888 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx, nxo, 0);
3889
3890 /* construct ACL and handle inheritance */
3891 if (!error) {
3892 error = kauth_acl_inherit(dvp,
3893 NULL,
3894 &xacl, /* isdir */
3895 1,
3896 ctx);
3897
3898 if (!error && xacl != NULL) {
3899 VATTR_SET(vap, va_acl, xacl);
3900 }
3901 }
3902
3903 VATTR_CLEAR_ACTIVE(vap, va_data_size);
3904 VATTR_CLEAR_ACTIVE(vap, va_access_time);
3905 /*
3906 * We don't support the S_ISGID bit for directories. Solaris and other
3907 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3908 * any ways.
3909 */
3910 if (VATTR_IS_ACTIVE(vap, va_mode)) {
3911 vap->va_mode &= ~S_ISGID;
3912 }
3913 /*
3914 * Server policy is to alway use the mapped rpc credential for
3915 * file system object creation. This has the nice side effect of
3916 * enforcing BSD creation semantics
3917 */
3918 VATTR_CLEAR_ACTIVE(vap, va_uid);
3919 VATTR_CLEAR_ACTIVE(vap, va_gid);
3920
3921 /* validate new-file security information */
3922 if (!error) {
3923 error = vnode_authattr_new(dvp, vap, 0, ctx);
3924 }
3925 /*
3926 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3927 * sit well with our clients so we map all errors to EPERM.
3928 */
3929 if (error) {
3930 error = EPERM;
3931 }
3932
3933 if (!error) {
3934 error = vn_authorize_mkdir(dvp, &ni.ni_cnd, vap, ctx, NULL);
3935 if (error) {
3936 error = EACCES;
3937 }
3938 }
3939
3940 if (!error) {
3941 error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx);
3942 }
3943
3944 #if CONFIG_FSE
3945 if (nfsrv_fsevents_enabled && !error) {
3946 add_fsevent(FSE_CREATE_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
3947 }
3948 #endif
3949
3950 if (!error && !VATTR_ALL_SUPPORTED(vap)) {
3951 /*
3952 * If some of the requested attributes weren't handled by the VNOP,
3953 * use our fallback code.
3954 */
3955 error = vnode_setattr_fallback(vp, vap, ctx);
3956 }
3957
3958 if (xacl != NULL) {
3959 kauth_acl_free(xacl);
3960 }
3961
3962 if (!error) {
3963 error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh);
3964 if (!error) {
3965 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
3966 postattrerr = vnode_getattr(vp, &postattr, ctx);
3967 if (nd->nd_vers == NFS_VER2) {
3968 error = postattrerr;
3969 }
3970 }
3971 vnode_put(vp);
3972 vp = NULL;
3973 }
3974 /*
3975 * nameidone has to happen before we vnode_put(dvp)
3976 * since it may need to release the fs_nodelock on the dvp
3977 */
3978 nameidone(&ni);
3979 vnode_put(dvp);
3980 out:
3981 ni.ni_cnd.cn_nameiop = 0;
3982
3983 if (dirp) {
3984 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3985 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
3986 vnode_put(dirp);
3987 dirp = NULL;
3988 }
3989
3990 nfsmerr:
3991 /* assemble reply */
3992 nd->nd_repstat = error;
3993 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3994 NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3995 assert(nfs_vers == nd->nd_vers);
3996 nfsmout_if(error);
3997 *mrepp = nmrep.nmc_mhead;
3998 nfsmout_on_status(nd, error);
3999 if (nd->nd_vers == NFS_VER3) {
4000 if (!nd->nd_repstat) {
4001 if (!nfh.nfh_fhp) {
4002 error = NFSERR_SERVERFAULT;
4003 goto nfsmerr;
4004 }
4005 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
4006 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
4007 }
4008 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4009 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4010 } else {
4011 nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len);
4012 if (!error) {
4013 error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
4014 }
4015 }
4016 nfsmout:
4017 nfsm_chain_build_done(error, &nmrep);
4018 if (ni.ni_cnd.cn_nameiop) {
4019 /*
4020 * nameidone has to happen before we vnode_put(dvp)
4021 * since it may need to release the fs_nodelock on the dvp
4022 */
4023 nameidone(&ni);
4024 vnode_put(dvp);
4025 if (vp) {
4026 vnode_put(vp);
4027 }
4028 }
4029 if (dirp) {
4030 vnode_put(dirp);
4031 }
4032 if (error) {
4033 nfsm_chain_cleanup(&nmrep);
4034 *mrepp = NULL;
4035 }
4036 return error;
4037 }
4038
4039 /*
4040 * nfs rmdir service
4041 */
4042 int
nfsrv_rmdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4043 nfsrv_rmdir(
4044 struct nfsrv_descript *nd,
4045 struct nfsrv_sock *slp,
4046 vfs_context_t ctx,
4047 mbuf_t *mrepp)
4048 {
4049 int error, dpreattrerr, dpostattrerr;
4050 uint32_t len = 0;
4051 uid_t saved_uid;
4052 vnode_t vp, dvp, dirp;
4053 struct vnode_attr dpreattr, dpostattr;
4054 struct nfs_filehandle nfh;
4055 struct nfs_export *nx = NULL;
4056 struct nfs_export_options *nxo = NULL;
4057 struct nameidata ni;
4058 struct nfsm_chain *nmreq, nmrep;
4059
4060 error = 0;
4061 dpreattrerr = dpostattrerr = ENOENT;
4062 saved_uid = kauth_cred_getuid(nd->nd_cr);
4063 nmreq = &nd->nd_nmreq;
4064 nfsm_chain_null(&nmrep);
4065
4066 vp = dvp = dirp = NULL;
4067
4068 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4069 nfsm_chain_get_32(error, nmreq, len);
4070 nfsm_name_len_check(error, nd, len);
4071 nfsmerr_if(error);
4072
4073 NDINIT(&ni, DELETE, OP_UNLINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
4074 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
4075 if (!error) {
4076 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
4077 if (nx != NULL) {
4078 /* update export stats */
4079 NFSStatAdd64(&nx->nx_stats.ops, 1);
4080
4081 /* update active user stats */
4082 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
4083 }
4084 }
4085 if (dirp) {
4086 if (nd->nd_vers == NFS_VER3) {
4087 nfsm_srv_pre_vattr_init(&dpreattr);
4088 dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx);
4089 } else {
4090 vnode_put(dirp);
4091 dirp = NULL;
4092 }
4093 }
4094 nfsmerr_if(error);
4095
4096 dvp = ni.ni_dvp;
4097 vp = ni.ni_vp;
4098
4099 if (vnode_vtype(vp) != VDIR) {
4100 error = ENOTDIR;
4101 goto out;
4102 }
4103 /*
4104 * No rmdir "." please.
4105 */
4106 if (dvp == vp) {
4107 error = EINVAL;
4108 goto out;
4109 }
4110 /*
4111 * No rmdir ".." please.
4112 */
4113 if (vnode_parent(dvp) == vp) {
4114 error = EINVAL;
4115 goto out;
4116 }
4117 /*
4118 * The root of a mounted filesystem cannot be deleted.
4119 */
4120 if (vnode_isvroot(vp)) {
4121 error = EBUSY;
4122 }
4123 if (!error) {
4124 error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0);
4125 }
4126 if (!error) {
4127 error = vn_authorize_rmdir(dvp, vp, &ni.ni_cnd, ctx, NULL);
4128 if (error) {
4129 error = EACCES;
4130 }
4131 }
4132
4133 if (!error) {
4134 #if CONFIG_FSE
4135 char *path = NULL;
4136 int plen = 0;
4137 fse_info finfo;
4138
4139 if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) {
4140 plen = MAXPATHLEN;
4141 if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) {
4142 get_fse_info(vp, &finfo, ctx);
4143 } else if (path) {
4144 release_pathbuff(path);
4145 path = NULL;
4146 }
4147 }
4148 #endif /* CONFIG_FSE */
4149
4150 error = VNOP_RMDIR(dvp, vp, &ni.ni_cnd, ctx);
4151
4152 #if CONFIG_FSE
4153 if (path) {
4154 if (!error) {
4155 add_fsevent(FSE_DELETE, ctx,
4156 FSE_ARG_STRING, plen, path,
4157 FSE_ARG_FINFO, &finfo,
4158 FSE_ARG_DONE);
4159 }
4160 release_pathbuff(path);
4161 }
4162 #endif /* CONFIG_FSE */
4163 }
4164 out:
4165 /*
4166 * nameidone has to happen before we vnode_put(dvp)
4167 * since it may need to release the fs_nodelock on the dvp
4168 */
4169 nameidone(&ni);
4170
4171 vnode_put(dvp);
4172 vnode_put(vp);
4173
4174 if (dirp) {
4175 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
4176 dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx);
4177 vnode_put(dirp);
4178 dirp = NULL;
4179 }
4180
4181 nfsmerr:
4182 /* assemble reply */
4183 nd->nd_repstat = error;
4184 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
4185 nfsmout_if(error);
4186 *mrepp = nmrep.nmc_mhead;
4187 nfsmout_on_status(nd, error);
4188 if (nd->nd_vers == NFS_VER3) {
4189 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4190 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4191 }
4192 nfsmout:
4193 nfsm_chain_build_done(error, &nmrep);
4194 if (dirp) {
4195 vnode_put(dirp);
4196 }
4197 if (error) {
4198 nfsm_chain_cleanup(&nmrep);
4199 *mrepp = NULL;
4200 }
4201 return error;
4202 }
4203
4204 /*
4205 * nfs readdir service
4206 * - mallocs what it thinks is enough to read
4207 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4208 * - calls VNOP_READDIR()
4209 * - loops around building the reply
4210 * if the output generated exceeds count break out of loop
4211 * The nfsm_clget macro is used here so that the reply will be packed
4212 * tightly in mbuf clusters.
4213 * - it only knows that it has encountered eof when the VNOP_READDIR()
4214 * reads nothing
4215 * - as such one readdir rpc will return eof false although you are there
4216 * and then the next will return eof
4217 * - it trims out records with d_fileno == 0
4218 * this doesn't matter for Unix clients, but they might confuse clients
4219 * for other os'.
4220 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4221 * than requested, but this may not apply to all filesystems. For
4222 * example, client NFS does not { although it is never remote mounted
4223 * anyhow }
4224 * The alternate call nfsrv_readdirplus() does lookups as well.
4225 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
4226 * are supposed to cover. For readdir, the count is the total number of
4227 * bytes included in everything from the directory's postopattr through
4228 * the EOF flag. For readdirplus, the maxcount is the same, and the
4229 * dircount includes all that except for the entry attributes and handles.
4230 */
4231 int
nfsrv_readdir(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4232 nfsrv_readdir(
4233 struct nfsrv_descript *nd,
4234 struct nfsrv_sock *slp,
4235 vfs_context_t ctx,
4236 mbuf_t *mrepp)
4237 {
4238 struct direntry *dp;
4239 char *cpos, *cend, *rbuf;
4240 size_t rbuf_siz;
4241 vnode_t vp;
4242 struct vnode_attr attr = {};
4243 struct nfs_filehandle nfh;
4244 struct nfs_export *nx;
4245 struct nfs_export_options *nxo;
4246 uio_t auio = NULL;
4247 UIO_STACKBUF(uio_buf, 1);
4248 int len, nlen, rem, xfer, error, attrerr;
4249 int siz, count, fullsiz, eofflag, nentries;
4250 u_quad_t off, toff, verf = 0;
4251 int vnopflag;
4252 struct nfsm_chain *nmreq, nmrep;
4253
4254 error = 0;
4255 attrerr = ENOENT;
4256 count = nentries = 0;
4257 nmreq = &nd->nd_nmreq;
4258 nfsm_chain_null(&nmrep);
4259 rbuf = NULL;
4260 rbuf_siz = 0;
4261 vp = NULL;
4262
4263 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4264
4265 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4266 if (nd->nd_vers == NFS_VER3) {
4267 nfsm_chain_get_64(error, nmreq, toff);
4268 nfsm_chain_get_64(error, nmreq, verf);
4269 } else {
4270 nfsm_chain_get_32(error, nmreq, toff);
4271 }
4272 nfsm_chain_get_32(error, nmreq, count);
4273 nfsmerr_if(error);
4274
4275 off = toff;
4276 siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4277 xfer = NFSRV_NDMAXDATA(nd);
4278 if (siz > xfer) {
4279 siz = xfer;
4280 }
4281 fullsiz = siz;
4282
4283 if (fullsiz == 0) {
4284 error = NFSERR_TOOSMALL;
4285 goto nfsmerr;
4286 }
4287
4288 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4289 nfsmerr_if(error);
4290
4291 /* update export stats */
4292 NFSStatAdd64(&nx->nx_stats.ops, 1);
4293
4294 /* update active user stats */
4295 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4296
4297 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4298 nfsmerr_if(error);
4299
4300 if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2) {
4301 vnopflag |= VNODE_READDIR_NAMEMAX;
4302 }
4303
4304 if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS)) {
4305 vnopflag |= VNODE_READDIR_SEEKOFF32;
4306 }
4307
4308 if (nd->nd_vers == NFS_VER3) {
4309 nfsm_srv_vattr_init(&attr, NFS_VER3);
4310 error = attrerr = vnode_getattr(vp, &attr, ctx);
4311 if (!error && verf && (verf != attr.va_filerev)) {
4312 error = NFSERR_BAD_COOKIE;
4313 }
4314 }
4315 if (!error) {
4316 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4317 }
4318 #if CONFIG_MACF
4319 if (!error) {
4320 if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4321 error = EACCES;
4322 }
4323
4324 if (!error) {
4325 error = mac_vnode_check_readdir(ctx, vp);
4326 }
4327 }
4328 #endif
4329 nfsmerr_if(error);
4330
4331 rbuf_siz = siz;
4332 rbuf = kalloc_data(rbuf_siz, Z_WAITOK);
4333 if (rbuf) {
4334 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4335 &uio_buf[0], sizeof(uio_buf));
4336 }
4337 if (!rbuf || !auio) {
4338 error = ENOMEM;
4339 goto nfsmerr;
4340 }
4341 again:
4342 uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4343 uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4344 eofflag = 0;
4345 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4346 off = uio_offset(auio);
4347
4348 if (nd->nd_vers == NFS_VER3) {
4349 nfsm_srv_vattr_init(&attr, NFS_VER3);
4350 attrerr = vnode_getattr(vp, &attr, ctx);
4351 }
4352 nfsmerr_if(error);
4353
4354 if (uio_resid(auio) != 0) {
4355 siz -= uio_resid(auio);
4356
4357 /* If nothing read, return empty reply with eof set */
4358 if (siz == 0) {
4359 vnode_put(vp);
4360 vp = NULL;
4361 kfree_data(rbuf, rbuf_siz);
4362 /* assemble reply */
4363 nd->nd_repstat = error;
4364 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4365 NFSX_COOKIEVERF(nd->nd_vers) + 2 * NFSX_UNSIGNED);
4366 nfsmout_if(error);
4367 *mrepp = nmrep.nmc_mhead;
4368 nfsmout_on_status(nd, error);
4369 if (nd->nd_vers == NFS_VER3) {
4370 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4371 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4372 }
4373 nfsm_chain_add_32(error, &nmrep, FALSE);
4374 nfsm_chain_add_32(error, &nmrep, TRUE);
4375 nfsm_chain_build_done(error, &nmrep);
4376 return error;
4377 }
4378 }
4379
4380 /*
4381 * Check for degenerate cases of nothing useful read.
4382 * If so go try again
4383 */
4384 cpos = rbuf;
4385 cend = rbuf + siz;
4386 dp = (struct direntry *)cpos;
4387 while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4388 cpos += dp->d_reclen;
4389 dp = (struct direntry *)cpos;
4390 nentries--;
4391 }
4392 if ((cpos >= cend) || (nentries == 0)) {
4393 toff = off;
4394 siz = fullsiz;
4395 goto again;
4396 }
4397
4398 vnode_put(vp);
4399 vp = NULL;
4400
4401 /* assemble reply */
4402 nd->nd_repstat = error;
4403 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4404 NFSX_COOKIEVERF(nd->nd_vers) + siz);
4405 nfsmout_if(error);
4406 *mrepp = nmrep.nmc_mhead;
4407 nfsmout_on_status(nd, error);
4408 nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4409
4410 len = 2 * NFSX_UNSIGNED;
4411 if (nd->nd_vers == NFS_VER3) {
4412 len += NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF;
4413 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4414 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4415 nfsmerr_if(error);
4416 }
4417
4418 /* Loop through the records and build reply */
4419 while ((cpos < cend) && (nentries > 0)) {
4420 if (dp->d_fileno != 0) {
4421 nlen = dp->d_namlen;
4422 if ((nd->nd_vers == NFS_VER2) && (nlen > NFS_MAXNAMLEN)) {
4423 nlen = NFS_MAXNAMLEN;
4424 }
4425 rem = nfsm_rndup(nlen) - nlen;
4426 len += (4 * NFSX_UNSIGNED + nlen + rem);
4427 if (nd->nd_vers == NFS_VER3) {
4428 len += 2 * NFSX_UNSIGNED;
4429 }
4430 if (len > count) {
4431 eofflag = 0;
4432 break;
4433 }
4434 /* Build the directory record xdr from the direntry. */
4435 nfsm_chain_add_32(error, &nmrep, TRUE);
4436 if (nd->nd_vers == NFS_VER3) {
4437 nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4438 } else {
4439 nfsm_chain_add_32(error, &nmrep, dp->d_fileno);
4440 }
4441 nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4442 if (nd->nd_vers == NFS_VER3) {
4443 if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4444 dp->d_seekoff &= 0x00000000ffffffffULL;
4445 }
4446 nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4447 } else {
4448 nfsm_chain_add_32(error, &nmrep, dp->d_seekoff);
4449 }
4450 nfsmerr_if(error);
4451 }
4452 cpos += dp->d_reclen;
4453 dp = (struct direntry *)cpos;
4454 nentries--;
4455 }
4456 nfsm_chain_add_32(error, &nmrep, FALSE);
4457 nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4458 kfree_data(rbuf, rbuf_siz);
4459 goto nfsmout;
4460 nfsmerr:
4461 if (rbuf) {
4462 kfree_data(rbuf, rbuf_siz);
4463 }
4464 if (vp) {
4465 vnode_put(vp);
4466 }
4467 nd->nd_repstat = error;
4468 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers));
4469 nfsmout_if(error);
4470 *mrepp = nmrep.nmc_mhead;
4471 nfsmout_on_status(nd, error);
4472 if (nd->nd_vers == NFS_VER3) {
4473 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4474 }
4475 nfsmout:
4476 nfsm_chain_build_done(error, &nmrep);
4477 if (error) {
4478 nfsm_chain_cleanup(&nmrep);
4479 *mrepp = NULL;
4480 }
4481 return error;
4482 }
4483
4484 int
nfsrv_readdirplus(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4485 nfsrv_readdirplus(
4486 struct nfsrv_descript *nd,
4487 struct nfsrv_sock *slp,
4488 vfs_context_t ctx,
4489 mbuf_t *mrepp)
4490 {
4491 struct direntry *dp;
4492 char *cpos, *cend, *rbuf;
4493 size_t rbuf_siz;
4494 vnode_t vp, nvp;
4495 struct nfs_filehandle dnfh, nfh;
4496 struct nfs_export *nx;
4497 struct nfs_export_options *nxo;
4498 uio_t auio = NULL;
4499 UIO_STACKBUF(uio_buf, 1);
4500 struct vnode_attr attr, va, *vap = &va;
4501 int len, nlen, rem, xfer, error, attrerr, gotfh, gotattr;
4502 int siz, dircount, maxcount, fullsiz, eofflag, dirlen, nentries, isdotdot;
4503 u_quad_t off, toff, verf;
4504 int vnopflag;
4505 struct nfsm_chain *nmreq, nmrep;
4506
4507 error = 0;
4508 attrerr = ENOENT;
4509 nentries = 0;
4510 nmreq = &nd->nd_nmreq;
4511 nfsm_chain_null(&nmrep);
4512 rbuf = NULL;
4513 rbuf_siz = 0;
4514 vp = NULL;
4515 dircount = maxcount = 0;
4516
4517 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4518
4519 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
4520 nfsm_chain_get_64(error, nmreq, toff);
4521 nfsm_chain_get_64(error, nmreq, verf);
4522 nfsm_chain_get_32(error, nmreq, dircount);
4523 nfsm_chain_get_32(error, nmreq, maxcount);
4524 nfsmerr_if(error);
4525
4526 off = toff;
4527 xfer = NFSRV_NDMAXDATA(nd);
4528 dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4529 if (dircount > xfer) {
4530 dircount = xfer;
4531 }
4532 fullsiz = siz = dircount;
4533 maxcount = ((maxcount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4534 if (maxcount > xfer) {
4535 maxcount = xfer;
4536 }
4537
4538 if (maxcount == 0) {
4539 error = NFSERR_TOOSMALL;
4540 goto nfsmerr;
4541 }
4542
4543 error = nfsrv_fhtovp(&dnfh, nd, &vp, &nx, &nxo);
4544 nfsmerr_if(error);
4545
4546 /* update export stats */
4547 NFSStatAdd64(&nx->nx_stats.ops, 1);
4548
4549 /* update active user stats */
4550 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4551
4552 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4553 nfsmerr_if(error);
4554
4555 if (nxo->nxo_flags & NX_32BITCLIENTS) {
4556 vnopflag |= VNODE_READDIR_SEEKOFF32;
4557 }
4558
4559 if (nxo->nxo_flags & NX_MANGLEDNAMES) {
4560 vnopflag |= VNODE_READDIR_NAMEMAX;
4561 }
4562
4563 nfsm_srv_vattr_init(&attr, NFS_VER3);
4564 error = attrerr = vnode_getattr(vp, &attr, ctx);
4565 if (!error && verf && (verf != attr.va_filerev)) {
4566 error = NFSERR_BAD_COOKIE;
4567 }
4568 if (!error) {
4569 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4570 }
4571 #if CONFIG_MACF
4572 if (!error) {
4573 if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4574 error = EACCES;
4575 }
4576
4577 if (!error) {
4578 error = mac_vnode_check_readdir(ctx, vp);
4579 }
4580 }
4581 #endif
4582 nfsmerr_if(error);
4583
4584 rbuf_siz = siz;
4585 rbuf = kalloc_data(siz, Z_WAITOK);
4586 if (rbuf) {
4587 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
4588 &uio_buf[0], sizeof(uio_buf));
4589 }
4590 if (!rbuf || !auio) {
4591 error = ENOMEM;
4592 goto nfsmerr;
4593 }
4594
4595 again:
4596 uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
4597 uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
4598 eofflag = 0;
4599 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4600 off = uio_offset(auio);
4601 nfsm_srv_vattr_init(&attr, NFS_VER3);
4602 attrerr = vnode_getattr(vp, &attr, ctx);
4603 nfsmerr_if(error);
4604
4605 if (uio_resid(auio) != 0) {
4606 siz -= uio_resid(auio);
4607
4608 /* If nothing read, return empty reply with eof set */
4609 if (siz == 0) {
4610 vnode_put(vp);
4611 vp = NULL;
4612 kfree_data(rbuf, rbuf_siz);
4613 /* assemble reply */
4614 nd->nd_repstat = error;
4615 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR +
4616 NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED);
4617 nfsmout_if(error);
4618 *mrepp = nmrep.nmc_mhead;
4619 nfsmout_on_status(nd, error);
4620 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4621 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4622 nfsm_chain_add_32(error, &nmrep, FALSE);
4623 nfsm_chain_add_32(error, &nmrep, TRUE);
4624 nfsm_chain_build_done(error, &nmrep);
4625 return error;
4626 }
4627 }
4628
4629 /*
4630 * Check for degenerate cases of nothing useful read.
4631 * If so go try again
4632 */
4633 cpos = rbuf;
4634 cend = rbuf + siz;
4635 dp = (struct direntry *)cpos;
4636 while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4637 cpos += dp->d_reclen;
4638 dp = (struct direntry *)cpos;
4639 nentries--;
4640 }
4641 if ((cpos >= cend) || (nentries == 0)) {
4642 toff = off;
4643 siz = fullsiz;
4644 goto again;
4645 }
4646
4647 /*
4648 * Probe the directory to see if the filesystem supports VGET.
4649 */
4650 if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)attr.va_fileid, &nvp, ctx))) {
4651 if (error == ENOTSUP) { /* let others get passed back */
4652 error = NFSERR_NOTSUPP;
4653 }
4654 goto nfsmerr;
4655 }
4656 vnode_put(nvp);
4657
4658 /* assemble reply */
4659 nd->nd_repstat = error;
4660 error = nfsrv_rephead(nd, slp, &nmrep, maxcount);
4661 nfsmout_if(error);
4662 *mrepp = nmrep.nmc_mhead;
4663 nfsmout_on_status(nd, error);
4664 nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4665
4666 dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
4667 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4668 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4669 nfsmerr_if(error);
4670
4671 /* Loop through the records and build reply */
4672 while ((cpos < cend) && (nentries > 0)) {
4673 if (dp->d_fileno != 0) {
4674 nlen = dp->d_namlen;
4675 rem = nfsm_rndup(nlen) - nlen;
4676 gotfh = gotattr = 1;
4677
4678 /* Got to get the vnode for lookup per entry. */
4679 if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, ctx)) {
4680 /* Can't get the vnode... so no fh or attrs */
4681 gotfh = gotattr = 0;
4682 } else {
4683 isdotdot = ((dp->d_namlen == 2) &&
4684 (dp->d_name[0] == '.') && (dp->d_name[1] == '.'));
4685 if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, ctx, &nfh)) {
4686 gotfh = 0;
4687 }
4688 nfsm_srv_vattr_init(vap, NFS_VER3);
4689 if (vnode_getattr(nvp, vap, ctx)) {
4690 gotattr = 0;
4691 }
4692 vnode_put(nvp);
4693 }
4694
4695 /*
4696 * If either the dircount or maxcount will be
4697 * exceeded, get out now. Both of these lengths
4698 * are calculated conservatively, including all
4699 * XDR overheads.
4700 */
4701 len += 8 * NFSX_UNSIGNED + nlen + rem;
4702 if (gotattr) {
4703 len += NFSX_V3FATTR;
4704 }
4705 if (gotfh) {
4706 len += NFSX_UNSIGNED + nfsm_rndup(nfh.nfh_len);
4707 }
4708 dirlen += 6 * NFSX_UNSIGNED + nlen + rem;
4709 if ((len > maxcount) || (dirlen > dircount)) {
4710 eofflag = 0;
4711 break;
4712 }
4713
4714 /* Build the directory record xdr from the direntry. */
4715 nfsm_chain_add_32(error, &nmrep, TRUE);
4716 nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4717 nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4718 if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4719 dp->d_seekoff &= 0x00000000ffffffffULL;
4720 }
4721 nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4722 nfsm_chain_add_postop_attr(error, nd, &nmrep, (gotattr ? 0 : ENOENT), vap);
4723 if (gotfh) {
4724 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
4725 } else {
4726 nfsm_chain_add_32(error, &nmrep, FALSE);
4727 }
4728 nfsmerr_if(error);
4729 }
4730 cpos += dp->d_reclen;
4731 dp = (struct direntry *)cpos;
4732 nentries--;
4733 }
4734 vnode_put(vp);
4735 vp = NULL;
4736 nfsm_chain_add_32(error, &nmrep, FALSE);
4737 nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4738 kfree_data(rbuf, rbuf_siz);
4739 goto nfsmout;
4740 nfsmerr:
4741 if (rbuf) {
4742 kfree_data(rbuf, rbuf_siz);
4743 }
4744 nd->nd_repstat = error;
4745 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR);
4746 nfsmout_if(error);
4747 *mrepp = nmrep.nmc_mhead;
4748 nfsmout_on_status(nd, error);
4749 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4750 nfsmout:
4751 nfsm_chain_build_done(error, &nmrep);
4752 if (vp) {
4753 vnode_put(vp);
4754 }
4755 if (error) {
4756 nfsm_chain_cleanup(&nmrep);
4757 *mrepp = NULL;
4758 }
4759 return error;
4760 }
4761
4762 /*
4763 * nfs commit service
4764 */
4765 int
nfsrv_commit(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4766 nfsrv_commit(
4767 struct nfsrv_descript *nd,
4768 struct nfsrv_sock *slp,
4769 vfs_context_t ctx,
4770 mbuf_t *mrepp)
4771 {
4772 vnode_t vp;
4773 struct nfs_filehandle nfh;
4774 struct nfs_export *nx = NULL;
4775 struct nfs_export_options *nxo;
4776 int error, preattrerr, postattrerr, count;
4777 struct vnode_attr preattr, postattr;
4778 u_quad_t off;
4779 struct nfsm_chain *nmreq, nmrep;
4780
4781 error = 0;
4782 preattrerr = postattrerr = ENOENT;
4783 nmreq = &nd->nd_nmreq;
4784 nfsm_chain_null(&nmrep);
4785 vp = NULL;
4786
4787 /*
4788 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4789 * count parameters, so those arguments are useless (someday maybe).
4790 */
4791
4792 nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
4793 nfsm_chain_get_64(error, nmreq, off);
4794 nfsm_chain_get_32(error, nmreq, count);
4795 nfsmerr_if(error);
4796
4797 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4798 nfsmerr_if(error);
4799
4800 /* update export stats */
4801 NFSStatAdd64(&nx->nx_stats.ops, 1);
4802
4803 /* update active user stats */
4804 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4805
4806 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4807 nfsmerr_if(error);
4808
4809 /* This must identify a file system object of type, NF3REG */
4810 if (vnode_vtype(vp) != VREG) {
4811 error = NFSERR_BADTYPE;
4812 goto nfsmerr;
4813 }
4814
4815 nfsm_srv_pre_vattr_init(&preattr);
4816 preattrerr = vnode_getattr(vp, &preattr, ctx);
4817
4818 error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
4819
4820 nfsm_srv_vattr_init(&postattr, 1);
4821 postattrerr = vnode_getattr(vp, &postattr, ctx);
4822
4823 nfsmerr:
4824 if (vp) {
4825 vnode_put(vp);
4826 }
4827
4828 /* assemble reply */
4829 nd->nd_repstat = error;
4830 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
4831 nfsmout_if(error);
4832 *mrepp = nmrep.nmc_mhead;
4833 nfsmout_on_status(nd, error);
4834 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4835 preattrerr, &preattr, postattrerr, &postattr);
4836 if (!nd->nd_repstat) {
4837 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
4838 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
4839 }
4840 nfsmout:
4841 nfsm_chain_build_done(error, &nmrep);
4842 if (error) {
4843 nfsm_chain_cleanup(&nmrep);
4844 *mrepp = NULL;
4845 }
4846 return error;
4847 }
4848
4849 /*
4850 * nfs statfs service
4851 */
4852 int
nfsrv_statfs(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4853 nfsrv_statfs(
4854 struct nfsrv_descript *nd,
4855 struct nfsrv_sock *slp,
4856 vfs_context_t ctx,
4857 mbuf_t *mrepp)
4858 {
4859 struct vfs_attr va = {};
4860 int error, attrerr;
4861 vnode_t vp;
4862 struct vnode_attr attr;
4863 struct nfs_filehandle nfh;
4864 struct nfs_export *nx;
4865 struct nfs_export_options *nxo;
4866 off_t blksize;
4867 struct nfsm_chain *nmreq, nmrep;
4868
4869 error = 0;
4870 attrerr = ENOENT;
4871 nmreq = &nd->nd_nmreq;
4872 nfsm_chain_null(&nmrep);
4873 vp = NULL;
4874 blksize = 512;
4875
4876 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4877 nfsmerr_if(error);
4878 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4879 nfsmerr_if(error);
4880
4881 /* update export stats */
4882 NFSStatAdd64(&nx->nx_stats.ops, 1);
4883
4884 /* update active user stats */
4885 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4886
4887 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4888 nfsmerr_if(error);
4889
4890 VFSATTR_INIT(&va);
4891 VFSATTR_WANTED(&va, f_blocks);
4892 VFSATTR_WANTED(&va, f_bfree);
4893 VFSATTR_WANTED(&va, f_bavail);
4894 VFSATTR_WANTED(&va, f_files);
4895 VFSATTR_WANTED(&va, f_ffree);
4896 error = vfs_getattr(vnode_mount(vp), &va, ctx);
4897 blksize = vfs_statfs(vnode_mount(vp))->f_bsize;
4898
4899 if (nd->nd_vers == NFS_VER3) {
4900 nfsm_srv_vattr_init(&attr, nd->nd_vers);
4901 attrerr = vnode_getattr(vp, &attr, ctx);
4902 }
4903
4904 nfsmerr:
4905 if (vp) {
4906 vnode_put(vp);
4907 }
4908
4909 /* assemble reply */
4910 nd->nd_repstat = error;
4911 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_STATFS(nd->nd_vers));
4912 nfsmout_if(error);
4913 *mrepp = nmrep.nmc_mhead;
4914 nfsmout_on_status(nd, error);
4915 if (nd->nd_vers == NFS_VER3) {
4916 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4917 }
4918 nfsmout_if(nd->nd_repstat);
4919
4920 if (nd->nd_vers == NFS_VER3) {
4921 nfsm_chain_add_64(error, &nmrep, va.f_blocks * blksize);
4922 nfsm_chain_add_64(error, &nmrep, va.f_bfree * blksize);
4923 nfsm_chain_add_64(error, &nmrep, va.f_bavail * blksize);
4924 nfsm_chain_add_64(error, &nmrep, va.f_files);
4925 nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4926 nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4927 nfsm_chain_add_32(error, &nmrep, 0); /* invarsec */
4928 } else {
4929 nfsm_chain_add_32(error, &nmrep, NFS_V2MAXDATA);
4930 nfsm_chain_add_32(error, &nmrep, blksize);
4931 nfsm_chain_add_32(error, &nmrep, va.f_blocks);
4932 nfsm_chain_add_32(error, &nmrep, va.f_bfree);
4933 nfsm_chain_add_32(error, &nmrep, va.f_bavail);
4934 }
4935 nfsmout:
4936 nfsm_chain_build_done(error, &nmrep);
4937 if (error) {
4938 nfsm_chain_cleanup(&nmrep);
4939 *mrepp = NULL;
4940 }
4941 return error;
4942 }
4943
4944 /*
4945 * nfs fsinfo service
4946 */
4947 int
nfsrv_fsinfo(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)4948 nfsrv_fsinfo(
4949 struct nfsrv_descript *nd,
4950 struct nfsrv_sock *slp,
4951 vfs_context_t ctx,
4952 mbuf_t *mrepp)
4953 {
4954 int error, attrerr, prefsize, maxsize;
4955 vnode_t vp;
4956 struct vnode_attr attr;
4957 struct nfs_filehandle nfh;
4958 struct nfs_export *nx;
4959 struct nfs_export_options *nxo;
4960 struct nfsm_chain *nmreq, nmrep;
4961
4962 error = 0;
4963 attrerr = ENOENT;
4964 nmreq = &nd->nd_nmreq;
4965 nfsm_chain_null(&nmrep);
4966 vp = NULL;
4967
4968 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4969 nfsmerr_if(error);
4970 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4971 nfsmerr_if(error);
4972
4973 /* update export stats */
4974 NFSStatAdd64(&nx->nx_stats.ops, 1);
4975
4976 /* update active user stats */
4977 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
4978
4979 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4980 nfsmerr_if(error);
4981
4982 nfsm_srv_vattr_init(&attr, NFS_VER3);
4983 attrerr = vnode_getattr(vp, &attr, ctx);
4984
4985 nfsmerr:
4986 if (vp) {
4987 vnode_put(vp);
4988 }
4989
4990 /* assemble reply */
4991 nd->nd_repstat = error;
4992 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
4993 nfsmout_if(error);
4994 *mrepp = nmrep.nmc_mhead;
4995 nfsmout_on_status(nd, error);
4996 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4997 nfsmout_if(nd->nd_repstat);
4998
4999 /*
5000 * XXX There should be file system VFS OP(s) to get this information.
5001 * For now, assume our usual NFS defaults.
5002 */
5003 if (slp->ns_sotype == SOCK_DGRAM) {
5004 maxsize = NFS_MAXDGRAMDATA;
5005 prefsize = NFS_PREFDGRAMDATA;
5006 } else {
5007 maxsize = prefsize = NFSRV_MAXDATA;
5008 }
5009
5010 nfsm_chain_add_32(error, &nmrep, maxsize);
5011 nfsm_chain_add_32(error, &nmrep, prefsize);
5012 nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5013 nfsm_chain_add_32(error, &nmrep, maxsize);
5014 nfsm_chain_add_32(error, &nmrep, prefsize);
5015 nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5016 nfsm_chain_add_32(error, &nmrep, prefsize);
5017 nfsm_chain_add_64(error, &nmrep, 0xffffffffffffffffULL);
5018 nfsm_chain_add_32(error, &nmrep, 0);
5019 nfsm_chain_add_32(error, &nmrep, 1);
5020 /* XXX link/symlink support should be taken from volume capabilities */
5021 nfsm_chain_add_32(error, &nmrep,
5022 NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK |
5023 NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME);
5024
5025 nfsmout:
5026 nfsm_chain_build_done(error, &nmrep);
5027 if (error) {
5028 nfsm_chain_cleanup(&nmrep);
5029 *mrepp = NULL;
5030 }
5031 return error;
5032 }
5033
5034 /*
5035 * nfs pathconf service
5036 */
5037 int
nfsrv_pathconf(struct nfsrv_descript * nd,struct nfsrv_sock * slp,vfs_context_t ctx,mbuf_t * mrepp)5038 nfsrv_pathconf(
5039 struct nfsrv_descript *nd,
5040 struct nfsrv_sock *slp,
5041 vfs_context_t ctx,
5042 mbuf_t *mrepp)
5043 {
5044 int error, attrerr, linkmax = 0, namemax = 0;
5045 int chownres = 0, notrunc = 0, case_sensitive = 0, case_preserving = 0;
5046 vnode_t vp;
5047 struct vnode_attr attr;
5048 struct nfs_filehandle nfh;
5049 struct nfs_export *nx;
5050 struct nfs_export_options *nxo;
5051 struct nfsm_chain *nmreq, nmrep;
5052
5053 error = 0;
5054 attrerr = ENOENT;
5055 nmreq = &nd->nd_nmreq;
5056 nfsm_chain_null(&nmrep);
5057 vp = NULL;
5058
5059 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
5060 nfsmerr_if(error);
5061 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
5062 nfsmerr_if(error);
5063
5064 /* update export stats */
5065 NFSStatAdd64(&nx->nx_stats.ops, 1);
5066
5067 /* update active user stats */
5068 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(nd->nd_cr), 1, 0, 0);
5069
5070 error = nfsrv_credcheck(nd, ctx, nx, nxo);
5071 nfsmerr_if(error);
5072
5073 error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, ctx);
5074 if (!error) {
5075 error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, ctx);
5076 }
5077 if (!error) {
5078 error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, ctx);
5079 }
5080 if (!error) {
5081 error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc, ctx);
5082 }
5083 if (!error) {
5084 error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, ctx);
5085 }
5086 if (!error) {
5087 error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, ctx);
5088 }
5089
5090 nfsm_srv_vattr_init(&attr, NFS_VER3);
5091 attrerr = vnode_getattr(vp, &attr, ctx);
5092
5093 nfsmerr:
5094 if (vp) {
5095 vnode_put(vp);
5096 }
5097
5098 /* assemble reply */
5099 nd->nd_repstat = error;
5100 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
5101 nfsmout_if(error);
5102 *mrepp = nmrep.nmc_mhead;
5103 nfsmout_on_status(nd, error);
5104 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
5105 nfsmout_if(nd->nd_repstat);
5106
5107 nfsm_chain_add_32(error, &nmrep, linkmax);
5108 nfsm_chain_add_32(error, &nmrep, namemax);
5109 nfsm_chain_add_32(error, &nmrep, notrunc);
5110 nfsm_chain_add_32(error, &nmrep, chownres);
5111 nfsm_chain_add_32(error, &nmrep, !case_sensitive);
5112 nfsm_chain_add_32(error, &nmrep, case_preserving);
5113
5114 nfsmout:
5115 nfsm_chain_build_done(error, &nmrep);
5116 if (error) {
5117 nfsm_chain_cleanup(&nmrep);
5118 *mrepp = NULL;
5119 }
5120 return error;
5121 }
5122
5123 /*
5124 * Null operation, used by clients to ping server
5125 */
5126 /* ARGSUSED */
5127 int
nfsrv_null(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5128 nfsrv_null(
5129 struct nfsrv_descript *nd,
5130 struct nfsrv_sock *slp,
5131 __unused vfs_context_t ctx,
5132 mbuf_t *mrepp)
5133 {
5134 int error = NFSERR_RETVOID;
5135 struct nfsm_chain nmrep;
5136
5137 /*
5138 * RPCSEC_GSS context setup ?
5139 */
5140 if (nd->nd_gss_context) {
5141 return nfs_gss_svc_ctx_init(nd, slp, mrepp);
5142 }
5143
5144 nfsm_chain_null(&nmrep);
5145
5146 /* assemble reply */
5147 nd->nd_repstat = error;
5148 error = nfsrv_rephead(nd, slp, &nmrep, 0);
5149 nfsmout_if(error);
5150 *mrepp = nmrep.nmc_mhead;
5151 nfsmout:
5152 nfsm_chain_build_done(error, &nmrep);
5153 if (error) {
5154 nfsm_chain_cleanup(&nmrep);
5155 *mrepp = NULL;
5156 }
5157 return error;
5158 }
5159
5160 /*
5161 * No operation, used for obsolete procedures
5162 */
5163 /* ARGSUSED */
5164 int
nfsrv_noop(struct nfsrv_descript * nd,struct nfsrv_sock * slp,__unused vfs_context_t ctx,mbuf_t * mrepp)5165 nfsrv_noop(
5166 struct nfsrv_descript *nd,
5167 struct nfsrv_sock *slp,
5168 __unused vfs_context_t ctx,
5169 mbuf_t *mrepp)
5170 {
5171 int error;
5172 struct nfsm_chain nmrep;
5173
5174 nfsm_chain_null(&nmrep);
5175
5176 if (nd->nd_repstat) {
5177 error = nd->nd_repstat;
5178 } else {
5179 error = EPROCUNAVAIL;
5180 }
5181
5182 /* assemble reply */
5183 nd->nd_repstat = error;
5184 error = nfsrv_rephead(nd, slp, &nmrep, 0);
5185 nfsmout_if(error);
5186 *mrepp = nmrep.nmc_mhead;
5187 nfsmout:
5188 nfsm_chain_build_done(error, &nmrep);
5189 if (error) {
5190 nfsm_chain_cleanup(&nmrep);
5191 *mrepp = NULL;
5192 }
5193 return error;
5194 }
5195
5196 const nfsrv_proc_t nfsrv_procs[NFS_NPROCS] = {
5197 nfsrv_null,
5198 nfsrv_getattr,
5199 nfsrv_setattr,
5200 nfsrv_lookup,
5201 nfsrv_access,
5202 nfsrv_readlink,
5203 nfsrv_read,
5204 nfsrv_write,
5205 nfsrv_create,
5206 nfsrv_mkdir,
5207 nfsrv_symlink,
5208 nfsrv_mknod,
5209 nfsrv_remove,
5210 nfsrv_rmdir,
5211 nfsrv_rename,
5212 nfsrv_link,
5213 nfsrv_readdir,
5214 nfsrv_readdirplus,
5215 nfsrv_statfs,
5216 nfsrv_fsinfo,
5217 nfsrv_pathconf,
5218 nfsrv_commit,
5219 nfsrv_noop
5220 };
5221
5222 /*
5223 * Perform access checking for vnodes obtained from file handles that would
5224 * refer to files already opened by a Unix client. You cannot just use
5225 * vnode_authorize() for two reasons.
5226 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5227 * 2 - The owner is to be given access irrespective of mode bits so that
5228 * processes that chmod after opening a file don't break. I don't like
5229 * this because it opens a security hole, but since the nfs server opens
5230 * a security hole the size of a barn door anyhow, what the heck.
5231 *
5232 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5233 * will return EPERM instead of EACCESS. EPERM is always an error.
5234 */
5235
5236 int
nfsrv_authorize(vnode_t vp,vnode_t dvp,kauth_action_t action,vfs_context_t ctx,struct nfs_export_options * nxo,int override)5237 nfsrv_authorize(
5238 vnode_t vp,
5239 vnode_t dvp,
5240 kauth_action_t action,
5241 vfs_context_t ctx,
5242 struct nfs_export_options *nxo,
5243 int override)
5244 {
5245 struct vnode_attr vattr;
5246 int error;
5247
5248 if (action & KAUTH_VNODE_WRITE_RIGHTS) {
5249 /*
5250 * Disallow write attempts on read-only exports;
5251 * unless the file is a socket or a block or character
5252 * device resident on the file system.
5253 */
5254 if (nxo->nxo_flags & NX_READONLY) {
5255 switch (vnode_vtype(vp)) {
5256 case VREG: case VDIR: case VLNK: case VCPLX:
5257 return EROFS;
5258 default:
5259 break;
5260 }
5261 }
5262 }
5263 error = vnode_authorize(vp, dvp, action, ctx);
5264 /*
5265 * Allow certain operations for the owner (reads and writes
5266 * on files that are already open). Picking up from FreeBSD.
5267 */
5268 if (override && (error == EACCES)) {
5269 VATTR_INIT(&vattr);
5270 VATTR_WANTED(&vattr, va_uid);
5271 if ((vnode_getattr(vp, &vattr, ctx) == 0) &&
5272 (kauth_cred_getuid(vfs_context_ucred(ctx)) == vattr.va_uid)) {
5273 error = 0;
5274 }
5275 }
5276 return error;
5277 }
5278
5279 #endif /* CONFIG_NFS_SERVER */
5280