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