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