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