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