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