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
118int nfsd_thread_count = 0;
119int nfsd_thread_max = 0;
120static LCK_GRP_DECLARE(nfsd_lck_grp, "nfsd");
121LCK_MTX_DECLARE(nfsd_mutex, &nfsd_lck_grp);
122struct nfsd_head nfsd_head, nfsd_queue;
123
124LCK_GRP_DECLARE(nfsrv_slp_rwlock_group, "nfsrv-slp-rwlock");
125LCK_GRP_DECLARE(nfsrv_slp_mutex_group, "nfsrv-slp-mutex");
126struct nfsrv_sockhead nfsrv_socklist, nfsrv_sockwg,
127 nfsrv_sockwait, nfsrv_sockwork;
128struct nfsrv_sock *nfsrv_udpsock = NULL;
129struct nfsrv_sock *nfsrv_udp6sock = NULL;
130
131/* NFS exports */
132struct nfsrv_expfs_list nfsrv_exports;
133struct nfsrv_export_hashhead *nfsrv_export_hashtbl = NULL;
134int nfsrv_export_hash_size = NFSRVEXPHASHSZ;
135u_long nfsrv_export_hash;
136static LCK_GRP_DECLARE(nfsrv_export_rwlock_group, "nfsrv-export-rwlock");
137LCK_RW_DECLARE(nfsrv_export_rwlock, &nfsrv_export_rwlock_group);
138
139#if CONFIG_FSE
140/* NFS server file modification event generator */
141struct nfsrv_fmod_hashhead *nfsrv_fmod_hashtbl;
142u_long nfsrv_fmod_hash;
143static LCK_GRP_DECLARE(nfsrv_fmod_grp, "nfsrv_fmod");
144LCK_MTX_DECLARE(nfsrv_fmod_mutex, &nfsrv_fmod_grp);
145static int nfsrv_fmod_timer_on = 0;
146int nfsrv_fsevents_enabled = 1;
147#endif
148
149/* NFS server timers */
150#if CONFIG_FSE
151thread_call_t nfsrv_fmod_timer_call;
152#endif
153thread_call_t nfsrv_idlesock_timer_call;
154thread_call_t nfsrv_wg_timer_call;
155int nfsrv_wg_timer_on;
156
157/* globals for the active user list */
158uint32_t nfsrv_user_stat_enabled = 1;
159uint32_t nfsrv_user_stat_node_count = 0;
160uint32_t nfsrv_user_stat_max_idle_sec = NFSRV_USER_STAT_DEF_IDLE_SEC;
161uint32_t nfsrv_user_stat_max_nodes = NFSRV_USER_STAT_DEF_MAX_NODES;
162LCK_GRP_DECLARE(nfsrv_active_user_mutex_group, "nfs-active-user-mutex");
163
164int nfsrv_wg_delay = NFSRV_WGATHERDELAY * 1000;
165int nfsrv_wg_delay_v3 = 0;
166
167int nfsrv_async = 0;
168
169int nfsrv_authorize(vnode_t, vnode_t, kauth_action_t, vfs_context_t, struct nfs_export_options*, int);
170int nfsrv_wg_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
171void nfsrv_modified(vnode_t, vfs_context_t);
172
173extern 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
182static volatile UInt32 nfsrv_initted = NFSRV_NOT_INITIALIZED;
183
184int
185nfsrv_is_initialized(void)
186{
187 return nfsrv_initted == NFSRV_INITIALIZED;
188}
189
190void
191nfsrv_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(milliseconds: 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, hashmask: &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(func: nfsrv_fmod_timer, NULL);
219#endif
220 nfsrv_idlesock_timer_call = thread_call_allocate(func: nfsrv_idlesock_timer, NULL);
221 nfsrv_wg_timer_call = thread_call_allocate(func: 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 */
266int
267nfsrv_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(cred: 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, vap: &vattr, ctx);
374
375nfsmerr:
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 }
386nfsmout:
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 */
401int
402nfsrv_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(cred: 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, vap: &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, va: &vattr);
454 nfsmerr_if(error);
455#endif
456
457 vnode_put(vp);
458 vp = NULL;
459
460nfsmerr:
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);
468nfsmout:
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 */
483int
484nfsrv_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(cred: 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, vap: &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(cred: 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(cred: nd->nd_cr, gid: vap->va_gid, resultp: &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, actionp: &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: (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(ts: &current_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, vap: &postattr, ctx);
626 if (!error) {
627 error = postattrerr;
628 }
629 }
630
631nfsmerr:
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 }
648nfsmout:
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 */
660int
661nfsrv_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(cred: 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, vp: ni.ni_vp, FREAD)) {
702 error = EACCES;
703 if (dirp) {
704 vnode_put(vp: dirp);
705 dirp = NULL;
706 }
707
708 if (ni.ni_vp) {
709 vnode_put(vp: 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(vp: dirp, vap: &dirattr, ctx);
719 }
720 vnode_put(vp: 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
734nfsmerr:
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 }
754nfsmout:
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 */
766int
767nfsrv_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(a_iovcount: mpcnt, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ, a_buf_p: uio_bufp, a_buffer_size: uio_buflen);
809 if (!auio) {
810 error = ENOMEM;
811 }
812 nfsmerr_if(error);
813
814 for (mp = mpath; mp; mp = mbuf_next(mbuf: mp)) {
815 uio_addiov(a_uio: auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), a_length: mbuf_len(mbuf: 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(cred: 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, vap: &vattr, ctx);
857 }
858 vnode_put(vp);
859 vp = NULL;
860 }
861 if (error) {
862 mbuf_freem(mbuf: mpath);
863 mpath = NULL;
864 }
865
866nfsmerr:
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(a_uio: auio) > 0)) {
881 len -= uio_resid(a_uio: 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(mbuf: nmrep.nmc_mcur, next: mpath);
889 if (!error) {
890 mpath = NULL;
891 }
892nfsmout:
893 if (vp) {
894 vnode_put(vp);
895 }
896 if (mpath) {
897 mbuf_freem(mbuf: 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 */
912int
913nfsrv_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(cred: 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, file_cred: 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(a_iovcount: mreadcnt, a_offset: off, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ);
1016 if (!auio) {
1017 error = ENOMEM;
1018 goto errorexit;
1019 }
1020 for (m = mread; m; m = mbuf_next(mbuf: m)) {
1021 uio_addiov(a_uio: auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), a_length: mbuf_len(mbuf: m));
1022 }
1023 error = VNOP_READ(vp, uio: auio, IO_NODELOCKED, ctx);
1024 } else {
1025 auio = uio_createwithbuffer(a_iovcount: 0, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ, a_buf_p: &uio_buf[0], a_buffer_size: sizeof(uio_buf));
1026 if (!auio) {
1027 error = ENOMEM;
1028 goto errorexit;
1029 }
1030 }
1031
1032errorexit:
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(a_uio: auio);
1047 tlen = nfsm_rndup(len);
1048 if (count != tlen || tlen != len) {
1049 nfsm_adj(mread, count - tlen, tlen - len);
1050 }
1051
1052nfsmerr:
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(mbuf: nmrep.nmc_mcur, next: 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);
1085nfsmout:
1086 if (vp) {
1087 vnode_put(vp);
1088 }
1089 if (mread) {
1090 mbuf_freem(mbuf: mread);
1091 }
1092 if (count > 0 && auio != NULL) {
1093 uio_free(a_uio: 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 */
1115int nfsrv_fmod_pending; /* count of vnodes being written to */
1116int nfsrv_fmod_pendtime = 1000; /* msec to wait */
1117int 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 */
1125void
1126nfsrv_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(lck: &nfsrv_fmod_mutex);
1136again:
1137 clock_get_uptime(result: &timenow);
1138 clock_interval_to_deadline(interval: nfsrv_fmod_pendtime, scale_factor: 1000 * 1000,
1139 result: &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(lck: &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(vp: 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(lck: &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(lck: &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 */
1232void
1233nfsrv_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(lck: &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(interval: nfsrv_fmod_pendtime, scale_factor: 1000 * 1000, result: &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(lck: &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(cred: 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 }
1290done:
1291 lck_mtx_unlock(lck: &nfsrv_fmod_mutex);
1292 return;
1293}
1294#endif /* CONFIG_FSE */
1295
1296/*
1297 * nfs write service
1298 */
1299int
1300nfsrv_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(cred: 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, vap: &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, file_cred: 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(mbuf: m)) {
1411 if (mbuf_len(mbuf: m) > 0) {
1412 mcount++;
1413 }
1414 }
1415 auio = uio_create(a_iovcount: mcount, a_offset: off, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_WRITE);
1416 if (!auio) {
1417 error = ENOMEM;
1418 }
1419 nfsmerr_if(error);
1420 for (m = nmreq->nmc_mcur; m; m = mbuf_next(mbuf: m)) {
1421 if ((mlen = (int)mbuf_len(mbuf: m)) > 0) {
1422 uio_addiov(a_uio: auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), a_length: 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, uio: auio, ioflag: 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, vap: &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
1461nfsmerr:
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 }
1487nfsmout:
1488 nfsm_chain_build_done(error, &nmrep);
1489 if (vp) {
1490 vnode_put(vp);
1491 }
1492 if (auio) {
1493 uio_free(a_uio: 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
1524int
1525nfsrv_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(tv: &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;
1594nfsmerr:
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(lck: &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(lck: &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 */
1660loop1:
1661 microuptime(tv: &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(cred: 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, vap: &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(mbuf: m)) {
1719 if (mbuf_len(mbuf: m) > 0) {
1720 i++;
1721 }
1722 }
1723
1724 auio = uio_create(a_iovcount: i, a_offset: nd->nd_off, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_WRITE);
1725 if (!auio) {
1726 error = ENOMEM;
1727 }
1728 if (!error) {
1729 for (m = nmreq->nmc_mhead; m; m = mbuf_next(mbuf: m)) {
1730 if ((tlen = mbuf_len(mbuf: m)) > 0) {
1731 uio_addiov(a_uio: auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), a_length: tlen);
1732 }
1733 }
1734 error = VNOP_WRITE(vp, uio: auio, ioflag: 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(a_uio: auio);
1750 auio = NULL;
1751 }
1752 }
1753 if (vp) {
1754 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
1755 postattrerr = vnode_getattr(vp, vap: &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(lck: &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(lck: &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(lck: &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 */
1858int
1859nfsrv_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(mbuf: nd->nd_nmreq.nmc_mhead, len: (int)overlap);
1875 }
1876 mp = owp->nd_nmreq.nmc_mhead;
1877 while ((mpnext = mbuf_next(mbuf: mp))) {
1878 mp = mpnext;
1879 }
1880 error = mbuf_setnext(mbuf: mp, next: nd->nd_nmreq.nmc_mhead);
1881 if (error) {
1882 return error;
1883 }
1884 owp->nd_eoff = nd->nd_eoff;
1885 } else {
1886 mbuf_freem(mbuf: 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 */
1913void
1914nfsrv_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(tv: &now);
1923 cur_usec = now.tv_sec * 1000000 + now.tv_usec;
1924 next_usec = cur_usec + (NFSRV_WGATHERDELAY * 1000);
1925
1926 lck_mtx_lock(lck: &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(lck: &slp->ns_rwlock);
1932 slp->ns_flag |= SLP_DOWRITES;
1933 lck_rw_done(lck: &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(lck: &nfsd_mutex);
1946 return;
1947 }
1948 lck_mtx_unlock(lck: &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 */
1965void
1966nfsrv_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 */
1986int
1987nfsrv_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(cred: 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(vp: dirp, vap: &dpreattr, ctx);
2043 } else {
2044 vnode_put(vp: 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: dvp,
2120 NULL,
2121 product: &xacl,
2122 isdir: 0 /* !isdir */,
2123 ctx: 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, noauth: 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(fsp: xacl);
2166 }
2167
2168 if (!error) {
2169 if (exclusive_flag) {
2170 exclusive_flag = 0;
2171 VATTR_INIT(vap);
2172 bcopy(src: cverf, dst: (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(cred: 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(fsp: 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(ndp: &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(vp: 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(vp: 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, vap: &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(s1: cverf, s2: &postattr.va_access_time, NFSX_V3CREATEVERF)) {
2292 error = EEXIST;
2293 }
2294 nfsm_srv_vattr_init(&dpostattr, NFS_VER3);
2295 dpostattrerr = vnode_getattr(vp: dirp, vap: &dpostattr, ctx);
2296 vnode_put(vp: dirp);
2297 dirp = NULL;
2298 }
2299
2300nfsmerr:
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 }
2326nfsmout:
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(vp: dvp);
2339 }
2340 if (dirp) {
2341 vnode_put(vp: 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 */
2353int
2354nfsrv_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(cred: 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(vp: dirp, vap: &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: dvp,
2447 NULL,
2448 product: &xacl,
2449 isdir: 0 /* !isdir */,
2450 ctx: 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, noauth: 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(cred: nd->nd_cr, acflag: (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(ndp: &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 }
2524out1:
2525 if (xacl != NULL) {
2526 kauth_acl_free(fsp: xacl);
2527 }
2528out:
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(vp: 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, vap: &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(vp: dirp, vap: &dpostattr, ctx);
2553 vnode_put(vp: dirp);
2554 dirp = NULL;
2555
2556nfsmerr:
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);
2574nfsmout:
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(vp: dvp);
2587 }
2588 if (dvp) {
2589 vnode_put(vp: dvp);
2590 }
2591 if (vp) {
2592 vnode_put(vp);
2593 }
2594 if (dirp) {
2595 vnode_put(vp: 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 */
2607int
2608nfsrv_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(cred: 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(vp: dirp, vap: &dpreattr, ctx);
2653 } else {
2654 vnode_put(vp: 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, cnp: &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, vp: dvp)) {
2688 plen = MAXPATHLEN;
2689 if ((path = get_pathbuff()) && !vn_getpath(vp, pathbuf: path, len: &plen)) {
2690 get_fse_info(vp, fse: &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(vp: dvp);
2720 }
2721
2722nfsmerr:
2723 if (dirp) {
2724 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
2725 dpostattrerr = vnode_getattr(vp: dirp, vap: &dpostattr, ctx);
2726 vnode_put(vp: 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 }
2739nfsmout:
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 */
2751int
2752nfsrv_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 mount_t locked_mp;
2775 struct nfsm_chain *nmreq, nmrep;
2776 char *from_name, *to_name;
2777#if CONFIG_FSE
2778 int from_len = 0, to_len = 0;
2779 fse_info from_finfo, to_finfo;
2780#endif
2781 u_char didstats = 0;
2782 const char *oname;
2783
2784 error = 0;
2785 fdpreattrerr = fdpostattrerr = ENOENT;
2786 tdpreattrerr = tdpostattrerr = ENOENT;
2787 saved_uid = kauth_cred_getuid(cred: nd->nd_cr);
2788 fromlen = tolen = 0;
2789 frompath = topath = NULL;
2790 fdirp = tdirp = NULL;
2791 nmreq = &nd->nd_nmreq;
2792 nfsm_chain_null(&nmrep);
2793
2794 /*
2795 * these need to be set before calling any code
2796 * that they may take us out through the error path.
2797 */
2798 holding_mntlock = 0;
2799 fvp = tvp = NULL;
2800 fdvp = tdvp = NULL;
2801 locked_mp = NULL;
2802
2803 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, fnfh.nfh_fhp, fnfh.nfh_len);
2804 nfsm_chain_get_32(error, nmreq, fromlen);
2805 nfsm_name_len_check(error, nd, fromlen);
2806 nfsmerr_if(error);
2807 error = nfsm_chain_get_path_namei(nmreq, fromlen, &fromni);
2808 nfsmerr_if(error);
2809 frompath = fromni.ni_cnd.cn_pnbuf;
2810
2811 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, tnfh.nfh_fhp, tnfh.nfh_len);
2812 nfsm_chain_get_32(error, nmreq, tolen);
2813 nfsm_name_len_check(error, nd, tolen);
2814 nfsmerr_if(error);
2815 error = nfsm_chain_get_path_namei(nmreq, tolen, &toni);
2816 nfsmerr_if(error);
2817 topath = toni.ni_cnd.cn_pnbuf;
2818
2819 /*
2820 * Remember our original uid so that we can reset cr_uid before
2821 * the second nfsrv_namei() call, in case it is remapped.
2822 */
2823 saved_cred = nd->nd_cr;
2824 kauth_cred_ref(cred: saved_cred);
2825retry:
2826 NDINIT(&fromni, DELETE, OP_UNLINK, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(frompath), ctx);
2827 fromni.ni_cnd.cn_pnbuf = frompath;
2828 frompath = NULL;
2829 fromni.ni_cnd.cn_pnlen = MAXPATHLEN;
2830 fromni.ni_cnd.cn_flags |= HASBUF;
2831
2832 error = nfsrv_namei(nd, ctx, &fromni, &fnfh, &fdirp, &fnx, &fnxo);
2833 if (error) {
2834 goto out;
2835 }
2836 fdvp = fromni.ni_dvp;
2837 fvp = fromni.ni_vp;
2838
2839 if (fdirp) {
2840 if (nd->nd_vers == NFS_VER3) {
2841 nfsm_srv_pre_vattr_init(&fdpreattr);
2842 fdpreattrerr = vnode_getattr(vp: fdirp, vap: &fdpreattr, ctx);
2843 } else {
2844 vnode_put(vp: fdirp);
2845 fdirp = NULL;
2846 }
2847 }
2848 fvtype = vnode_vtype(vp: fvp);
2849
2850 /* reset credential if it was remapped */
2851 if (nd->nd_cr != saved_cred) {
2852 kauth_cred_ref(cred: saved_cred);
2853 kauth_cred_unref(&nd->nd_cr);
2854 ctx->vc_ucred = nd->nd_cr = saved_cred;
2855 }
2856
2857 NDINIT(&toni, RENAME, OP_RENAME, WANTPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(topath), ctx);
2858 toni.ni_cnd.cn_pnbuf = topath;
2859 topath = NULL;
2860 toni.ni_cnd.cn_pnlen = MAXPATHLEN;
2861 toni.ni_cnd.cn_flags |= HASBUF;
2862
2863 if (fvtype == VDIR) {
2864 toni.ni_cnd.cn_flags |= WILLBEDIR;
2865 }
2866
2867 tnx = NULL;
2868 error = nfsrv_namei(nd, ctx, &toni, &tnfh, &tdirp, &tnx, &tnxo);
2869 if (error) {
2870 /*
2871 * Translate error code for rename("dir1", "dir2/.").
2872 */
2873 if (error == EISDIR && fvtype == VDIR) {
2874 if (nd->nd_vers == NFS_VER3) {
2875 error = EINVAL;
2876 } else {
2877 error = ENOTEMPTY;
2878 }
2879 }
2880 goto out;
2881 }
2882 tdvp = toni.ni_dvp;
2883 tvp = toni.ni_vp;
2884
2885 if (!didstats) {
2886 /* update export stats once only */
2887 if (tnx != NULL) {
2888 /* update export stats */
2889 NFSStatAdd64(&tnx->nx_stats.ops, 1);
2890
2891 /* update active user stats */
2892 nfsrv_update_user_stat(tnx, nd, saved_uid, 1, 0, 0);
2893 didstats = 1;
2894 }
2895 }
2896
2897 if (tdirp) {
2898 if (nd->nd_vers == NFS_VER3) {
2899 nfsm_srv_pre_vattr_init(&tdpreattr);
2900 tdpreattrerr = vnode_getattr(vp: tdirp, vap: &tdpreattr, ctx);
2901 } else {
2902 vnode_put(vp: tdirp);
2903 tdirp = NULL;
2904 }
2905 }
2906
2907 if (tvp != NULL) {
2908 tvtype = vnode_vtype(vp: tvp);
2909
2910 if (fvtype == VDIR && tvtype != VDIR) {
2911 if (nd->nd_vers == NFS_VER3) {
2912 error = EEXIST;
2913 } else {
2914 error = EISDIR;
2915 }
2916 goto out;
2917 } else if (fvtype != VDIR && tvtype == VDIR) {
2918 if (nd->nd_vers == NFS_VER3) {
2919 error = EEXIST;
2920 } else {
2921 error = ENOTDIR;
2922 }
2923 goto out;
2924 }
2925 if (tvtype == VDIR && vnode_mountedhere(vp: tvp)) {
2926 if (nd->nd_vers == NFS_VER3) {
2927 error = EXDEV;
2928 } else {
2929 error = ENOTEMPTY;
2930 }
2931 goto out;
2932 }
2933 }
2934 if (fvp == tdvp) {
2935 if (nd->nd_vers == NFS_VER3) {
2936 error = EINVAL;
2937 } else {
2938 error = ENOTEMPTY;
2939 }
2940 goto out;
2941 }
2942
2943 /*
2944 * Authorization.
2945 *
2946 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2947 * the node is moving between directories and we need rights to remove from the
2948 * old and add to the new.
2949 *
2950 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2951 *
2952 * Note that we do not inherit when renaming. XXX this needs to be revisited to
2953 * implement the deferred-inherit bit.
2954 */
2955 {
2956 int moving = 0;
2957
2958 error = 0;
2959 if ((tvp != NULL) && vnode_isdir(vp: tvp)) {
2960 if (tvp != fdvp) {
2961 moving = 1;
2962 }
2963 } else if (tdvp != fdvp) {
2964 moving = 1;
2965 }
2966 if (moving) {
2967 /* moving out of fdvp, must have delete rights */
2968 if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, ctx, fnxo, 0)) != 0) {
2969 goto auth_exit;
2970 }
2971 /* moving into tdvp or tvp, must have rights to add */
2972 if ((error = nfsrv_authorize(((tvp != NULL) && vnode_isdir(vp: tvp)) ? tvp : tdvp,
2973 NULL,
2974 vnode_isdir(vp: fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2975 ctx, tnxo, 0)) != 0) {
2976 goto auth_exit;
2977 }
2978 } else {
2979 /* node staying in same directory, must be allowed to add new name */
2980 if ((error = nfsrv_authorize(fdvp, NULL,
2981 vnode_isdir(vp: fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
2982 ctx, fnxo, 0)) != 0) {
2983 goto auth_exit;
2984 }
2985 }
2986 /* overwriting tvp */
2987 if ((tvp != NULL) && !vnode_isdir(vp: tvp) &&
2988 ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, ctx, tnxo, 0)) != 0)) {
2989 goto auth_exit;
2990 }
2991
2992 if (!error &&
2993 ((error = vn_authorize_rename(fdvp, fvp, fcnp: &fromni.ni_cnd, tdvp, tvp, tcnp: &toni.ni_cnd, ctx, NULL)) != 0)) {
2994 if (error) {
2995 error = EACCES;
2996 }
2997 goto auth_exit;
2998 }
2999 /* XXX more checks? */
3000
3001auth_exit:
3002 /* authorization denied */
3003 if (error != 0) {
3004 goto out;
3005 }
3006 }
3007
3008 if ((vnode_mount(vp: fvp) != vnode_mount(vp: tdvp)) ||
3009 (tvp && (vnode_mount(vp: fvp) != vnode_mount(vp: tvp)))) {
3010 if (nd->nd_vers == NFS_VER3) {
3011 error = EXDEV;
3012 } else {
3013 error = ENOTEMPTY;
3014 }
3015 goto out;
3016 }
3017 /*
3018 * The following edge case is caught here:
3019 * (to cannot be a descendent of from)
3020 *
3021 * o fdvp
3022 * /
3023 * /
3024 * o fvp
3025 * \
3026 * \
3027 * o tdvp
3028 * /
3029 * /
3030 * o tvp
3031 */
3032 if (tdvp->v_parent == fvp) {
3033 if (nd->nd_vers == NFS_VER3) {
3034 error = EXDEV;
3035 } else {
3036 error = ENOTEMPTY;
3037 }
3038 goto out;
3039 }
3040 if (fvtype == VDIR && vnode_mountedhere(vp: fvp)) {
3041 if (nd->nd_vers == NFS_VER3) {
3042 error = EXDEV;
3043 } else {
3044 error = ENOTEMPTY;
3045 }
3046 goto out;
3047 }
3048 /*
3049 * If source is the same as the destination (that is the
3050 * same vnode) then there is nothing to do...
3051 * EXCEPT if the underlying file system supports case
3052 * insensitivity and is case preserving. In this case
3053 * the file system needs to handle the special case of
3054 * getting the same vnode as target (fvp) and source (tvp).
3055 *
3056 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
3057 * and _PC_CASE_PRESERVING can have this exception, and they need to
3058 * handle the special case of getting the same vnode as target and
3059 * source. NOTE: Then the target is unlocked going into vnop_rename,
3060 * so not to cause locking problems. There is a single reference on tvp.
3061 *
3062 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
3063 * that correct behaviour then is just to remove the source (link)
3064 */
3065 if ((fvp == tvp) && (fdvp == tdvp)) {
3066 if (fromni.ni_cnd.cn_namelen == toni.ni_cnd.cn_namelen &&
3067 !bcmp(s1: fromni.ni_cnd.cn_nameptr, s2: toni.ni_cnd.cn_nameptr,
3068 n: fromni.ni_cnd.cn_namelen)) {
3069 goto out;
3070 }
3071 }
3072
3073 if (holding_mntlock && vnode_mount(vp: fvp) != locked_mp) {
3074 /*
3075 * we're holding a reference and lock
3076 * on locked_mp, but it no longer matches
3077 * what we want to do... so drop our hold
3078 */
3079 mount_unlock_renames(locked_mp);
3080 mount_drop(locked_mp, 0);
3081 holding_mntlock = 0;
3082 }
3083 if (tdvp != fdvp && fvtype == VDIR) {
3084 /*
3085 * serialize renames that re-shape
3086 * the tree... if holding_mntlock is
3087 * set, then we're ready to go...
3088 * otherwise we
3089 * first need to drop the iocounts
3090 * we picked up, second take the
3091 * lock to serialize the access,
3092 * then finally start the lookup
3093 * process over with the lock held
3094 */
3095 if (!holding_mntlock) {
3096 /*
3097 * need to grab a reference on
3098 * the mount point before we
3099 * drop all the iocounts... once
3100 * the iocounts are gone, the mount
3101 * could follow
3102 */
3103 locked_mp = vnode_mount(vp: fvp);
3104 mount_ref(locked_mp, 0);
3105
3106 /* make a copy of to path to pass to nfsrv_namei() again */
3107 topath = zalloc(view: ZV_NAMEI);
3108 bcopy(src: toni.ni_cnd.cn_pnbuf, dst: topath, n: tolen + 1);
3109
3110 /*
3111 * nameidone has to happen before we vnode_put(tdvp)
3112 * since it may need to release the fs_nodelock on the tdvp
3113 */
3114 nameidone(&toni);
3115
3116 if (tvp) {
3117 vnode_put(vp: tvp);
3118 }
3119 vnode_put(vp: tdvp);
3120
3121 /* make a copy of from path to pass to nfsrv_namei() again */
3122 frompath = zalloc(view: ZV_NAMEI);
3123 bcopy(src: fromni.ni_cnd.cn_pnbuf, dst: frompath, n: fromlen + 1);
3124
3125 /*
3126 * nameidone has to happen before we vnode_put(fdvp)
3127 * since it may need to release the fs_nodelock on the fdvp
3128 */
3129 nameidone(&fromni);
3130
3131 vnode_put(vp: fvp);
3132 vnode_put(vp: fdvp);
3133
3134 if (fdirp) {
3135 vnode_put(vp: fdirp);
3136 fdirp = NULL;
3137 }
3138 if (tdirp) {
3139 vnode_put(vp: tdirp);
3140 tdirp = NULL;
3141 }
3142 mount_lock_renames(locked_mp);
3143 holding_mntlock = 1;
3144
3145 fvp = tvp = NULL;
3146 fdvp = tdvp = NULL;
3147
3148 fdpreattrerr = tdpreattrerr = ENOENT;
3149
3150 if (!topath || !frompath) {
3151 /* we couldn't allocate a path, so bail */
3152 error = ENOMEM;
3153 goto out;
3154 }
3155
3156 /* reset credential if it was remapped */
3157 if (nd->nd_cr != saved_cred) {
3158 kauth_cred_ref(cred: saved_cred);
3159 kauth_cred_unref(&nd->nd_cr);
3160 ctx->vc_ucred = nd->nd_cr = saved_cred;
3161 }
3162
3163 goto retry;
3164 }
3165 } else {
3166 /*
3167 * when we dropped the iocounts to take
3168 * the lock, we allowed the identity of
3169 * the various vnodes to change... if they did,
3170 * we may no longer be dealing with a rename
3171 * that reshapes the tree... once we're holding
3172 * the iocounts, the vnodes can't change type
3173 * so we're free to drop the lock at this point
3174 * and continue on
3175 */
3176 if (holding_mntlock) {
3177 mount_unlock_renames(locked_mp);
3178 mount_drop(locked_mp, 0);
3179 holding_mntlock = 0;
3180 }
3181 }
3182
3183 // save these off so we can later verify that fvp is the same
3184 vnode_t oparent;
3185 oname = fvp->v_name;
3186 oparent = fvp->v_parent;
3187
3188 /*
3189 * If generating an fsevent, then
3190 * stash any pre-rename info we may need.
3191 */
3192#if CONFIG_FSE
3193 if (nfsrv_fsevents_enabled && need_fsevent(FSE_RENAME, vp: fvp)) {
3194 int from_truncated = 0, to_truncated = 0;
3195
3196 get_fse_info(vp: fvp, fse: &from_finfo, ctx);
3197 if (tvp) {
3198 get_fse_info(vp: tvp, fse: &to_finfo, ctx);
3199 }
3200
3201 from_name = get_pathbuff();
3202 if (from_name) {
3203 from_len = safe_getpath(dvp: fdvp, leafname: fromni.ni_cnd.cn_nameptr, path: from_name, MAXPATHLEN, truncated_path: &from_truncated);
3204 }
3205
3206 to_name = from_name ? get_pathbuff() : NULL;
3207 if (to_name) {
3208 to_len = safe_getpath(dvp: tdvp, leafname: toni.ni_cnd.cn_nameptr, path: to_name, MAXPATHLEN, truncated_path: &to_truncated);
3209 }
3210
3211 if (from_truncated || to_truncated) {
3212 from_finfo.mode |= FSE_TRUNCATED_PATH;
3213 }
3214 } else {
3215 from_name = NULL;
3216 to_name = NULL;
3217 }
3218#else /* CONFIG_FSE */
3219 from_name = NULL;
3220 to_name = NULL;
3221#endif /* CONFIG_FSE */
3222
3223 error = VNOP_RENAME(fromni.ni_dvp, fromni.ni_vp, &fromni.ni_cnd,
3224 toni.ni_dvp, toni.ni_vp, &toni.ni_cnd, ctx);
3225 /*
3226 * fix up name & parent pointers. note that we first
3227 * check that fvp has the same name/parent pointers it
3228 * had before the rename call... this is a 'weak' check
3229 * at best...
3230 */
3231 if (oname == fvp->v_name && oparent == fvp->v_parent) {
3232 int update_flags;
3233 update_flags = VNODE_UPDATE_NAME;
3234 if (fdvp != tdvp) {
3235 update_flags |= VNODE_UPDATE_PARENT;
3236 }
3237 vnode_update_identity(vp: fvp, dvp: tdvp, name: toni.ni_cnd.cn_nameptr,
3238 name_len: toni.ni_cnd.cn_namelen, name_hashval: toni.ni_cnd.cn_hash, flags: update_flags);
3239 }
3240
3241 /*
3242 * If the rename is OK and we've got the paths
3243 * then add an fsevent.
3244 */
3245#if CONFIG_FSE
3246 if (nfsrv_fsevents_enabled && !error && from_name && to_name) {
3247 if (tvp) {
3248 add_fsevent(FSE_RENAME, ctx,
3249 FSE_ARG_STRING, from_len, from_name,
3250 FSE_ARG_FINFO, &from_finfo,
3251 FSE_ARG_STRING, to_len, to_name,
3252 FSE_ARG_FINFO, &to_finfo,
3253 FSE_ARG_DONE);
3254 } else {
3255 add_fsevent(FSE_RENAME, ctx,
3256 FSE_ARG_STRING, from_len, from_name,
3257 FSE_ARG_FINFO, &from_finfo,
3258 FSE_ARG_STRING, to_len, to_name,
3259 FSE_ARG_DONE);
3260 }
3261 }
3262 if (from_name) {
3263 release_pathbuff(path: from_name);
3264 }
3265 if (to_name) {
3266 release_pathbuff(path: to_name);
3267 }
3268#endif /* CONFIG_FSE */
3269 from_name = to_name = NULL;
3270
3271out:
3272 if (holding_mntlock) {
3273 mount_unlock_renames(locked_mp);
3274 mount_drop(locked_mp, 0);
3275 holding_mntlock = 0;
3276 }
3277 if (tdvp) {
3278 /*
3279 * nameidone has to happen before we vnode_put(tdvp)
3280 * since it may need to release the fs_nodelock on the tdvp
3281 */
3282 nameidone(&toni);
3283 if (tvp) {
3284 vnode_put(vp: tvp);
3285 }
3286 vnode_put(vp: tdvp);
3287
3288 tdvp = NULL;
3289 }
3290 if (fdvp) {
3291 /*
3292 * nameidone has to happen before we vnode_put(fdvp)
3293 * since it may need to release the fs_nodelock on the fdvp
3294 */
3295 nameidone(&fromni);
3296
3297 if (fvp) {
3298 vnode_put(vp: fvp);
3299 }
3300 vnode_put(vp: fdvp);
3301
3302 fdvp = NULL;
3303 }
3304 if (fdirp) {
3305 nfsm_srv_vattr_init(&fdpostattr, nd->nd_vers);
3306 fdpostattrerr = vnode_getattr(vp: fdirp, vap: &fdpostattr, ctx);
3307 vnode_put(vp: fdirp);
3308 fdirp = NULL;
3309 }
3310 if (tdirp) {
3311 nfsm_srv_vattr_init(&tdpostattr, nd->nd_vers);
3312 tdpostattrerr = vnode_getattr(vp: tdirp, vap: &tdpostattr, ctx);
3313 vnode_put(vp: tdirp);
3314 tdirp = NULL;
3315 }
3316
3317nfsmerr:
3318 /* assemble reply */
3319 nd->nd_repstat = error;
3320 error = nfsrv_rephead(nd, slp, &nmrep, 2 * NFSX_WCCDATA(nd->nd_vers));
3321 nfsmout_if(error);
3322 *mrepp = nmrep.nmc_mhead;
3323 nfsmout_on_status(nd, error);
3324 if (nd->nd_vers == NFS_VER3) {
3325 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3326 fdpreattrerr, &fdpreattr, fdpostattrerr, &fdpostattr);
3327 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3328 tdpreattrerr, &tdpreattr, tdpostattrerr, &tdpostattr);
3329 }
3330nfsmout:
3331 nfsm_chain_build_done(error, &nmrep);
3332 if (holding_mntlock) {
3333 mount_unlock_renames(locked_mp);
3334 mount_drop(locked_mp, 0);
3335 }
3336 if (tdvp) {
3337 /*
3338 * nameidone has to happen before we vnode_put(tdvp)
3339 * since it may need to release the fs_nodelock on the tdvp
3340 */
3341 nameidone(&toni);
3342
3343 if (tvp) {
3344 vnode_put(vp: tvp);
3345 }
3346 vnode_put(vp: tdvp);
3347 }
3348 if (fdvp) {
3349 /*
3350 * nameidone has to happen before we vnode_put(fdvp)
3351 * since it may need to release the fs_nodelock on the fdvp
3352 */
3353 nameidone(&fromni);
3354
3355 if (fvp) {
3356 vnode_put(vp: fvp);
3357 }
3358 vnode_put(vp: fdvp);
3359 }
3360 if (fdirp) {
3361 vnode_put(vp: fdirp);
3362 }
3363 if (tdirp) {
3364 vnode_put(vp: tdirp);
3365 }
3366 if (frompath) {
3367 NFS_ZFREE(ZV_NAMEI, frompath);
3368 }
3369 if (topath) {
3370 NFS_ZFREE(ZV_NAMEI, topath);
3371 }
3372 if (saved_cred) {
3373 kauth_cred_unref(&saved_cred);
3374 }
3375 if (error) {
3376 nfsm_chain_cleanup(&nmrep);
3377 *mrepp = NULL;
3378 }
3379 return error;
3380}
3381
3382/*
3383 * nfs link service
3384 */
3385int
3386nfsrv_link(
3387 struct nfsrv_descript *nd,
3388 struct nfsrv_sock *slp,
3389 vfs_context_t ctx,
3390 mbuf_t *mrepp)
3391{
3392 struct nameidata ni;
3393 int error, dpreattrerr, dpostattrerr, attrerr;
3394 uint32_t len = 0;
3395 vnode_t vp, xp, dvp, dirp;
3396 struct vnode_attr dpreattr, dpostattr, attr;
3397 struct nfs_filehandle nfh, dnfh;
3398 struct nfs_export *nx;
3399 struct nfs_export_options *nxo;
3400 struct nfsm_chain *nmreq, nmrep;
3401
3402 error = 0;
3403 dpreattrerr = dpostattrerr = attrerr = ENOENT;
3404 vp = xp = dvp = dirp = NULL;
3405 nmreq = &nd->nd_nmreq;
3406 nfsm_chain_null(&nmrep);
3407
3408 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3409 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
3410 nfsm_chain_get_32(error, nmreq, len);
3411 nfsm_name_len_check(error, nd, len);
3412 nfsmerr_if(error);
3413 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
3414 nfsmerr_if(error);
3415
3416 /* update export stats */
3417 NFSStatAdd64(&nx->nx_stats.ops, 1);
3418
3419 /* update active user stats */
3420 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(cred: nd->nd_cr), 1, 0, 0);
3421
3422 error = nfsrv_credcheck(nd, ctx, nx, nxo);
3423 nfsmerr_if(error);
3424
3425 /* we're not allowed to link to directories... */
3426 if (vnode_vtype(vp) == VDIR) {
3427 error = NFSERR_ISDIR;
3428 goto out;
3429 }
3430
3431 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3432 if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx, nxo, 0)) != 0) {
3433 goto out;
3434 }
3435
3436 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, CAST_USER_ADDR_T(vnode_getname(vp)), ctx);
3437 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3438 if (!error) {
3439 error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo);
3440 }
3441 if (dirp) {
3442 if (nd->nd_vers == NFS_VER3) {
3443 nfsm_srv_pre_vattr_init(&dpreattr);
3444 dpreattrerr = vnode_getattr(vp: dirp, vap: &dpreattr, ctx);
3445 } else {
3446 vnode_put(vp: dirp);
3447 dirp = NULL;
3448 }
3449 }
3450 if (error) {
3451 goto out;
3452 }
3453 dvp = ni.ni_dvp;
3454 xp = ni.ni_vp;
3455
3456 if (xp != NULL) {
3457 error = EEXIST;
3458 } else if (vnode_mount(vp) != vnode_mount(vp: dvp)) {
3459 error = EXDEV;
3460 } else {
3461 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3462 }
3463
3464#if CONFIG_MACF
3465 if (!error) {
3466 error = mac_vnode_check_link(ctx, dvp, vp, cnp: &ni.ni_cnd);
3467 if (error) {
3468 error = EACCES;
3469 }
3470 }
3471#endif
3472 if (!error) {
3473 error = VNOP_LINK(vp, dvp, &ni.ni_cnd, ctx);
3474 }
3475
3476#if CONFIG_FSE
3477 if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, vp: dvp)) {
3478 char *target_path = NULL;
3479 int plen, truncated = 0;
3480 fse_info finfo;
3481
3482 /* build the path to the new link file */
3483 target_path = get_pathbuff();
3484 if (target_path) {
3485 plen = safe_getpath(dvp, leafname: ni.ni_cnd.cn_nameptr, path: target_path, MAXPATHLEN, truncated_path: &truncated);
3486
3487 if (get_fse_info(vp, fse: &finfo, ctx) == 0) {
3488 if (truncated) {
3489 finfo.mode |= FSE_TRUNCATED_PATH;
3490 }
3491 add_fsevent(FSE_CREATE_FILE, ctx,
3492 FSE_ARG_STRING, plen, target_path,
3493 FSE_ARG_FINFO, &finfo,
3494 FSE_ARG_DONE);
3495 }
3496
3497 release_pathbuff(path: target_path);
3498 }
3499 }
3500#endif
3501
3502 /*
3503 * nameidone has to happen before we vnode_put(dvp)
3504 * since it may need to release the fs_nodelock on the dvp
3505 */
3506 nameidone(&ni);
3507
3508 if (xp) {
3509 vnode_put(vp: xp);
3510 }
3511 vnode_put(vp: dvp);
3512out:
3513 if (nd->nd_vers == NFS_VER3) {
3514 nfsm_srv_vattr_init(&attr, NFS_VER3);
3515 attrerr = vnode_getattr(vp, vap: &attr, ctx);
3516 }
3517 if (dirp) {
3518 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3519 dpostattrerr = vnode_getattr(vp: dirp, vap: &dpostattr, ctx);
3520 vnode_put(vp: dirp);
3521 dirp = NULL;
3522 }
3523 vnode_put(vp);
3524 vp = NULL;
3525
3526nfsmerr:
3527 /* assemble reply */
3528 nd->nd_repstat = error;
3529 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3530 nfsmout_if(error);
3531 *mrepp = nmrep.nmc_mhead;
3532 nfsmout_on_status(nd, error);
3533 if (nd->nd_vers == NFS_VER3) {
3534 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
3535 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3536 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3537 }
3538nfsmout:
3539 nfsm_chain_build_done(error, &nmrep);
3540 if (vp) {
3541 vnode_put(vp);
3542 }
3543 if (error) {
3544 nfsm_chain_cleanup(&nmrep);
3545 *mrepp = NULL;
3546 }
3547 return error;
3548}
3549
3550/*
3551 * nfs symbolic link service
3552 */
3553int
3554nfsrv_symlink(
3555 struct nfsrv_descript *nd,
3556 struct nfsrv_sock *slp,
3557 vfs_context_t ctx,
3558 mbuf_t *mrepp)
3559{
3560 struct vnode_attr dpreattr, dpostattr, postattr;
3561 struct vnode_attr va, *vap = &va;
3562 struct nameidata ni;
3563 int error, dpreattrerr, dpostattrerr, postattrerr;
3564 uint32_t len = 0, linkdatalen = 0, cnflags;
3565 uid_t saved_uid;
3566 char *linkdata;
3567 vnode_t vp, dvp, dirp;
3568 struct nfs_filehandle nfh = {};
3569 struct nfs_export *nx = NULL;
3570 struct nfs_export_options *nxo = NULL;
3571 uio_t auio = NULL;
3572 UIO_STACKBUF(uio_buf, 1);
3573 struct nfsm_chain *nmreq, nmrep;
3574 __unused const int nfs_vers = nd->nd_vers;
3575 assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3576
3577 error = 0;
3578 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3579 nmreq = &nd->nd_nmreq;
3580 nfsm_chain_null(&nmrep);
3581 linkdata = NULL;
3582 dirp = NULL;
3583
3584 saved_uid = kauth_cred_getuid(cred: nd->nd_cr);
3585
3586 ni.ni_cnd.cn_nameiop = 0;
3587 vp = dvp = NULL;
3588
3589 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3590 nfsm_chain_get_32(error, nmreq, len);
3591 nfsm_name_len_check(error, nd, len);
3592 nfsmerr_if(error);
3593
3594 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT, UIO_SYSSPACE, 0, ctx);
3595 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3596 if (!error) {
3597 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3598 if (nx != NULL) {
3599 /* update export stats */
3600 NFSStatAdd64(&nx->nx_stats.ops, 1);
3601
3602 /* update active user stats */
3603 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3604 }
3605 }
3606 if (dirp) {
3607 if (nd->nd_vers == NFS_VER3) {
3608 nfsm_srv_pre_vattr_init(&dpreattr);
3609 dpreattrerr = vnode_getattr(vp: dirp, vap: &dpreattr, ctx);
3610 } else {
3611 vnode_put(vp: dirp);
3612 dirp = NULL;
3613 }
3614 }
3615 if (error) {
3616 ni.ni_cnd.cn_nameiop = 0;
3617 goto out1;
3618 }
3619 dvp = ni.ni_dvp;
3620 vp = ni.ni_vp;
3621
3622 VATTR_INIT(vap);
3623 if (nd->nd_vers == NFS_VER3) {
3624 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3625 }
3626 nfsm_chain_get_32(error, nmreq, linkdatalen);
3627 if (!error && (((nd->nd_vers == NFS_VER2) && (linkdatalen > NFS_MAXPATHLEN)) ||
3628 ((nd->nd_vers == NFS_VER3) && (linkdatalen > MAXPATHLEN)))) {
3629 error = NFSERR_NAMETOL;
3630 }
3631 nfsmerr_if(error);
3632 linkdata = kalloc_data(linkdatalen + 1, Z_WAITOK);
3633 if (linkdata) {
3634 auio = uio_createwithbuffer(a_iovcount: 1, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ,
3635 a_buf_p: &uio_buf[0], a_buffer_size: sizeof(uio_buf));
3636 }
3637 if (!linkdata || !auio) {
3638 error = ENOMEM;
3639 goto out;
3640 }
3641 uio_addiov(a_uio: auio, CAST_USER_ADDR_T(linkdata), a_length: linkdatalen);
3642 error = nfsm_chain_get_uio(nmreq, linkdatalen, auio);
3643 if (!error && (nd->nd_vers == NFS_VER2)) {
3644 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3645 }
3646 nfsmerr_if(error);
3647 *(linkdata + linkdatalen) = '\0';
3648 if (vp) {
3649 error = EEXIST;
3650 goto out;
3651 }
3652
3653 VATTR_SET(vap, va_type, VLNK);
3654 VATTR_CLEAR_ACTIVE(vap, va_data_size);
3655 VATTR_CLEAR_ACTIVE(vap, va_access_time);
3656 /*
3657 * Server policy is to alway use the mapped rpc credential for
3658 * file system object creation. This has the nice side effect of
3659 * enforcing BSD creation semantics
3660 */
3661 VATTR_CLEAR_ACTIVE(vap, va_uid);
3662 VATTR_CLEAR_ACTIVE(vap, va_gid);
3663
3664 /* authorize before creating */
3665 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
3666
3667 /* validate given attributes */
3668 if (!error) {
3669 error = vnode_authattr_new(dvp, vap, noauth: 0, ctx);
3670 }
3671 if (!error) {
3672 error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL);
3673 if (error) {
3674 error = EACCES;
3675 }
3676 }
3677
3678 if (!error) {
3679 error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx);
3680 }
3681
3682 if (!error && (nd->nd_vers == NFS_VER3)) {
3683 if (vp == NULL) {
3684 ni.ni_cnd.cn_nameiop = LOOKUP;
3685#if CONFIG_TRIGGERS
3686 ni.ni_op = OP_LOOKUP;
3687#endif
3688 ni.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
3689 ni.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
3690 ni.ni_cnd.cn_context = ctx;
3691 ni.ni_startdir = dvp;
3692 ni.ni_usedvp = dvp;
3693 ni.ni_rootdir = rootvnode;
3694 cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
3695 while ((error = lookup(ndp: &ni)) == ERECYCLE) {
3696 ni.ni_cnd.cn_flags = cnflags;
3697 ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
3698 ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
3699 }
3700 if (!error) {
3701 vp = ni.ni_vp;
3702 }
3703 }
3704 if (!error) {
3705 error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh);
3706 if (!error) {
3707 nfsm_srv_vattr_init(&postattr, NFS_VER3);
3708 postattrerr = vnode_getattr(vp, vap: &postattr, ctx);
3709 }
3710 }
3711 }
3712
3713#if CONFIG_FSE
3714 if (nfsrv_fsevents_enabled && !error && vp) {
3715 add_fsevent(FSE_CREATE_FILE, ctx,
3716 FSE_ARG_VNODE, vp,
3717 FSE_ARG_DONE);
3718 }
3719#endif
3720out:
3721 /*
3722 * nameidone has to happen before we vnode_put(dvp)
3723 * since it may need to release the fs_nodelock on the dvp
3724 */
3725 nameidone(&ni);
3726 ni.ni_cnd.cn_nameiop = 0;
3727 if (vp) {
3728 vnode_put(vp);
3729 }
3730 vnode_put(vp: dvp);
3731out1:
3732 if (linkdata) {
3733 kfree_data(linkdata, linkdatalen + 1);
3734 }
3735 if (dirp) {
3736 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3737 dpostattrerr = vnode_getattr(vp: dirp, vap: &dpostattr, ctx);
3738 vnode_put(vp: dirp);
3739 dirp = NULL;
3740 }
3741
3742nfsmerr:
3743 /* assemble reply */
3744 nd->nd_repstat = error;
3745 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3746 NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3747 assert(nfs_vers == nd->nd_vers);
3748 nfsmout_if(error);
3749 *mrepp = nmrep.nmc_mhead;
3750 nfsmout_on_status(nd, error);
3751 if (nd->nd_vers == NFS_VER3) {
3752 if (!nd->nd_repstat) {
3753 if (!nfh.nfh_fhp) {
3754 error = NFSERR_SERVERFAULT;
3755 goto nfsmerr;
3756 }
3757 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3758 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3759 }
3760 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3761 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3762 }
3763nfsmout:
3764 nfsm_chain_build_done(error, &nmrep);
3765 if (ni.ni_cnd.cn_nameiop) {
3766 /*
3767 * nameidone has to happen before we vnode_put(dvp)
3768 * since it may need to release the fs_nodelock on the dvp
3769 */
3770 nameidone(&ni);
3771
3772 if (vp) {
3773 vnode_put(vp);
3774 }
3775 vnode_put(vp: dvp);
3776 }
3777 if (dirp) {
3778 vnode_put(vp: dirp);
3779 }
3780 if (linkdata) {
3781 kfree_data(linkdata, linkdatalen + 1);
3782 }
3783 if (error) {
3784 nfsm_chain_cleanup(&nmrep);
3785 *mrepp = NULL;
3786 }
3787 return error;
3788}
3789
3790/*
3791 * nfs mkdir service
3792 */
3793
3794int
3795nfsrv_mkdir(
3796 struct nfsrv_descript *nd,
3797 struct nfsrv_sock *slp,
3798 vfs_context_t ctx,
3799 mbuf_t *mrepp)
3800{
3801 struct vnode_attr dpreattr, dpostattr, postattr;
3802 struct vnode_attr va, *vap = &va;
3803 struct nameidata ni;
3804 int error, dpreattrerr, dpostattrerr, postattrerr;
3805 uint32_t len = 0;
3806 vnode_t vp, dvp, dirp;
3807 struct nfs_filehandle nfh = {};
3808 struct nfs_export *nx = NULL;
3809 struct nfs_export_options *nxo = NULL;
3810 uid_t saved_uid;
3811 kauth_acl_t xacl = NULL;
3812 struct nfsm_chain *nmreq, nmrep;
3813 __unused const int nfs_vers = nd->nd_vers;
3814 assert(nd->nd_vers == NFS_VER2 || nd->nd_vers == NFS_VER3);
3815
3816 error = 0;
3817 dpreattrerr = dpostattrerr = postattrerr = ENOENT;
3818 nmreq = &nd->nd_nmreq;
3819 nfsm_chain_null(&nmrep);
3820
3821 saved_uid = kauth_cred_getuid(cred: nd->nd_cr);
3822
3823 ni.ni_cnd.cn_nameiop = 0;
3824 vp = dvp = dirp = NULL;
3825
3826 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
3827 nfsm_chain_get_32(error, nmreq, len);
3828 nfsm_name_len_check(error, nd, len);
3829 nfsmerr_if(error);
3830
3831 NDINIT(&ni, CREATE, OP_LINK, LOCKPARENT | WILLBEDIR, UIO_SYSSPACE, 0, ctx);
3832 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
3833 if (!error) {
3834 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
3835 if (nx != NULL) {
3836 /* update export stats */
3837 NFSStatAdd64(&nx->nx_stats.ops, 1);
3838
3839 /* update active user stats */
3840 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
3841 }
3842 }
3843 if (dirp) {
3844 if (nd->nd_vers == NFS_VER3) {
3845 nfsm_srv_pre_vattr_init(&dpreattr);
3846 dpreattrerr = vnode_getattr(vp: dirp, vap: &dpreattr, ctx);
3847 } else {
3848 vnode_put(vp: dirp);
3849 dirp = NULL;
3850 }
3851 }
3852 if (error) {
3853 ni.ni_cnd.cn_nameiop = 0;
3854 goto nfsmerr;
3855 }
3856 dvp = ni.ni_dvp;
3857 vp = ni.ni_vp;
3858
3859 VATTR_INIT(vap);
3860 error = nfsm_chain_get_sattr(nd, nmreq, vap);
3861 nfsmerr_if(error);
3862 VATTR_SET(vap, va_type, VDIR);
3863
3864 if (vp != NULL) {
3865 /*
3866 * nameidone has to happen before we vnode_put(dvp)
3867 * since it may need to release the fs_nodelock on the dvp
3868 */
3869 nameidone(&ni);
3870 vnode_put(vp: dvp);
3871 vnode_put(vp);
3872 error = EEXIST;
3873 goto out;
3874 }
3875
3876 error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx, nxo, 0);
3877
3878 /* construct ACL and handle inheritance */
3879 if (!error) {
3880 error = kauth_acl_inherit(dvp: dvp,
3881 NULL,
3882 product: &xacl, /* isdir */
3883 1,
3884 ctx: ctx);
3885
3886 if (!error && xacl != NULL) {
3887 VATTR_SET(vap, va_acl, xacl);
3888 }
3889 }
3890
3891 VATTR_CLEAR_ACTIVE(vap, va_data_size);
3892 VATTR_CLEAR_ACTIVE(vap, va_access_time);
3893 /*
3894 * We don't support the S_ISGID bit for directories. Solaris and other
3895 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3896 * any ways.
3897 */
3898 if (VATTR_IS_ACTIVE(vap, va_mode)) {
3899 vap->va_mode &= ~S_ISGID;
3900 }
3901 /*
3902 * Server policy is to alway use the mapped rpc credential for
3903 * file system object creation. This has the nice side effect of
3904 * enforcing BSD creation semantics
3905 */
3906 VATTR_CLEAR_ACTIVE(vap, va_uid);
3907 VATTR_CLEAR_ACTIVE(vap, va_gid);
3908
3909 /* validate new-file security information */
3910 if (!error) {
3911 error = vnode_authattr_new(dvp, vap, noauth: 0, ctx);
3912 }
3913 /*
3914 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3915 * sit well with our clients so we map all errors to EPERM.
3916 */
3917 if (error) {
3918 error = EPERM;
3919 }
3920
3921 if (!error) {
3922 error = vn_authorize_mkdir(dvp, &ni.ni_cnd, vap, ctx, NULL);
3923 if (error) {
3924 error = EACCES;
3925 }
3926 }
3927
3928 if (!error) {
3929 error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx);
3930 }
3931
3932#if CONFIG_FSE
3933 if (nfsrv_fsevents_enabled && !error) {
3934 add_fsevent(FSE_CREATE_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
3935 }
3936#endif
3937
3938 if (!error && !VATTR_ALL_SUPPORTED(vap)) {
3939 /*
3940 * If some of the requested attributes weren't handled by the VNOP,
3941 * use our fallback code.
3942 */
3943 error = vnode_setattr_fallback(vp, vap, ctx);
3944 }
3945
3946 if (xacl != NULL) {
3947 kauth_acl_free(fsp: xacl);
3948 }
3949
3950 if (!error) {
3951 error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh);
3952 if (!error) {
3953 nfsm_srv_vattr_init(&postattr, nd->nd_vers);
3954 postattrerr = vnode_getattr(vp, vap: &postattr, ctx);
3955 if (nd->nd_vers == NFS_VER2) {
3956 error = postattrerr;
3957 }
3958 }
3959 vnode_put(vp);
3960 vp = NULL;
3961 }
3962 /*
3963 * nameidone has to happen before we vnode_put(dvp)
3964 * since it may need to release the fs_nodelock on the dvp
3965 */
3966 nameidone(&ni);
3967 vnode_put(vp: dvp);
3968out:
3969 ni.ni_cnd.cn_nameiop = 0;
3970
3971 if (dirp) {
3972 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
3973 dpostattrerr = vnode_getattr(vp: dirp, vap: &dpostattr, ctx);
3974 vnode_put(vp: dirp);
3975 dirp = NULL;
3976 }
3977
3978nfsmerr:
3979 /* assemble reply */
3980 nd->nd_repstat = error;
3981 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) +
3982 NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers));
3983 assert(nfs_vers == nd->nd_vers);
3984 nfsmout_if(error);
3985 *mrepp = nmrep.nmc_mhead;
3986 nfsmout_on_status(nd, error);
3987 if (nd->nd_vers == NFS_VER3) {
3988 if (!nd->nd_repstat) {
3989 if (!nfh.nfh_fhp) {
3990 error = NFSERR_SERVERFAULT;
3991 goto nfsmerr;
3992 }
3993 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
3994 nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr);
3995 }
3996 nfsm_chain_add_wcc_data(error, nd, &nmrep,
3997 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
3998 } else {
3999 nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len);
4000 if (!error) {
4001 error = nfsm_chain_add_fattr(nd, &nmrep, &postattr);
4002 }
4003 }
4004nfsmout:
4005 nfsm_chain_build_done(error, &nmrep);
4006 if (ni.ni_cnd.cn_nameiop) {
4007 /*
4008 * nameidone has to happen before we vnode_put(dvp)
4009 * since it may need to release the fs_nodelock on the dvp
4010 */
4011 nameidone(&ni);
4012 vnode_put(vp: dvp);
4013 if (vp) {
4014 vnode_put(vp);
4015 }
4016 }
4017 if (dirp) {
4018 vnode_put(vp: dirp);
4019 }
4020 if (error) {
4021 nfsm_chain_cleanup(&nmrep);
4022 *mrepp = NULL;
4023 }
4024 return error;
4025}
4026
4027/*
4028 * nfs rmdir service
4029 */
4030int
4031nfsrv_rmdir(
4032 struct nfsrv_descript *nd,
4033 struct nfsrv_sock *slp,
4034 vfs_context_t ctx,
4035 mbuf_t *mrepp)
4036{
4037 int error, dpreattrerr, dpostattrerr;
4038 uint32_t len = 0;
4039 uid_t saved_uid;
4040 vnode_t vp, dvp, dirp;
4041 struct vnode_attr dpreattr, dpostattr;
4042 struct nfs_filehandle nfh;
4043 struct nfs_export *nx = NULL;
4044 struct nfs_export_options *nxo = NULL;
4045 struct nameidata ni;
4046 struct nfsm_chain *nmreq, nmrep;
4047
4048 error = 0;
4049 dpreattrerr = dpostattrerr = ENOENT;
4050 saved_uid = kauth_cred_getuid(cred: nd->nd_cr);
4051 nmreq = &nd->nd_nmreq;
4052 nfsm_chain_null(&nmrep);
4053
4054 vp = dvp = dirp = NULL;
4055
4056 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4057 nfsm_chain_get_32(error, nmreq, len);
4058 nfsm_name_len_check(error, nd, len);
4059 nfsmerr_if(error);
4060
4061 NDINIT(&ni, DELETE, OP_UNLINK, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE, 0, ctx);
4062 error = nfsm_chain_get_path_namei(nmreq, len, &ni);
4063 if (!error) {
4064 error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo);
4065 if (nx != NULL) {
4066 /* update export stats */
4067 NFSStatAdd64(&nx->nx_stats.ops, 1);
4068
4069 /* update active user stats */
4070 nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0);
4071 }
4072 }
4073 if (dirp) {
4074 if (nd->nd_vers == NFS_VER3) {
4075 nfsm_srv_pre_vattr_init(&dpreattr);
4076 dpreattrerr = vnode_getattr(vp: dirp, vap: &dpreattr, ctx);
4077 } else {
4078 vnode_put(vp: dirp);
4079 dirp = NULL;
4080 }
4081 }
4082 nfsmerr_if(error);
4083
4084 dvp = ni.ni_dvp;
4085 vp = ni.ni_vp;
4086
4087 if (vnode_vtype(vp) != VDIR) {
4088 error = ENOTDIR;
4089 goto out;
4090 }
4091 /*
4092 * No rmdir "." please.
4093 */
4094 if (dvp == vp) {
4095 error = EINVAL;
4096 goto out;
4097 }
4098 /*
4099 * No rmdir ".." please.
4100 */
4101 if (vnode_parent(vp: dvp) == vp) {
4102 error = EINVAL;
4103 goto out;
4104 }
4105 /*
4106 * The root of a mounted filesystem cannot be deleted.
4107 */
4108 if (vnode_isvroot(vp)) {
4109 error = EBUSY;
4110 }
4111 if (!error) {
4112 error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0);
4113 }
4114 if (!error) {
4115 error = vn_authorize_rmdir(dvp, vp, cnp: &ni.ni_cnd, ctx, NULL);
4116 if (error) {
4117 error = EACCES;
4118 }
4119 }
4120
4121 if (!error) {
4122#if CONFIG_FSE
4123 char *path = NULL;
4124 int plen = 0;
4125 fse_info finfo;
4126
4127 if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, vp: dvp)) {
4128 plen = MAXPATHLEN;
4129 if ((path = get_pathbuff()) && !vn_getpath(vp, pathbuf: path, len: &plen)) {
4130 get_fse_info(vp, fse: &finfo, ctx);
4131 } else if (path) {
4132 release_pathbuff(path);
4133 path = NULL;
4134 }
4135 }
4136#endif /* CONFIG_FSE */
4137
4138 error = VNOP_RMDIR(dvp, vp, &ni.ni_cnd, ctx);
4139
4140#if CONFIG_FSE
4141 if (path) {
4142 if (!error) {
4143 add_fsevent(FSE_DELETE, ctx,
4144 FSE_ARG_STRING, plen, path,
4145 FSE_ARG_FINFO, &finfo,
4146 FSE_ARG_DONE);
4147 }
4148 release_pathbuff(path);
4149 }
4150#endif /* CONFIG_FSE */
4151 }
4152out:
4153 /*
4154 * nameidone has to happen before we vnode_put(dvp)
4155 * since it may need to release the fs_nodelock on the dvp
4156 */
4157 nameidone(&ni);
4158
4159 vnode_put(vp: dvp);
4160 vnode_put(vp);
4161
4162 if (dirp) {
4163 nfsm_srv_vattr_init(&dpostattr, nd->nd_vers);
4164 dpostattrerr = vnode_getattr(vp: dirp, vap: &dpostattr, ctx);
4165 vnode_put(vp: dirp);
4166 dirp = NULL;
4167 }
4168
4169nfsmerr:
4170 /* assemble reply */
4171 nd->nd_repstat = error;
4172 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers));
4173 nfsmout_if(error);
4174 *mrepp = nmrep.nmc_mhead;
4175 nfsmout_on_status(nd, error);
4176 if (nd->nd_vers == NFS_VER3) {
4177 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4178 dpreattrerr, &dpreattr, dpostattrerr, &dpostattr);
4179 }
4180nfsmout:
4181 nfsm_chain_build_done(error, &nmrep);
4182 if (dirp) {
4183 vnode_put(vp: dirp);
4184 }
4185 if (error) {
4186 nfsm_chain_cleanup(&nmrep);
4187 *mrepp = NULL;
4188 }
4189 return error;
4190}
4191
4192/*
4193 * nfs readdir service
4194 * - mallocs what it thinks is enough to read
4195 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4196 * - calls VNOP_READDIR()
4197 * - loops around building the reply
4198 * if the output generated exceeds count break out of loop
4199 * The nfsm_clget macro is used here so that the reply will be packed
4200 * tightly in mbuf clusters.
4201 * - it only knows that it has encountered eof when the VNOP_READDIR()
4202 * reads nothing
4203 * - as such one readdir rpc will return eof false although you are there
4204 * and then the next will return eof
4205 * - it trims out records with d_fileno == 0
4206 * this doesn't matter for Unix clients, but they might confuse clients
4207 * for other os'.
4208 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4209 * than requested, but this may not apply to all filesystems. For
4210 * example, client NFS does not { although it is never remote mounted
4211 * anyhow }
4212 * The alternate call nfsrv_readdirplus() does lookups as well.
4213 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
4214 * are supposed to cover. For readdir, the count is the total number of
4215 * bytes included in everything from the directory's postopattr through
4216 * the EOF flag. For readdirplus, the maxcount is the same, and the
4217 * dircount includes all that except for the entry attributes and handles.
4218 */
4219int
4220nfsrv_readdir(
4221 struct nfsrv_descript *nd,
4222 struct nfsrv_sock *slp,
4223 vfs_context_t ctx,
4224 mbuf_t *mrepp)
4225{
4226 struct direntry *dp;
4227 char *cpos, *cend, *rbuf;
4228 size_t rbuf_siz;
4229 vnode_t vp;
4230 struct vnode_attr attr = {};
4231 struct nfs_filehandle nfh;
4232 struct nfs_export *nx;
4233 struct nfs_export_options *nxo;
4234 uio_t auio = NULL;
4235 UIO_STACKBUF(uio_buf, 1);
4236 int len, nlen, rem, xfer, error, attrerr;
4237 int siz, count, fullsiz, eofflag, nentries;
4238 u_quad_t off, toff, verf = 0;
4239 int vnopflag;
4240 struct nfsm_chain *nmreq, nmrep;
4241
4242 error = 0;
4243 attrerr = ENOENT;
4244 count = nentries = 0;
4245 nmreq = &nd->nd_nmreq;
4246 nfsm_chain_null(&nmrep);
4247 rbuf = NULL;
4248 rbuf_siz = 0;
4249 vp = NULL;
4250
4251 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4252
4253 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4254 if (nd->nd_vers == NFS_VER3) {
4255 nfsm_chain_get_64(error, nmreq, toff);
4256 nfsm_chain_get_64(error, nmreq, verf);
4257 } else {
4258 nfsm_chain_get_32(error, nmreq, toff);
4259 }
4260 nfsm_chain_get_32(error, nmreq, count);
4261 nfsmerr_if(error);
4262
4263 off = toff;
4264 siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4265 xfer = NFSRV_NDMAXDATA(nd);
4266 if (siz > xfer) {
4267 siz = xfer;
4268 }
4269 fullsiz = siz;
4270
4271 if (fullsiz == 0) {
4272 error = NFSERR_TOOSMALL;
4273 goto nfsmerr;
4274 }
4275
4276 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4277 nfsmerr_if(error);
4278
4279 /* update export stats */
4280 NFSStatAdd64(&nx->nx_stats.ops, 1);
4281
4282 /* update active user stats */
4283 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(cred: nd->nd_cr), 1, 0, 0);
4284
4285 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4286 nfsmerr_if(error);
4287
4288 if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2) {
4289 vnopflag |= VNODE_READDIR_NAMEMAX;
4290 }
4291
4292 if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS)) {
4293 vnopflag |= VNODE_READDIR_SEEKOFF32;
4294 }
4295
4296 if (nd->nd_vers == NFS_VER3) {
4297 nfsm_srv_vattr_init(&attr, NFS_VER3);
4298 error = attrerr = vnode_getattr(vp, vap: &attr, ctx);
4299 if (!error && verf && (verf != attr.va_filerev)) {
4300 error = NFSERR_BAD_COOKIE;
4301 }
4302 }
4303 if (!error) {
4304 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4305 }
4306#if CONFIG_MACF
4307 if (!error) {
4308 if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4309 error = EACCES;
4310 }
4311
4312 if (!error) {
4313 error = mac_vnode_check_readdir(ctx, vp);
4314 }
4315 }
4316#endif
4317 nfsmerr_if(error);
4318
4319 rbuf_siz = siz;
4320 rbuf = kalloc_data(rbuf_siz, Z_WAITOK);
4321 if (rbuf) {
4322 auio = uio_createwithbuffer(a_iovcount: 1, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ,
4323 a_buf_p: &uio_buf[0], a_buffer_size: sizeof(uio_buf));
4324 }
4325 if (!rbuf || !auio) {
4326 error = ENOMEM;
4327 goto nfsmerr;
4328 }
4329again:
4330 uio_reset(a_uio: auio, a_offset: off, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ);
4331 uio_addiov(a_uio: auio, CAST_USER_ADDR_T(rbuf), a_length: fullsiz);
4332 eofflag = 0;
4333 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4334 off = uio_offset(a_uio: auio);
4335
4336 if (nd->nd_vers == NFS_VER3) {
4337 nfsm_srv_vattr_init(&attr, NFS_VER3);
4338 attrerr = vnode_getattr(vp, vap: &attr, ctx);
4339 }
4340 nfsmerr_if(error);
4341
4342 if (uio_resid(a_uio: auio) != 0) {
4343 siz -= uio_resid(a_uio: auio);
4344
4345 /* If nothing read, return empty reply with eof set */
4346 if (siz == 0) {
4347 vnode_put(vp);
4348 vp = NULL;
4349 kfree_data(rbuf, rbuf_siz);
4350 /* assemble reply */
4351 nd->nd_repstat = error;
4352 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4353 NFSX_COOKIEVERF(nd->nd_vers) + 2 * NFSX_UNSIGNED);
4354 nfsmout_if(error);
4355 *mrepp = nmrep.nmc_mhead;
4356 nfsmout_on_status(nd, error);
4357 if (nd->nd_vers == NFS_VER3) {
4358 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4359 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4360 }
4361 nfsm_chain_add_32(error, &nmrep, FALSE);
4362 nfsm_chain_add_32(error, &nmrep, TRUE);
4363 nfsm_chain_build_done(error, &nmrep);
4364 return error;
4365 }
4366 }
4367
4368 /*
4369 * Check for degenerate cases of nothing useful read.
4370 * If so go try again
4371 */
4372 cpos = rbuf;
4373 cend = rbuf + siz;
4374 dp = (struct direntry *)cpos;
4375 while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4376 cpos += dp->d_reclen;
4377 dp = (struct direntry *)cpos;
4378 nentries--;
4379 }
4380 if ((cpos >= cend) || (nentries == 0)) {
4381 toff = off;
4382 siz = fullsiz;
4383 goto again;
4384 }
4385
4386 vnode_put(vp);
4387 vp = NULL;
4388
4389 /* assemble reply */
4390 nd->nd_repstat = error;
4391 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) +
4392 NFSX_COOKIEVERF(nd->nd_vers) + siz);
4393 nfsmout_if(error);
4394 *mrepp = nmrep.nmc_mhead;
4395 nfsmout_on_status(nd, error);
4396 nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4397
4398 len = 2 * NFSX_UNSIGNED;
4399 if (nd->nd_vers == NFS_VER3) {
4400 len += NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF;
4401 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4402 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4403 nfsmerr_if(error);
4404 }
4405
4406 /* Loop through the records and build reply */
4407 while ((cpos < cend) && (nentries > 0)) {
4408 if (dp->d_fileno != 0) {
4409 nlen = dp->d_namlen;
4410 if ((nd->nd_vers == NFS_VER2) && (nlen > NFS_MAXNAMLEN)) {
4411 nlen = NFS_MAXNAMLEN;
4412 }
4413 rem = nfsm_rndup(nlen) - nlen;
4414 len += (4 * NFSX_UNSIGNED + nlen + rem);
4415 if (nd->nd_vers == NFS_VER3) {
4416 len += 2 * NFSX_UNSIGNED;
4417 }
4418 if (len > count) {
4419 eofflag = 0;
4420 break;
4421 }
4422 /* Build the directory record xdr from the direntry. */
4423 nfsm_chain_add_32(error, &nmrep, TRUE);
4424 if (nd->nd_vers == NFS_VER3) {
4425 nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4426 } else {
4427 nfsm_chain_add_32(error, &nmrep, dp->d_fileno);
4428 }
4429 nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4430 if (nd->nd_vers == NFS_VER3) {
4431 if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4432 dp->d_seekoff &= 0x00000000ffffffffULL;
4433 }
4434 nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4435 } else {
4436 nfsm_chain_add_32(error, &nmrep, dp->d_seekoff);
4437 }
4438 nfsmerr_if(error);
4439 }
4440 cpos += dp->d_reclen;
4441 dp = (struct direntry *)cpos;
4442 nentries--;
4443 }
4444 nfsm_chain_add_32(error, &nmrep, FALSE);
4445 nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4446 kfree_data(rbuf, rbuf_siz);
4447 goto nfsmout;
4448nfsmerr:
4449 if (rbuf) {
4450 kfree_data(rbuf, rbuf_siz);
4451 }
4452 if (vp) {
4453 vnode_put(vp);
4454 }
4455 nd->nd_repstat = error;
4456 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers));
4457 nfsmout_if(error);
4458 *mrepp = nmrep.nmc_mhead;
4459 nfsmout_on_status(nd, error);
4460 if (nd->nd_vers == NFS_VER3) {
4461 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4462 }
4463nfsmout:
4464 nfsm_chain_build_done(error, &nmrep);
4465 if (error) {
4466 nfsm_chain_cleanup(&nmrep);
4467 *mrepp = NULL;
4468 }
4469 return error;
4470}
4471
4472int
4473nfsrv_readdirplus(
4474 struct nfsrv_descript *nd,
4475 struct nfsrv_sock *slp,
4476 vfs_context_t ctx,
4477 mbuf_t *mrepp)
4478{
4479 struct direntry *dp;
4480 char *cpos, *cend, *rbuf;
4481 size_t rbuf_siz;
4482 vnode_t vp, nvp;
4483 struct nfs_filehandle dnfh, nfh;
4484 struct nfs_export *nx;
4485 struct nfs_export_options *nxo;
4486 uio_t auio = NULL;
4487 UIO_STACKBUF(uio_buf, 1);
4488 struct vnode_attr attr, va, *vap = &va;
4489 int len, nlen, rem, xfer, error, attrerr, gotfh, gotattr;
4490 int siz, dircount, maxcount, fullsiz, eofflag, dirlen, nentries, isdotdot;
4491 u_quad_t off, toff, verf;
4492 int vnopflag;
4493 struct nfsm_chain *nmreq, nmrep;
4494
4495 error = 0;
4496 attrerr = ENOENT;
4497 nentries = 0;
4498 nmreq = &nd->nd_nmreq;
4499 nfsm_chain_null(&nmrep);
4500 rbuf = NULL;
4501 rbuf_siz = 0;
4502 vp = NULL;
4503 dircount = maxcount = 0;
4504
4505 vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
4506
4507 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, dnfh.nfh_fhp, dnfh.nfh_len);
4508 nfsm_chain_get_64(error, nmreq, toff);
4509 nfsm_chain_get_64(error, nmreq, verf);
4510 nfsm_chain_get_32(error, nmreq, dircount);
4511 nfsm_chain_get_32(error, nmreq, maxcount);
4512 nfsmerr_if(error);
4513
4514 off = toff;
4515 xfer = NFSRV_NDMAXDATA(nd);
4516 dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4517 if (dircount > xfer) {
4518 dircount = xfer;
4519 }
4520 fullsiz = siz = dircount;
4521 maxcount = ((maxcount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
4522 if (maxcount > xfer) {
4523 maxcount = xfer;
4524 }
4525
4526 if (maxcount == 0) {
4527 error = NFSERR_TOOSMALL;
4528 goto nfsmerr;
4529 }
4530
4531 error = nfsrv_fhtovp(&dnfh, nd, &vp, &nx, &nxo);
4532 nfsmerr_if(error);
4533
4534 /* update export stats */
4535 NFSStatAdd64(&nx->nx_stats.ops, 1);
4536
4537 /* update active user stats */
4538 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(cred: nd->nd_cr), 1, 0, 0);
4539
4540 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4541 nfsmerr_if(error);
4542
4543 if (nxo->nxo_flags & NX_32BITCLIENTS) {
4544 vnopflag |= VNODE_READDIR_SEEKOFF32;
4545 }
4546
4547 if (nxo->nxo_flags & NX_MANGLEDNAMES) {
4548 vnopflag |= VNODE_READDIR_NAMEMAX;
4549 }
4550
4551 nfsm_srv_vattr_init(&attr, NFS_VER3);
4552 error = attrerr = vnode_getattr(vp, vap: &attr, ctx);
4553 if (!error && verf && (verf != attr.va_filerev)) {
4554 error = NFSERR_BAD_COOKIE;
4555 }
4556 if (!error) {
4557 error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0);
4558 }
4559#if CONFIG_MACF
4560 if (!error) {
4561 if (!error && mac_vnode_check_open(ctx, vp, FREAD)) {
4562 error = EACCES;
4563 }
4564
4565 if (!error) {
4566 error = mac_vnode_check_readdir(ctx, vp);
4567 }
4568 }
4569#endif
4570 nfsmerr_if(error);
4571
4572 rbuf_siz = siz;
4573 rbuf = kalloc_data(siz, Z_WAITOK);
4574 if (rbuf) {
4575 auio = uio_createwithbuffer(a_iovcount: 1, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ,
4576 a_buf_p: &uio_buf[0], a_buffer_size: sizeof(uio_buf));
4577 }
4578 if (!rbuf || !auio) {
4579 error = ENOMEM;
4580 goto nfsmerr;
4581 }
4582
4583again:
4584 uio_reset(a_uio: auio, a_offset: off, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ);
4585 uio_addiov(a_uio: auio, CAST_USER_ADDR_T(rbuf), a_length: fullsiz);
4586 eofflag = 0;
4587 error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, ctx);
4588 off = uio_offset(a_uio: auio);
4589 nfsm_srv_vattr_init(&attr, NFS_VER3);
4590 attrerr = vnode_getattr(vp, vap: &attr, ctx);
4591 nfsmerr_if(error);
4592
4593 if (uio_resid(a_uio: auio) != 0) {
4594 siz -= uio_resid(a_uio: auio);
4595
4596 /* If nothing read, return empty reply with eof set */
4597 if (siz == 0) {
4598 vnode_put(vp);
4599 vp = NULL;
4600 kfree_data(rbuf, rbuf_siz);
4601 /* assemble reply */
4602 nd->nd_repstat = error;
4603 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR +
4604 NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED);
4605 nfsmout_if(error);
4606 *mrepp = nmrep.nmc_mhead;
4607 nfsmout_on_status(nd, error);
4608 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4609 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4610 nfsm_chain_add_32(error, &nmrep, FALSE);
4611 nfsm_chain_add_32(error, &nmrep, TRUE);
4612 nfsm_chain_build_done(error, &nmrep);
4613 return error;
4614 }
4615 }
4616
4617 /*
4618 * Check for degenerate cases of nothing useful read.
4619 * If so go try again
4620 */
4621 cpos = rbuf;
4622 cend = rbuf + siz;
4623 dp = (struct direntry *)cpos;
4624 while ((dp->d_fileno == 0) && (cpos < cend) && (nentries > 0)) {
4625 cpos += dp->d_reclen;
4626 dp = (struct direntry *)cpos;
4627 nentries--;
4628 }
4629 if ((cpos >= cend) || (nentries == 0)) {
4630 toff = off;
4631 siz = fullsiz;
4632 goto again;
4633 }
4634
4635 /*
4636 * Probe the directory to see if the filesystem supports VGET.
4637 */
4638 if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)attr.va_fileid, &nvp, ctx))) {
4639 if (error == ENOTSUP) { /* let others get passed back */
4640 error = NFSERR_NOTSUPP;
4641 }
4642 goto nfsmerr;
4643 }
4644 vnode_put(vp: nvp);
4645
4646 /* assemble reply */
4647 nd->nd_repstat = error;
4648 error = nfsrv_rephead(nd, slp, &nmrep, maxcount);
4649 nfsmout_if(error);
4650 *mrepp = nmrep.nmc_mhead;
4651 nfsmout_on_status(nd, error);
4652 nmrep.nmc_flags |= NFSM_CHAIN_FLAG_ADD_CLUSTERS;
4653
4654 dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
4655 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4656 nfsm_chain_add_64(error, &nmrep, attr.va_filerev);
4657 nfsmerr_if(error);
4658
4659 /* Loop through the records and build reply */
4660 while ((cpos < cend) && (nentries > 0)) {
4661 if (dp->d_fileno != 0) {
4662 nlen = dp->d_namlen;
4663 rem = nfsm_rndup(nlen) - nlen;
4664 gotfh = gotattr = 1;
4665
4666 /* Got to get the vnode for lookup per entry. */
4667 if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, ctx)) {
4668 /* Can't get the vnode... so no fh or attrs */
4669 gotfh = gotattr = 0;
4670 } else {
4671 isdotdot = ((dp->d_namlen == 2) &&
4672 (dp->d_name[0] == '.') && (dp->d_name[1] == '.'));
4673 if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, ctx, &nfh)) {
4674 gotfh = 0;
4675 }
4676 nfsm_srv_vattr_init(vap, NFS_VER3);
4677 if (vnode_getattr(vp: nvp, vap, ctx)) {
4678 gotattr = 0;
4679 }
4680 vnode_put(vp: nvp);
4681 }
4682
4683 /*
4684 * If either the dircount or maxcount will be
4685 * exceeded, get out now. Both of these lengths
4686 * are calculated conservatively, including all
4687 * XDR overheads.
4688 */
4689 len += 8 * NFSX_UNSIGNED + nlen + rem;
4690 if (gotattr) {
4691 len += NFSX_V3FATTR;
4692 }
4693 if (gotfh) {
4694 len += NFSX_UNSIGNED + nfsm_rndup(nfh.nfh_len);
4695 }
4696 dirlen += 6 * NFSX_UNSIGNED + nlen + rem;
4697 if ((len > maxcount) || (dirlen > dircount)) {
4698 eofflag = 0;
4699 break;
4700 }
4701
4702 /* Build the directory record xdr from the direntry. */
4703 nfsm_chain_add_32(error, &nmrep, TRUE);
4704 nfsm_chain_add_64(error, &nmrep, dp->d_fileno);
4705 nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen);
4706 if (vnopflag & VNODE_READDIR_SEEKOFF32) {
4707 dp->d_seekoff &= 0x00000000ffffffffULL;
4708 }
4709 nfsm_chain_add_64(error, &nmrep, dp->d_seekoff);
4710 nfsm_chain_add_postop_attr(error, nd, &nmrep, (gotattr ? 0 : ENOENT), vap);
4711 if (gotfh) {
4712 nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len);
4713 } else {
4714 nfsm_chain_add_32(error, &nmrep, FALSE);
4715 }
4716 nfsmerr_if(error);
4717 }
4718 cpos += dp->d_reclen;
4719 dp = (struct direntry *)cpos;
4720 nentries--;
4721 }
4722 vnode_put(vp);
4723 vp = NULL;
4724 nfsm_chain_add_32(error, &nmrep, FALSE);
4725 nfsm_chain_add_32(error, &nmrep, eofflag ? TRUE : FALSE);
4726 kfree_data(rbuf, rbuf_siz);
4727 goto nfsmout;
4728nfsmerr:
4729 if (rbuf) {
4730 kfree_data(rbuf, rbuf_siz);
4731 }
4732 nd->nd_repstat = error;
4733 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR);
4734 nfsmout_if(error);
4735 *mrepp = nmrep.nmc_mhead;
4736 nfsmout_on_status(nd, error);
4737 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4738nfsmout:
4739 nfsm_chain_build_done(error, &nmrep);
4740 if (vp) {
4741 vnode_put(vp);
4742 }
4743 if (error) {
4744 nfsm_chain_cleanup(&nmrep);
4745 *mrepp = NULL;
4746 }
4747 return error;
4748}
4749
4750/*
4751 * nfs commit service
4752 */
4753int
4754nfsrv_commit(
4755 struct nfsrv_descript *nd,
4756 struct nfsrv_sock *slp,
4757 vfs_context_t ctx,
4758 mbuf_t *mrepp)
4759{
4760 vnode_t vp;
4761 struct nfs_filehandle nfh;
4762 struct nfs_export *nx = NULL;
4763 struct nfs_export_options *nxo;
4764 int error, preattrerr, postattrerr, count;
4765 struct vnode_attr preattr, postattr;
4766 u_quad_t off;
4767 struct nfsm_chain *nmreq, nmrep;
4768
4769 error = 0;
4770 preattrerr = postattrerr = ENOENT;
4771 nmreq = &nd->nd_nmreq;
4772 nfsm_chain_null(&nmrep);
4773 vp = NULL;
4774
4775 /*
4776 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4777 * count parameters, so those arguments are useless (someday maybe).
4778 */
4779
4780 nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
4781 nfsm_chain_get_64(error, nmreq, off);
4782 nfsm_chain_get_32(error, nmreq, count);
4783 nfsmerr_if(error);
4784
4785 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4786 nfsmerr_if(error);
4787
4788 /* update export stats */
4789 NFSStatAdd64(&nx->nx_stats.ops, 1);
4790
4791 /* update active user stats */
4792 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(cred: nd->nd_cr), 1, 0, 0);
4793
4794 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4795 nfsmerr_if(error);
4796
4797 /* This must identify a file system object of type, NF3REG */
4798 if (vnode_vtype(vp) != VREG) {
4799 error = NFSERR_BADTYPE;
4800 goto nfsmerr;
4801 }
4802
4803 nfsm_srv_pre_vattr_init(&preattr);
4804 preattrerr = vnode_getattr(vp, vap: &preattr, ctx);
4805
4806 error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
4807
4808 nfsm_srv_vattr_init(&postattr, 1);
4809 postattrerr = vnode_getattr(vp, vap: &postattr, ctx);
4810
4811nfsmerr:
4812 if (vp) {
4813 vnode_put(vp);
4814 }
4815
4816 /* assemble reply */
4817 nd->nd_repstat = error;
4818 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
4819 nfsmout_if(error);
4820 *mrepp = nmrep.nmc_mhead;
4821 nfsmout_on_status(nd, error);
4822 nfsm_chain_add_wcc_data(error, nd, &nmrep,
4823 preattrerr, &preattr, postattrerr, &postattr);
4824 if (!nd->nd_repstat) {
4825 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec);
4826 nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec);
4827 }
4828nfsmout:
4829 nfsm_chain_build_done(error, &nmrep);
4830 if (error) {
4831 nfsm_chain_cleanup(&nmrep);
4832 *mrepp = NULL;
4833 }
4834 return error;
4835}
4836
4837/*
4838 * nfs statfs service
4839 */
4840int
4841nfsrv_statfs(
4842 struct nfsrv_descript *nd,
4843 struct nfsrv_sock *slp,
4844 vfs_context_t ctx,
4845 mbuf_t *mrepp)
4846{
4847 struct vfs_attr va = {};
4848 int error, attrerr;
4849 vnode_t vp;
4850 struct vnode_attr attr;
4851 struct nfs_filehandle nfh;
4852 struct nfs_export *nx;
4853 struct nfs_export_options *nxo;
4854 off_t blksize;
4855 struct nfsm_chain *nmreq, nmrep;
4856
4857 error = 0;
4858 attrerr = ENOENT;
4859 nmreq = &nd->nd_nmreq;
4860 nfsm_chain_null(&nmrep);
4861 vp = NULL;
4862 blksize = 512;
4863
4864 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4865 nfsmerr_if(error);
4866 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4867 nfsmerr_if(error);
4868
4869 /* update export stats */
4870 NFSStatAdd64(&nx->nx_stats.ops, 1);
4871
4872 /* update active user stats */
4873 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(cred: nd->nd_cr), 1, 0, 0);
4874
4875 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4876 nfsmerr_if(error);
4877
4878 VFSATTR_INIT(&va);
4879 VFSATTR_WANTED(&va, f_blocks);
4880 VFSATTR_WANTED(&va, f_bfree);
4881 VFSATTR_WANTED(&va, f_bavail);
4882 VFSATTR_WANTED(&va, f_files);
4883 VFSATTR_WANTED(&va, f_ffree);
4884 error = vfs_getattr(mp: vnode_mount(vp), vfa: &va, ctx);
4885 blksize = vfs_statfs(mp: vnode_mount(vp))->f_bsize;
4886
4887 if (nd->nd_vers == NFS_VER3) {
4888 nfsm_srv_vattr_init(&attr, nd->nd_vers);
4889 attrerr = vnode_getattr(vp, vap: &attr, ctx);
4890 }
4891
4892nfsmerr:
4893 if (vp) {
4894 vnode_put(vp);
4895 }
4896
4897 /* assemble reply */
4898 nd->nd_repstat = error;
4899 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + NFSX_STATFS(nd->nd_vers));
4900 nfsmout_if(error);
4901 *mrepp = nmrep.nmc_mhead;
4902 nfsmout_on_status(nd, error);
4903 if (nd->nd_vers == NFS_VER3) {
4904 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4905 }
4906 nfsmout_if(nd->nd_repstat);
4907
4908 if (nd->nd_vers == NFS_VER3) {
4909 nfsm_chain_add_64(error, &nmrep, va.f_blocks * blksize);
4910 nfsm_chain_add_64(error, &nmrep, va.f_bfree * blksize);
4911 nfsm_chain_add_64(error, &nmrep, va.f_bavail * blksize);
4912 nfsm_chain_add_64(error, &nmrep, va.f_files);
4913 nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4914 nfsm_chain_add_64(error, &nmrep, va.f_ffree);
4915 nfsm_chain_add_32(error, &nmrep, 0); /* invarsec */
4916 } else {
4917 nfsm_chain_add_32(error, &nmrep, NFS_V2MAXDATA);
4918 nfsm_chain_add_32(error, &nmrep, blksize);
4919 nfsm_chain_add_32(error, &nmrep, va.f_blocks);
4920 nfsm_chain_add_32(error, &nmrep, va.f_bfree);
4921 nfsm_chain_add_32(error, &nmrep, va.f_bavail);
4922 }
4923nfsmout:
4924 nfsm_chain_build_done(error, &nmrep);
4925 if (error) {
4926 nfsm_chain_cleanup(&nmrep);
4927 *mrepp = NULL;
4928 }
4929 return error;
4930}
4931
4932/*
4933 * nfs fsinfo service
4934 */
4935int
4936nfsrv_fsinfo(
4937 struct nfsrv_descript *nd,
4938 struct nfsrv_sock *slp,
4939 vfs_context_t ctx,
4940 mbuf_t *mrepp)
4941{
4942 int error, attrerr, prefsize, maxsize;
4943 vnode_t vp;
4944 struct vnode_attr attr;
4945 struct nfs_filehandle nfh;
4946 struct nfs_export *nx;
4947 struct nfs_export_options *nxo;
4948 struct nfsm_chain *nmreq, nmrep;
4949
4950 error = 0;
4951 attrerr = ENOENT;
4952 nmreq = &nd->nd_nmreq;
4953 nfsm_chain_null(&nmrep);
4954 vp = NULL;
4955
4956 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
4957 nfsmerr_if(error);
4958 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
4959 nfsmerr_if(error);
4960
4961 /* update export stats */
4962 NFSStatAdd64(&nx->nx_stats.ops, 1);
4963
4964 /* update active user stats */
4965 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(cred: nd->nd_cr), 1, 0, 0);
4966
4967 error = nfsrv_credcheck(nd, ctx, nx, nxo);
4968 nfsmerr_if(error);
4969
4970 nfsm_srv_vattr_init(&attr, NFS_VER3);
4971 attrerr = vnode_getattr(vp, vap: &attr, ctx);
4972
4973nfsmerr:
4974 if (vp) {
4975 vnode_put(vp);
4976 }
4977
4978 /* assemble reply */
4979 nd->nd_repstat = error;
4980 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
4981 nfsmout_if(error);
4982 *mrepp = nmrep.nmc_mhead;
4983 nfsmout_on_status(nd, error);
4984 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
4985 nfsmout_if(nd->nd_repstat);
4986
4987 /*
4988 * XXX There should be file system VFS OP(s) to get this information.
4989 * For now, assume our usual NFS defaults.
4990 */
4991 if (slp->ns_sotype == SOCK_DGRAM) {
4992 maxsize = NFS_MAXDGRAMDATA;
4993 prefsize = NFS_PREFDGRAMDATA;
4994 } else {
4995 maxsize = prefsize = NFSRV_MAXDATA;
4996 }
4997
4998 nfsm_chain_add_32(error, &nmrep, maxsize);
4999 nfsm_chain_add_32(error, &nmrep, prefsize);
5000 nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5001 nfsm_chain_add_32(error, &nmrep, maxsize);
5002 nfsm_chain_add_32(error, &nmrep, prefsize);
5003 nfsm_chain_add_32(error, &nmrep, NFS_FABLKSIZE);
5004 nfsm_chain_add_32(error, &nmrep, prefsize);
5005 nfsm_chain_add_64(error, &nmrep, 0xffffffffffffffffULL);
5006 nfsm_chain_add_32(error, &nmrep, 0);
5007 nfsm_chain_add_32(error, &nmrep, 1);
5008 /* XXX link/symlink support should be taken from volume capabilities */
5009 nfsm_chain_add_32(error, &nmrep,
5010 NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK |
5011 NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME);
5012
5013nfsmout:
5014 nfsm_chain_build_done(error, &nmrep);
5015 if (error) {
5016 nfsm_chain_cleanup(&nmrep);
5017 *mrepp = NULL;
5018 }
5019 return error;
5020}
5021
5022/*
5023 * nfs pathconf service
5024 */
5025int
5026nfsrv_pathconf(
5027 struct nfsrv_descript *nd,
5028 struct nfsrv_sock *slp,
5029 vfs_context_t ctx,
5030 mbuf_t *mrepp)
5031{
5032 int error, attrerr, linkmax = 0, namemax = 0;
5033 int chownres = 0, notrunc = 0, case_sensitive = 0, case_preserving = 0;
5034 vnode_t vp;
5035 struct vnode_attr attr;
5036 struct nfs_filehandle nfh;
5037 struct nfs_export *nx;
5038 struct nfs_export_options *nxo;
5039 struct nfsm_chain *nmreq, nmrep;
5040
5041 error = 0;
5042 attrerr = ENOENT;
5043 nmreq = &nd->nd_nmreq;
5044 nfsm_chain_null(&nmrep);
5045 vp = NULL;
5046
5047 nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
5048 nfsmerr_if(error);
5049 error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
5050 nfsmerr_if(error);
5051
5052 /* update export stats */
5053 NFSStatAdd64(&nx->nx_stats.ops, 1);
5054
5055 /* update active user stats */
5056 nfsrv_update_user_stat(nx, nd, kauth_cred_getuid(cred: nd->nd_cr), 1, 0, 0);
5057
5058 error = nfsrv_credcheck(nd, ctx, nx, nxo);
5059 nfsmerr_if(error);
5060
5061 error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, ctx);
5062 if (!error) {
5063 error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, ctx);
5064 }
5065 if (!error) {
5066 error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, ctx);
5067 }
5068 if (!error) {
5069 error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, &notrunc, ctx);
5070 }
5071 if (!error) {
5072 error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, ctx);
5073 }
5074 if (!error) {
5075 error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, ctx);
5076 }
5077
5078 nfsm_srv_vattr_init(&attr, NFS_VER3);
5079 attrerr = vnode_getattr(vp, vap: &attr, ctx);
5080
5081nfsmerr:
5082 if (vp) {
5083 vnode_put(vp);
5084 }
5085
5086 /* assemble reply */
5087 nd->nd_repstat = error;
5088 error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
5089 nfsmout_if(error);
5090 *mrepp = nmrep.nmc_mhead;
5091 nfsmout_on_status(nd, error);
5092 nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr);
5093 nfsmout_if(nd->nd_repstat);
5094
5095 nfsm_chain_add_32(error, &nmrep, linkmax);
5096 nfsm_chain_add_32(error, &nmrep, namemax);
5097 nfsm_chain_add_32(error, &nmrep, notrunc);
5098 nfsm_chain_add_32(error, &nmrep, chownres);
5099 nfsm_chain_add_32(error, &nmrep, !case_sensitive);
5100 nfsm_chain_add_32(error, &nmrep, case_preserving);
5101
5102nfsmout:
5103 nfsm_chain_build_done(error, &nmrep);
5104 if (error) {
5105 nfsm_chain_cleanup(&nmrep);
5106 *mrepp = NULL;
5107 }
5108 return error;
5109}
5110
5111/*
5112 * Null operation, used by clients to ping server
5113 */
5114/* ARGSUSED */
5115int
5116nfsrv_null(
5117 struct nfsrv_descript *nd,
5118 struct nfsrv_sock *slp,
5119 __unused vfs_context_t ctx,
5120 mbuf_t *mrepp)
5121{
5122 int error = NFSERR_RETVOID;
5123 struct nfsm_chain nmrep;
5124
5125 /*
5126 * RPCSEC_GSS context setup ?
5127 */
5128 if (nd->nd_gss_context) {
5129 return nfs_gss_svc_ctx_init(nd, slp, mrepp);
5130 }
5131
5132 nfsm_chain_null(&nmrep);
5133
5134 /* assemble reply */
5135 nd->nd_repstat = error;
5136 error = nfsrv_rephead(nd, slp, &nmrep, 0);
5137 nfsmout_if(error);
5138 *mrepp = nmrep.nmc_mhead;
5139nfsmout:
5140 nfsm_chain_build_done(error, &nmrep);
5141 if (error) {
5142 nfsm_chain_cleanup(&nmrep);
5143 *mrepp = NULL;
5144 }
5145 return error;
5146}
5147
5148/*
5149 * No operation, used for obsolete procedures
5150 */
5151/* ARGSUSED */
5152int
5153nfsrv_noop(
5154 struct nfsrv_descript *nd,
5155 struct nfsrv_sock *slp,
5156 __unused vfs_context_t ctx,
5157 mbuf_t *mrepp)
5158{
5159 int error;
5160 struct nfsm_chain nmrep;
5161
5162 nfsm_chain_null(&nmrep);
5163
5164 if (nd->nd_repstat) {
5165 error = nd->nd_repstat;
5166 } else {
5167 error = EPROCUNAVAIL;
5168 }
5169
5170 /* assemble reply */
5171 nd->nd_repstat = error;
5172 error = nfsrv_rephead(nd, slp, &nmrep, 0);
5173 nfsmout_if(error);
5174 *mrepp = nmrep.nmc_mhead;
5175nfsmout:
5176 nfsm_chain_build_done(error, &nmrep);
5177 if (error) {
5178 nfsm_chain_cleanup(&nmrep);
5179 *mrepp = NULL;
5180 }
5181 return error;
5182}
5183
5184const nfsrv_proc_t nfsrv_procs[NFS_NPROCS] = {
5185 nfsrv_null,
5186 nfsrv_getattr,
5187 nfsrv_setattr,
5188 nfsrv_lookup,
5189 nfsrv_access,
5190 nfsrv_readlink,
5191 nfsrv_read,
5192 nfsrv_write,
5193 nfsrv_create,
5194 nfsrv_mkdir,
5195 nfsrv_symlink,
5196 nfsrv_mknod,
5197 nfsrv_remove,
5198 nfsrv_rmdir,
5199 nfsrv_rename,
5200 nfsrv_link,
5201 nfsrv_readdir,
5202 nfsrv_readdirplus,
5203 nfsrv_statfs,
5204 nfsrv_fsinfo,
5205 nfsrv_pathconf,
5206 nfsrv_commit,
5207 nfsrv_noop
5208};
5209
5210/*
5211 * Perform access checking for vnodes obtained from file handles that would
5212 * refer to files already opened by a Unix client. You cannot just use
5213 * vnode_authorize() for two reasons.
5214 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5215 * 2 - The owner is to be given access irrespective of mode bits so that
5216 * processes that chmod after opening a file don't break. I don't like
5217 * this because it opens a security hole, but since the nfs server opens
5218 * a security hole the size of a barn door anyhow, what the heck.
5219 *
5220 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5221 * will return EPERM instead of EACCESS. EPERM is always an error.
5222 */
5223
5224int
5225nfsrv_authorize(
5226 vnode_t vp,
5227 vnode_t dvp,
5228 kauth_action_t action,
5229 vfs_context_t ctx,
5230 struct nfs_export_options *nxo,
5231 int override)
5232{
5233 struct vnode_attr vattr;
5234 int error;
5235
5236 if (action & KAUTH_VNODE_WRITE_RIGHTS) {
5237 /*
5238 * Disallow write attempts on read-only exports;
5239 * unless the file is a socket or a block or character
5240 * device resident on the file system.
5241 */
5242 if (nxo->nxo_flags & NX_READONLY) {
5243 switch (vnode_vtype(vp)) {
5244 case VREG: case VDIR: case VLNK: case VCPLX:
5245 return EROFS;
5246 default:
5247 break;
5248 }
5249 }
5250 }
5251 error = vnode_authorize(vp, dvp, action, ctx);
5252 /*
5253 * Allow certain operations for the owner (reads and writes
5254 * on files that are already open). Picking up from FreeBSD.
5255 */
5256 if (override && (error == EACCES)) {
5257 VATTR_INIT(&vattr);
5258 VATTR_WANTED(&vattr, va_uid);
5259 if ((vnode_getattr(vp, vap: &vattr, ctx) == 0) &&
5260 (kauth_cred_getuid(cred: vfs_context_ucred(ctx)) == vattr.va_uid)) {
5261 error = 0;
5262 }
5263 }
5264 return error;
5265}
5266
5267#endif /* CONFIG_NFS_SERVER */
5268