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