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