2 * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
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.
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
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 $
68 #include <nfs/nfs_conf.h>
71 #include <sys/param.h>
72 #include <sys/systm.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>
84 #include <sys/kernel.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>
94 #include <sys/vmparam.h>
96 #include <sys/fcntl.h>
98 #include <netinet/in.h>
100 #include <nfs/nfsproto.h>
101 #include <nfs/rpcv2.h>
103 #include <nfs/xdr_subs.h>
104 #include <nfs/nfsm_subs.h>
105 #include <nfs/nfsrvcache.h>
106 #include <nfs/nfs_gss.h>
109 #include <security/mac.h>
110 #include <security/mac_framework.h>
117 int nfsd_thread_count
= 0;
118 int nfsd_thread_max
= 0;
119 static LCK_GRP_DECLARE(nfsd_lck_grp
, "nfsd");
120 LCK_MTX_DECLARE(nfsd_mutex
, &nfsd_lck_grp
);
121 struct nfsd_head nfsd_head
, nfsd_queue
;
123 LCK_GRP_DECLARE(nfsrv_slp_rwlock_group
, "nfsrv-slp-rwlock");
124 LCK_GRP_DECLARE(nfsrv_slp_mutex_group
, "nfsrv-slp-mutex");
125 struct nfsrv_sockhead nfsrv_socklist
, nfsrv_sockwg
,
126 nfsrv_sockwait
, nfsrv_sockwork
;
127 struct nfsrv_sock
*nfsrv_udpsock
= NULL
;
128 struct nfsrv_sock
*nfsrv_udp6sock
= NULL
;
131 struct nfsrv_expfs_list nfsrv_exports
;
132 struct nfsrv_export_hashhead
*nfsrv_export_hashtbl
= NULL
;
133 int nfsrv_export_hash_size
= NFSRVEXPHASHSZ
;
134 u_long nfsrv_export_hash
;
135 static LCK_GRP_DECLARE(nfsrv_export_rwlock_group
, "nfsrv-export-rwlock");
136 LCK_RW_DECLARE(nfsrv_export_rwlock
, &nfsrv_export_rwlock_group
);
139 /* NFS server file modification event generator */
140 struct nfsrv_fmod_hashhead
*nfsrv_fmod_hashtbl
;
141 u_long nfsrv_fmod_hash
;
142 static LCK_GRP_DECLARE(nfsrv_fmod_grp
, "nfsrv_fmod");
143 LCK_MTX_DECLARE(nfsrv_fmod_mutex
, &nfsrv_fmod_grp
);
144 static int nfsrv_fmod_timer_on
= 0;
145 int nfsrv_fsevents_enabled
= 1;
148 /* NFS server timers */
150 thread_call_t nfsrv_fmod_timer_call
;
152 thread_call_t nfsrv_idlesock_timer_call
;
153 thread_call_t nfsrv_wg_timer_call
;
154 int nfsrv_wg_timer_on
;
156 /* globals for the active user list */
157 uint32_t nfsrv_user_stat_enabled
= 1;
158 uint32_t nfsrv_user_stat_node_count
= 0;
159 uint32_t nfsrv_user_stat_max_idle_sec
= NFSRV_USER_STAT_DEF_IDLE_SEC
;
160 uint32_t nfsrv_user_stat_max_nodes
= NFSRV_USER_STAT_DEF_MAX_NODES
;
161 LCK_GRP_DECLARE(nfsrv_active_user_mutex_group
, "nfs-active-user-mutex");
163 int nfsrv_wg_delay
= NFSRV_WGATHERDELAY
* 1000;
164 int nfsrv_wg_delay_v3
= 0;
168 int nfsrv_authorize(vnode_t
, vnode_t
, kauth_action_t
, vfs_context_t
, struct nfs_export_options
*, int);
169 int nfsrv_wg_coalesce(struct nfsrv_descript
*, struct nfsrv_descript
*);
170 void nfsrv_modified(vnode_t
, vfs_context_t
);
172 extern void IOSleep(int);
173 extern int safe_getpath(struct vnode
*dvp
, char *leafname
, char *path
, int _len
, int *truncated_path
);
176 * Initialize the data structures for the server.
179 #define NFSRV_NOT_INITIALIZED 0
180 #define NFSRV_INITIALIZING 1
181 #define NFSRV_INITIALIZED 2
182 static volatile UInt32 nfsrv_initted
= NFSRV_NOT_INITIALIZED
;
185 nfsrv_is_initialized(void)
187 return nfsrv_initted
== NFSRV_INITIALIZED
;
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()) {
202 if (sizeof(struct nfsrv_sock
) > NFS_SVCALLOC
) {
203 printf("struct nfsrv_sock bloated (> %dbytes)\n", NFS_SVCALLOC
);
206 /* init export data structures */
207 LIST_INIT(&nfsrv_exports
);
210 /* init NFS server file modified event generation */
211 nfsrv_fmod_hashtbl
= hashinit(NFSRVFMODHASHSZ
, M_TEMP
, &nfsrv_fmod_hash
);
214 /* initialize NFS server timer callouts */
216 nfsrv_fmod_timer_call
= thread_call_allocate(nfsrv_fmod_timer
, NULL
);
218 nfsrv_idlesock_timer_call
= thread_call_allocate(nfsrv_idlesock_timer
, NULL
);
219 nfsrv_wg_timer_call
= thread_call_allocate(nfsrv_wg_timer
, NULL
);
221 /* Init server data structures */
222 TAILQ_INIT(&nfsrv_socklist
);
223 TAILQ_INIT(&nfsrv_sockwait
);
224 TAILQ_INIT(&nfsrv_sockwork
);
225 TAILQ_INIT(&nfsrv_sockwg
);
226 TAILQ_INIT(&nfsd_head
);
227 TAILQ_INIT(&nfsd_queue
);
228 nfsrv_udpsock
= NULL
;
229 nfsrv_udp6sock
= NULL
;
231 /* Setup the up-call handling */
234 /* initialization complete */
235 nfsrv_initted
= NFSRV_INITIALIZED
;
241 * NFS version 2 and 3 server request processing functions
243 * These functions take the following parameters:
245 * struct nfsrv_descript *nd - the NFS request descriptor
246 * struct nfsrv_sock *slp - the NFS socket the request came in on
247 * vfs_context_t ctx - VFS context
248 * mbuf_t *mrepp - pointer to hold the reply mbuf list
250 * These routines generally have 3 phases:
252 * 1 - break down and validate the RPC request in the mbuf chain
253 * provided in nd->nd_nmreq.
254 * 2 - perform the vnode operations for the request
255 * (many are very similar to syscalls in vfs_syscalls.c and
256 * should therefore be kept in sync with those implementations)
257 * 3 - build the RPC reply in an mbuf chain (nmrep) and return the mbuf chain
262 * nfs v3 access service
266 struct nfsrv_descript
*nd
,
267 struct nfsrv_sock
*slp
,
271 struct nfsm_chain
*nmreq
, nmrep
;
274 struct vnode_attr vattr
;
275 struct nfs_filehandle nfh
;
277 kauth_action_t testaction
;
278 struct nfs_export
*nx
;
279 struct nfs_export_options
*nxo
;
284 nmreq
= &nd
->nd_nmreq
;
285 nfsm_chain_null(&nmrep
);
289 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
290 nfsm_chain_get_32(error
, nmreq
, nfsmode
);
292 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
295 /* update export stats */
296 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
298 /* update active user stats */
299 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
301 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
305 * Each NFS mode bit is tested separately.
307 * XXX this code is nominally correct, but returns a pessimistic
308 * rather than optimistic result. It will be necessary to add
309 * an NFS-specific interface to the vnode_authorize code to
310 * obtain good performance in the optimistic mode.
312 if (nfsmode
& NFS_ACCESS_READ
) {
313 testaction
= vnode_isdir(vp
) ? KAUTH_VNODE_LIST_DIRECTORY
: KAUTH_VNODE_READ_DATA
;
314 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
315 nfsmode
&= ~NFS_ACCESS_READ
;
318 if ((nfsmode
& NFS_ACCESS_LOOKUP
) &&
320 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_SEARCH
, ctx
, nxo
, 0))) {
321 nfsmode
&= ~NFS_ACCESS_LOOKUP
;
323 if (nfsmode
& NFS_ACCESS_MODIFY
) {
324 if (vnode_isdir(vp
)) {
326 KAUTH_VNODE_ADD_FILE
|
327 KAUTH_VNODE_ADD_SUBDIRECTORY
|
328 KAUTH_VNODE_DELETE_CHILD
;
331 KAUTH_VNODE_WRITE_DATA
;
333 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
334 nfsmode
&= ~NFS_ACCESS_MODIFY
;
337 if (nfsmode
& NFS_ACCESS_EXTEND
) {
338 if (vnode_isdir(vp
)) {
340 KAUTH_VNODE_ADD_FILE
|
341 KAUTH_VNODE_ADD_SUBDIRECTORY
;
344 KAUTH_VNODE_WRITE_DATA
|
345 KAUTH_VNODE_APPEND_DATA
;
347 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
348 nfsmode
&= ~NFS_ACCESS_EXTEND
;
353 * Note concerning NFS_ACCESS_DELETE:
354 * For hard links, the answer may be wrong if the vnode
355 * has multiple parents with different permissions.
356 * Also, some clients (e.g. MacOSX 10.3) may incorrectly
357 * interpret the missing/cleared DELETE bit.
358 * So we'll just leave the DELETE bit alone. At worst,
359 * we're telling the client it might be able to do
360 * something it really can't.
363 if ((nfsmode
& NFS_ACCESS_EXECUTE
) &&
365 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 0))) {
366 nfsmode
&= ~NFS_ACCESS_EXECUTE
;
369 /* get postop attributes */
370 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
371 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
375 nd
->nd_repstat
= error
;
376 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(NFS_VER3
) + NFSX_UNSIGNED
);
378 *mrepp
= nmrep
.nmc_mhead
;
379 nfsmout_on_status(nd
, error
);
380 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
381 if (!nd
->nd_repstat
) {
382 nfsm_chain_add_32(error
, &nmrep
, nfsmode
);
385 nfsm_chain_build_done(error
, &nmrep
);
390 nfsm_chain_cleanup(&nmrep
);
397 * nfs getattr service
401 struct nfsrv_descript
*nd
,
402 struct nfsrv_sock
*slp
,
406 struct nfsm_chain
*nmreq
, nmrep
;
407 struct vnode_attr vattr
;
410 struct nfs_filehandle nfh
;
411 struct nfs_export
*nx
;
412 struct nfs_export_options
*nxo
;
415 nmreq
= &nd
->nd_nmreq
;
416 nfsm_chain_null(&nmrep
);
420 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
422 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
425 /* update export stats */
426 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
428 /* update active user stats */
429 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
431 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
435 if (mac_vnode_check_open(ctx
, vp
, FREAD
)) {
441 nfsm_srv_vattr_init(&vattr
, nd
->nd_vers
);
442 error
= vnode_getattr(vp
, &vattr
, ctx
);
445 /* XXXab: Comment in the VFS code makes it sound like
446 * some arguments can be filtered out, but not
447 * what it actually means. Hopefully not like
448 * they gonna set mtime to 0 or something. For
449 * now trust there are no shenanigans here.
451 error
= mac_vnode_check_getattr(ctx
, NOCRED
, vp
, &vattr
);
460 nd
->nd_repstat
= error
;
461 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_FATTR(nd
->nd_vers
));
463 *mrepp
= nmrep
.nmc_mhead
;
464 nfsmout_if(nd
->nd_repstat
);
465 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &vattr
);
467 nfsm_chain_build_done(error
, &nmrep
);
472 nfsm_chain_cleanup(&nmrep
);
479 * nfs setattr service
483 struct nfsrv_descript
*nd
,
484 struct nfsrv_sock
*slp
,
488 struct nfsm_chain
*nmreq
, nmrep
;
489 struct vnode_attr preattr
, postattr
;
490 struct vnode_attr vattr
, *vap
= &vattr
;
492 struct nfs_export
*nx
;
493 struct nfs_export_options
*nxo
;
494 int error
, preattrerr
, postattrerr
, gcheck
;
495 struct nfs_filehandle nfh
;
496 struct timespec guard
= { .tv_sec
= 0, .tv_nsec
= 0 };
497 kauth_action_t action
;
501 preattrerr
= postattrerr
= ENOENT
;
503 nmreq
= &nd
->nd_nmreq
;
504 nfsm_chain_null(&nmrep
);
508 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
512 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
513 if (nd
->nd_vers
== NFS_VER3
) {
514 nfsm_chain_get_32(error
, nmreq
, gcheck
);
516 nfsm_chain_get_time(error
, nmreq
, nd
->nd_vers
, guard
.tv_sec
, guard
.tv_nsec
);
522 * Save the original credential UID in case they are
523 * mapped and we need to map the IDs in the attributes.
525 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
528 * Now that we have all the fields, lets do it.
530 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
533 /* update export stats */
534 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
536 /* update active user stats */
537 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
539 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
542 if (nd
->nd_vers
== NFS_VER3
) {
543 nfsm_srv_pre_vattr_init(&preattr
);
544 error
= preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
545 if (!error
&& gcheck
&& VATTR_IS_SUPPORTED(&preattr
, va_change_time
) &&
546 (preattr
.va_change_time
.tv_sec
!= guard
.tv_sec
||
547 preattr
.va_change_time
.tv_nsec
!= guard
.tv_nsec
)) {
548 error
= NFSERR_NOT_SYNC
;
550 if (!preattrerr
&& !VATTR_ALL_SUPPORTED(&preattr
)) {
557 * If the credentials were mapped, we should
558 * map the same values in the attributes.
560 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
562 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
563 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
) {
564 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
568 /* Authorize the attribute changes. */
569 error
= vnode_authattr(vp
, vap
, &action
, ctx
);
571 error
= nfsrv_authorize(vp
, NULL
, action
, ctx
, nxo
, 0);
575 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
| FWRITE
)) {
581 if (VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
)) {
582 error
= mac_vnode_check_setowner(ctx
, vp
,
583 VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: -1,
584 VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: -1);
587 if (!error
&& VATTR_IS_ACTIVE(vap
, va_mode
)) {
588 error
= mac_vnode_check_setmode(ctx
, vp
, (mode_t
)vap
->va_mode
);
591 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
592 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
593 error
= mac_vnode_check_truncate(ctx
, NOCRED
, vp
);
595 /* set utimes case */
596 if (!error
&& (VATTR_IS_ACTIVE(vap
, va_access_time
) || VATTR_IS_ACTIVE(vap
, va_modify_time
))) {
597 struct timespec current_time
;
598 nanotime(¤t_time
);
600 error
= mac_vnode_check_setutimes(ctx
, vp
,
601 VATTR_IS_ACTIVE(vap
, va_access_time
) ? vap
->va_access_time
: current_time
,
602 VATTR_IS_ACTIVE(vap
, va_modify_time
) ? vap
->va_modify_time
: current_time
);
606 /* set the new attributes */
608 error
= vnode_setattr(vp
, vap
, ctx
);
611 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
612 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
613 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
625 nd
->nd_repstat
= error
;
626 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCORFATTR(nd
->nd_vers
));
628 *mrepp
= nmrep
.nmc_mhead
;
629 nfsmout_on_status(nd
, error
);
630 if (nd
->nd_vers
== NFS_VER3
) {
631 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
632 preattrerr
, &preattr
, postattrerr
, &postattr
);
634 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
637 nfsm_chain_build_done(error
, &nmrep
);
639 nfsm_chain_cleanup(&nmrep
);
650 struct nfsrv_descript
*nd
,
651 struct nfsrv_sock
*slp
,
656 vnode_t vp
, dirp
= NULL
;
657 struct nfs_filehandle dnfh
, nfh
;
658 struct nfs_export
*nx
= NULL
;
659 struct nfs_export_options
*nxo
;
660 int error
, attrerr
, dirattrerr
, isdotdot
;
663 struct vnode_attr va
, dirattr
, *vap
= &va
;
664 struct nfsm_chain
*nmreq
, nmrep
;
667 attrerr
= dirattrerr
= ENOENT
;
668 nmreq
= &nd
->nd_nmreq
;
669 nfsm_chain_null(&nmrep
);
670 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
672 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
673 nfsm_chain_get_32(error
, nmreq
, len
);
674 nfsm_name_len_check(error
, nd
, len
);
677 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
679 ni
.ni_op
= OP_LOOKUP
;
681 ni
.ni_cnd
.cn_flags
= LOCKLEAF
;
682 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
683 isdotdot
= ((len
== 2) && (ni
.ni_cnd
.cn_pnbuf
[0] == '.') && (ni
.ni_cnd
.cn_pnbuf
[1] == '.'));
685 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
687 /* update export stats */
688 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
690 /* update active user stats */
691 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
693 if (!error
&& mac_vnode_check_open(ctx
, ni
.ni_vp
, FREAD
)) {
708 if (nd
->nd_vers
== NFS_VER3
) {
709 nfsm_srv_vattr_init(&dirattr
, NFS_VER3
);
710 dirattrerr
= vnode_getattr(dirp
, &dirattr
, ctx
);
719 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, (isdotdot
? &dnfh
: NULL
), vp
, ctx
, &nfh
);
721 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
722 attrerr
= vnode_getattr(vp
, vap
, ctx
);
728 nd
->nd_repstat
= error
;
729 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
730 NFSX_POSTOPORFATTR(nd
->nd_vers
) + NFSX_POSTOPATTR(nd
->nd_vers
));
732 *mrepp
= nmrep
.nmc_mhead
;
733 if (nd
->nd_repstat
) {
734 if (nd
->nd_vers
== NFS_VER3
) {
735 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
739 nfsm_chain_add_fh(error
, &nmrep
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
740 if (nd
->nd_vers
== NFS_VER3
) {
741 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
742 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
744 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
747 nfsm_chain_build_done(error
, &nmrep
);
749 nfsm_chain_cleanup(&nmrep
);
756 * nfs readlink service
760 struct nfsrv_descript
*nd
,
761 struct nfsrv_sock
*slp
,
765 int error
, mpcnt
, tlen
, len
, attrerr
;
767 struct vnode_attr vattr
;
768 struct nfs_filehandle nfh
;
769 struct nfs_export
*nx
;
770 struct nfs_export_options
*nxo
;
771 struct nfsm_chain
*nmreq
, nmrep
;
774 char uio_buf
[UIO_SIZEOF(4)];
775 char *uio_bufp
= &uio_buf
[0];
776 int uio_buflen
= UIO_SIZEOF(4);
780 nmreq
= &nd
->nd_nmreq
;
781 nfsm_chain_null(&nmrep
);
784 len
= NFS_MAXPATHLEN
;
786 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
789 /* get mbuf list to hold symlink path */
790 error
= nfsm_mbuf_get_list(len
, &mpath
, &mpcnt
);
793 uio_buflen
= UIO_SIZEOF(mpcnt
);
794 MALLOC(uio_bufp
, char*, uio_buflen
, M_TEMP
, M_WAITOK
);
800 auio
= uio_createwithbuffer(mpcnt
, 0, UIO_SYSSPACE
, UIO_READ
, uio_bufp
, uio_buflen
);
806 for (mp
= mpath
; mp
; mp
= mbuf_next(mp
)) {
807 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(mp
)), mbuf_len(mp
));
810 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
813 /* update export stats */
814 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
816 /* update active user stats */
817 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
819 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
822 if (vnode_vtype(vp
) != VLNK
) {
823 if (nd
->nd_vers
== NFS_VER3
) {
831 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 0);
834 if (mac_vnode_check_open(ctx
, vp
, FREAD
)) {
839 error
= mac_vnode_check_readlink(ctx
, vp
);
843 error
= VNOP_READLINK(vp
, auio
, ctx
);
846 if (nd
->nd_vers
== NFS_VER3
) {
847 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
848 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
860 nd
->nd_repstat
= error
;
861 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_UNSIGNED
);
863 *mrepp
= nmrep
.nmc_mhead
;
864 nfsmout_on_status(nd
, error
);
865 if (nd
->nd_vers
== NFS_VER3
) {
866 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
868 if (error
|| nd
->nd_repstat
) {
869 nfsm_chain_build_done(error
, &nmrep
);
872 if (auio
&& (uio_resid(auio
) > 0)) {
873 len
-= uio_resid(auio
);
874 tlen
= nfsm_rndup(len
);
875 nfsm_adj(mpath
, NFS_MAXPATHLEN
- tlen
, tlen
- len
);
877 nfsm_chain_add_32(error
, &nmrep
, len
);
878 nfsm_chain_build_done(error
, &nmrep
);
880 error
= mbuf_setnext(nmrep
.nmc_mcur
, mpath
);
891 if (uio_bufp
!= &uio_buf
[0]) {
892 FREE(uio_bufp
, M_TEMP
);
895 nfsm_chain_cleanup(&nmrep
);
906 struct nfsrv_descript
*nd
,
907 struct nfsrv_sock
*slp
,
911 int error
, attrerr
, mreadcnt
;
912 uint32_t reqlen
, maxlen
, count
, len
, tlen
;
915 struct nfs_filehandle nfh
;
916 struct nfs_export
*nx
= NULL
;
917 struct nfs_export_options
*nxo
;
919 char *uio_bufp
= NULL
;
920 struct vnode_attr vattr
, *vap
= &vattr
;
923 char uio_buf
[UIO_SIZEOF(0)];
924 struct nfsm_chain
*nmreq
, nmrep
;
928 nmreq
= &nd
->nd_nmreq
;
929 nfsm_chain_null(&nmrep
);
933 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
935 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
937 if (nd
->nd_vers
== NFS_VER3
) {
938 nfsm_chain_get_64(error
, nmreq
, off
);
940 nfsm_chain_get_32(error
, nmreq
, off
);
942 nfsm_chain_get_32(error
, nmreq
, reqlen
);
943 maxlen
= NFSRV_NDMAXDATA(nd
);
944 if (reqlen
> maxlen
) {
948 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
951 /* update export stats */
952 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
954 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
957 if (vnode_vtype(vp
) != VREG
) {
958 if (nd
->nd_vers
== NFS_VER3
) {
961 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
966 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 1))) {
967 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 1);
972 error
= mac_vnode_check_open(ctx
, vp
, FREAD
);
976 /* XXXab: Do we need to do this?! */
977 error
= mac_vnode_check_read(ctx
, vfs_context_ucred(ctx
), vp
);
981 /* mac_vnode_check_exec() can't be done here. */
986 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
987 attrerr
= vnode_getattr(vp
, vap
, ctx
);
993 if ((u_quad_t
)off
>= vap
->va_data_size
) {
995 } else if (((u_quad_t
)off
+ reqlen
) > vap
->va_data_size
) {
996 count
= (int)nfsm_rndup(vap
->va_data_size
- off
);
1003 /* get mbuf list to hold read data */
1004 error
= nfsm_mbuf_get_list(count
, &mread
, &mreadcnt
);
1006 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mreadcnt
), M_TEMP
, M_WAITOK
);
1008 auio
= uio_createwithbuffer(mreadcnt
, off
, UIO_SYSSPACE
,
1009 UIO_READ
, uio_bufp
, UIO_SIZEOF(mreadcnt
));
1011 if (!uio_bufp
|| !auio
) {
1015 for (m
= mread
; m
; m
= mbuf_next(m
)) {
1016 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mbuf_len(m
));
1018 error
= VNOP_READ(vp
, auio
, IO_NODELOCKED
, ctx
);
1020 auio
= uio_createwithbuffer(0, 0, UIO_SYSSPACE
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
1028 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
1029 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
1030 attrerr
= vnode_getattr(vp
, vap
, ctx
);
1031 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
1032 error
= attrerr
; /* NFSv2 must have attributes to return */
1040 /* trim off any data not actually read */
1041 len
-= uio_resid(auio
);
1042 tlen
= nfsm_rndup(len
);
1043 if (count
!= tlen
|| tlen
!= len
) {
1044 nfsm_adj(mread
, count
- tlen
, tlen
- len
);
1048 /* assemble reply */
1049 nd
->nd_repstat
= error
;
1050 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPORFATTR(nd
->nd_vers
) + 3 * NFSX_UNSIGNED
);
1052 *mrepp
= nmrep
.nmc_mhead
;
1053 nfsmout_on_status(nd
, error
);
1054 if (nd
->nd_vers
== NFS_VER3
) {
1055 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
1057 if (error
|| nd
->nd_repstat
) {
1058 nfsm_chain_build_done(error
, &nmrep
);
1061 if (nd
->nd_vers
== NFS_VER3
) {
1062 nfsm_chain_add_32(error
, &nmrep
, len
);
1063 nfsm_chain_add_32(error
, &nmrep
, (len
< reqlen
) ? TRUE
: FALSE
);
1065 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
1067 nfsm_chain_add_32(error
, &nmrep
, len
);
1068 nfsm_chain_build_done(error
, &nmrep
);
1070 error
= mbuf_setnext(nmrep
.nmc_mcur
, mread
);
1075 /* update export stats */
1076 NFSStatAdd64(&nx
->nx_stats
.bytes_read
, len
);
1078 /* update active user stats */
1079 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, len
, 0);
1087 if (uio_bufp
!= NULL
) {
1088 FREE(uio_bufp
, M_TEMP
);
1091 nfsm_chain_cleanup(&nmrep
);
1099 * NFS File modification reporting
1101 * When the contents of a file are changed, a "content modified"
1102 * fsevent needs to be issued. Normally this would be done at
1103 * file close time. This is difficult for NFS because the protocol
1104 * has no "close" operation. The client sends a stream of write
1105 * requests that just stop. So we keep a hash table full of
1106 * vnodes that have been written to recently, and issue a
1107 * "content modified" fsevent only if there are no writes to
1108 * a vnode for nfsrv_fmod_pendtime milliseconds.
1110 int nfsrv_fmod_pending
; /* count of vnodes being written to */
1111 int nfsrv_fmod_pendtime
= 1000; /* msec to wait */
1112 int nfsrv_fmod_min_interval
= 100; /* msec min interval between callbacks */
1115 * This function is called via the kernel's callout
1116 * mechanism. Calls are made only when there are
1117 * vnodes pending a fsevent creation, and no more
1118 * frequently than every nfsrv_fmod_min_interval ms.
1121 nfsrv_fmod_timer(__unused
void *param0
, __unused
void *param1
)
1123 struct nfsrv_fmod_hashhead
*headp
, firehead
;
1124 struct nfsrv_fmod
*fp
, *nfp
, *pfp
;
1125 uint64_t timenow
, next_deadline
;
1126 time_t interval
= 0;
1129 LIST_INIT(&firehead
);
1130 lck_mtx_lock(&nfsrv_fmod_mutex
);
1132 clock_get_uptime(&timenow
);
1133 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000,
1137 * Scan all the hash chains
1140 for (i
= 0; i
< NFSRVFMODHASHSZ
; i
++) {
1142 * For each hash chain, look for an entry
1143 * that has exceeded the deadline.
1145 headp
= &nfsrv_fmod_hashtbl
[i
];
1146 LIST_FOREACH(fp
, headp
, fm_link
) {
1147 if (timenow
>= fp
->fm_deadline
) {
1150 if (fp
->fm_deadline
< next_deadline
) {
1151 next_deadline
= fp
->fm_deadline
;
1156 * If we have an entry that's exceeded the
1157 * deadline, then the same is true for all
1158 * following entries in the chain, since they're
1159 * sorted in time order.
1163 /* move each entry to the fire list */
1164 nfp
= LIST_NEXT(fp
, fm_link
);
1165 LIST_REMOVE(fp
, fm_link
);
1168 LIST_INSERT_AFTER(pfp
, fp
, fm_link
);
1170 LIST_INSERT_HEAD(&firehead
, fp
, fm_link
);
1178 lck_mtx_unlock(&nfsrv_fmod_mutex
);
1180 * Fire off the content modified fsevent for each
1181 * entry and free it.
1183 LIST_FOREACH_SAFE(fp
, &firehead
, fm_link
, nfp
) {
1184 if (nfsrv_fsevents_enabled
) {
1185 fp
->fm_context
.vc_thread
= current_thread();
1186 add_fsevent(FSE_CONTENT_MODIFIED
, &fp
->fm_context
,
1187 FSE_ARG_VNODE
, fp
->fm_vp
,
1190 vnode_put(fp
->fm_vp
);
1191 kauth_cred_unref(&fp
->fm_context
.vc_ucred
);
1192 LIST_REMOVE(fp
, fm_link
);
1195 lck_mtx_lock(&nfsrv_fmod_mutex
);
1196 nfsrv_fmod_pending
-= fmod_fire
;
1201 * If there are still pending entries, set up another
1202 * callout to handle them later. Set the timeout deadline
1203 * so that the callout happens when the oldest pending
1204 * entry is ready to send its fsevent.
1206 if (nfsrv_fmod_pending
> 0) {
1207 interval
= ((time_t)(next_deadline
- timenow
)) / (1000 * 1000);
1208 if (interval
< nfsrv_fmod_min_interval
) {
1209 interval
= nfsrv_fmod_min_interval
;
1213 nfsrv_fmod_timer_on
= interval
> 0;
1214 if (nfsrv_fmod_timer_on
) {
1215 nfs_interval_timer_start(nfsrv_fmod_timer_call
, interval
);
1218 lck_mtx_unlock(&nfsrv_fmod_mutex
);
1222 * When a vnode has been written to, enter it in the hash
1223 * table of vnodes pending creation of an fsevent. If the
1224 * callout timer isn't already running, schedule a callback
1225 * for nfsrv_fmod_pendtime msec from now.
1228 nfsrv_modified(vnode_t vp
, vfs_context_t ctx
)
1231 struct nfsrv_fmod
*fp
;
1232 struct nfsrv_fmod_hashhead
*head
;
1234 lck_mtx_lock(&nfsrv_fmod_mutex
);
1237 * Compute the time in the future when the
1238 * content modified fsevent is to be issued.
1240 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000, &deadline
);
1243 * Check if there's already a file content change fsevent
1244 * pending for this vnode. If there is, update its
1245 * timestamp and make sure it's at the front of the hash chain.
1247 head
= &nfsrv_fmod_hashtbl
[NFSRVFMODHASH(vp
)];
1248 LIST_FOREACH(fp
, head
, fm_link
) {
1249 if (vp
== fp
->fm_vp
) {
1250 fp
->fm_deadline
= deadline
;
1251 if (fp
!= LIST_FIRST(head
)) {
1252 LIST_REMOVE(fp
, fm_link
);
1253 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1255 lck_mtx_unlock(&nfsrv_fmod_mutex
);
1261 * First content change fsevent for this vnode.
1262 * Allocate a new file mod entry and add it
1263 * on the front of the hash chain.
1265 if (vnode_get(vp
) != 0) {
1268 MALLOC(fp
, struct nfsrv_fmod
*, sizeof(*fp
), M_TEMP
, M_WAITOK
);
1274 kauth_cred_ref(vfs_context_ucred(ctx
));
1275 fp
->fm_context
= *ctx
;
1276 fp
->fm_deadline
= deadline
;
1277 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1280 * If added to an empty hash table, then set the
1281 * callout timer to go off after nfsrv_fmod_pendtime.
1283 nfsrv_fmod_pending
++;
1284 if (!nfsrv_fmod_timer_on
) {
1285 nfsrv_fmod_timer_on
= 1;
1286 nfs_interval_timer_start(nfsrv_fmod_timer_call
,
1287 nfsrv_fmod_pendtime
);
1290 lck_mtx_unlock(&nfsrv_fmod_mutex
);
1293 #endif /* CONFIG_FSE */
1300 struct nfsrv_descript
*nd
,
1301 struct nfsrv_sock
*slp
,
1305 struct vnode_attr preattr
, postattr
;
1306 int error
, preattrerr
, postattrerr
;
1307 int ioflags
, len
, retlen
;
1309 int stable
= NFS_WRITE_FILESYNC
;
1312 struct nfs_filehandle nfh
;
1313 struct nfs_export
*nx
= NULL
;
1314 struct nfs_export_options
*nxo
;
1316 char *uio_bufp
= NULL
;
1319 struct nfsm_chain
*nmreq
, nmrep
;
1321 if (nd
->nd_nmreq
.nmc_mhead
== NULL
) {
1327 preattrerr
= postattrerr
= ENOENT
;
1328 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1329 nmreq
= &nd
->nd_nmreq
;
1330 nfsm_chain_null(&nmrep
);
1334 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
1336 if (nd
->nd_vers
== NFS_VER3
) {
1337 nfsm_chain_get_64(error
, nmreq
, off
);
1338 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1339 nfsm_chain_get_32(error
, nmreq
, stable
);
1341 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1342 nfsm_chain_get_32(error
, nmreq
, off
);
1343 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1345 stable
= NFS_WRITE_UNSTABLE
;
1348 nfsm_chain_get_32(error
, nmreq
, len
);
1353 * For NFS Version 2, it is not obvious what a write of zero length
1354 * should do, but I might as well be consistent with Version 3,
1355 * which is to return ok so long as there are no permission problems.
1359 error
= nfsm_chain_trim_data(nmreq
, len
, &mlen
);
1364 if ((len
> NFSRV_MAXDATA
) || (len
< 0) || (mlen
< len
)) {
1368 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
1371 /* update export stats */
1372 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1374 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1377 if (nd
->nd_vers
== NFS_VER3
) {
1378 nfsm_srv_pre_vattr_init(&preattr
);
1379 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1381 if (vnode_vtype(vp
) != VREG
) {
1382 if (nd
->nd_vers
== NFS_VER3
) {
1385 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1389 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1395 error
= mac_vnode_check_open(ctx
, vp
, FWRITE
);
1399 /* XXXab: Do we need to do this?! */
1400 error
= mac_vnode_check_write(ctx
, vfs_context_ucred(ctx
), vp
);
1410 for (mcount
= 0, m
= nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
)) {
1411 if (mbuf_len(m
) > 0) {
1415 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mcount
), M_TEMP
, M_WAITOK
);
1417 auio
= uio_createwithbuffer(mcount
, off
, UIO_SYSSPACE
, UIO_WRITE
, uio_bufp
, UIO_SIZEOF(mcount
));
1419 if (!uio_bufp
|| !auio
) {
1423 for (m
= nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
)) {
1424 if ((mlen
= (int)mbuf_len(m
)) > 0) {
1425 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mlen
);
1429 * XXX The IO_METASYNC flag indicates that all metadata (and not just
1430 * enough to ensure data integrity) mus be written to stable storage
1431 * synchronously. (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
1433 if (stable
== NFS_WRITE_UNSTABLE
) {
1434 ioflags
= IO_NODELOCKED
;
1435 } else if (stable
== NFS_WRITE_DATASYNC
) {
1436 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1438 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1441 error
= VNOP_WRITE(vp
, auio
, ioflags
, ctx
);
1442 OSAddAtomic64(1, &nfsstats
.srvvop_writes
);
1444 /* update export stats */
1445 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, len
);
1447 /* update active user stats */
1448 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, len
);
1451 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
)) {
1452 nfsrv_modified(vp
, ctx
);
1456 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1457 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1458 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
1459 error
= postattrerr
; /* NFSv2 must have attributes to return */
1465 /* assemble reply */
1466 nd
->nd_repstat
= error
;
1467 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1468 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1469 NFSX_WRITEVERF(nd
->nd_vers
));
1471 *mrepp
= nmrep
.nmc_mhead
;
1472 nfsmout_on_status(nd
, error
);
1473 if (nd
->nd_vers
== NFS_VER3
) {
1474 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1475 preattrerr
, &preattr
, postattrerr
, &postattr
);
1476 nfsmout_if(error
|| nd
->nd_repstat
);
1477 nfsm_chain_add_32(error
, &nmrep
, retlen
);
1478 /* If nfsrv_async is set, then pretend the write was FILESYNC. */
1479 if ((stable
== NFS_WRITE_UNSTABLE
) && !nfsrv_async
) {
1480 nfsm_chain_add_32(error
, &nmrep
, stable
);
1482 nfsm_chain_add_32(error
, &nmrep
, NFS_WRITE_FILESYNC
);
1484 /* write verifier */
1485 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1486 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1488 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1491 nfsm_chain_build_done(error
, &nmrep
);
1495 if (uio_bufp
!= NULL
) {
1496 FREE(uio_bufp
, M_TEMP
);
1499 nfsm_chain_cleanup(&nmrep
);
1506 * NFS write service with write gathering support. Called when
1507 * nfsrv_wg_delay > 0.
1508 * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
1509 * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
1513 #define NWDELAYHASH(sock, f) \
1514 (&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ])
1515 /* These macros compare nfsrv_descript structures. */
1516 #define NFSW_CONTIG(o, n) \
1517 (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh))
1519 * XXX The following is an incorrect comparison; it fails to take into account
1520 * XXX scoping of MAC labels, but we currently lack KPI for credential
1523 #define NFSW_SAMECRED(o, n) \
1524 (!bcmp((caddr_t)(o)->nd_cr, (caddr_t)(n)->nd_cr, \
1525 sizeof (struct ucred)))
1529 struct nfsrv_descript
**ndp
,
1530 struct nfsrv_sock
*slp
,
1534 struct nfsrv_descript
*nd
, *wp
, *owp
, *swp
;
1535 struct nfs_export
*nx
;
1536 struct nfs_export_options
*nxo
;
1537 struct nfsrv_wg_delayhash
*wpp
;
1539 struct vnode_attr preattr
, postattr
;
1540 int error
, mlen
, i
, ioflags
;
1542 int preattrerr
, postattrerr
;
1546 char *uio_bufp
= NULL
;
1549 struct nfsm_chain
*nmreq
, nmrep
;
1552 preattrerr
= postattrerr
= ENOENT
;
1553 nfsm_chain_null(&nmrep
);
1560 nmreq
= &nd
->nd_nmreq
;
1561 LIST_INIT(&nd
->nd_coalesce
);
1563 nd
->nd_stable
= NFS_WRITE_FILESYNC
;
1565 cur_usec
= now
.tv_sec
* 1000000 + now
.tv_usec
;
1566 nd
->nd_time
= cur_usec
+
1567 ((nd
->nd_vers
== NFS_VER3
) ? nfsrv_wg_delay_v3
: nfsrv_wg_delay
);
1569 /* Now, get the write header... */
1570 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nd
->nd_fh
.nfh_fhp
, nd
->nd_fh
.nfh_len
);
1571 /* XXX shouldn't we be checking for invalid FHs before doing any more work? */
1573 if (nd
->nd_vers
== NFS_VER3
) {
1574 nfsm_chain_get_64(error
, nmreq
, nd
->nd_off
);
1575 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1576 nfsm_chain_get_32(error
, nmreq
, nd
->nd_stable
);
1578 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1579 nfsm_chain_get_32(error
, nmreq
, nd
->nd_off
);
1580 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1582 nd
->nd_stable
= NFS_WRITE_UNSTABLE
;
1585 nfsm_chain_get_32(error
, nmreq
, nd
->nd_len
);
1587 nd
->nd_eoff
= nd
->nd_off
+ nd
->nd_len
;
1589 if (nd
->nd_len
> 0) {
1590 error
= nfsm_chain_trim_data(nmreq
, nd
->nd_len
, &mlen
);
1596 if ((nd
->nd_len
> NFSRV_MAXDATA
) || (nd
->nd_len
< 0) || (mlen
< nd
->nd_len
)) {
1599 nd
->nd_repstat
= error
;
1600 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1602 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1603 if (nd
->nd_vers
== NFS_VER3
) {
1604 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1605 preattrerr
, &preattr
, postattrerr
, &postattr
);
1608 nfsm_chain_build_done(error
, &nmrep
);
1613 * Add this entry to the hash and time queues.
1615 lck_mtx_lock(&slp
->ns_wgmutex
);
1617 wp
= slp
->ns_tq
.lh_first
;
1618 while (wp
&& wp
->nd_time
< nd
->nd_time
) {
1620 wp
= wp
->nd_tq
.le_next
;
1623 LIST_INSERT_AFTER(owp
, nd
, nd_tq
);
1625 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1628 wpp
= NWDELAYHASH(slp
, nd
->nd_fh
.nfh_fid
);
1631 while (wp
&& !nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1633 wp
= wp
->nd_hash
.le_next
;
1635 while (wp
&& (wp
->nd_off
< nd
->nd_off
) &&
1636 nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1638 wp
= wp
->nd_hash
.le_next
;
1641 LIST_INSERT_AFTER(owp
, nd
, nd_hash
);
1643 * Search the hash list for overlapping entries and
1646 for (; nd
&& NFSW_CONTIG(owp
, nd
); nd
= wp
) {
1647 wp
= nd
->nd_hash
.le_next
;
1648 if (NFSW_SAMECRED(owp
, nd
)) {
1649 nfsrv_wg_coalesce(owp
, nd
);
1653 LIST_INSERT_HEAD(wpp
, nd
, nd_hash
);
1657 lck_mtx_lock(&slp
->ns_wgmutex
);
1661 * Now, do VNOP_WRITE()s for any one(s) that need to be done now
1662 * and generate the associated reply mbuf list(s).
1666 cur_usec
= now
.tv_sec
* 1000000 + now
.tv_usec
;
1667 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= owp
) {
1668 owp
= nd
->nd_tq
.le_next
;
1669 if (nd
->nd_time
> cur_usec
) {
1675 LIST_REMOVE(nd
, nd_tq
);
1676 LIST_REMOVE(nd
, nd_hash
);
1677 nmreq
= &nd
->nd_nmreq
;
1678 preattrerr
= postattrerr
= ENOENT
;
1680 /* save the incoming uid before mapping, */
1681 /* for updating active user stats later */
1682 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1684 error
= nfsrv_fhtovp(&nd
->nd_fh
, nd
, &vp
, &nx
, &nxo
);
1686 /* update per-export stats */
1687 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1689 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1695 if (nd
->nd_vers
== NFS_VER3
) {
1696 nfsm_srv_pre_vattr_init(&preattr
);
1697 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1699 if (vnode_vtype(vp
) != VREG
) {
1700 if (nd
->nd_vers
== NFS_VER3
) {
1703 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1710 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1713 if (nd
->nd_stable
== NFS_WRITE_UNSTABLE
) {
1714 ioflags
= IO_NODELOCKED
;
1715 } else if (nd
->nd_stable
== NFS_WRITE_DATASYNC
) {
1716 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1718 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1721 if (!error
&& ((nd
->nd_eoff
- nd
->nd_off
) > 0)) {
1722 for (i
= 0, m
= nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
)) {
1723 if (mbuf_len(m
) > 0) {
1728 MALLOC(uio_bufp
, char *, UIO_SIZEOF(i
), M_TEMP
, M_WAITOK
);
1730 auio
= uio_createwithbuffer(i
, nd
->nd_off
, UIO_SYSSPACE
,
1731 UIO_WRITE
, uio_bufp
, UIO_SIZEOF(i
));
1733 if (!uio_bufp
|| !auio
) {
1737 for (m
= nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
)) {
1738 if ((tlen
= mbuf_len(m
)) > 0) {
1739 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), tlen
);
1742 error
= VNOP_WRITE(vp
, auio
, ioflags
, ctx
);
1743 OSAddAtomic64(1, &nfsstats
.srvvop_writes
);
1745 /* update export stats */
1746 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, nd
->nd_len
);
1747 /* update active user stats */
1748 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, nd
->nd_len
);
1751 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
)) {
1752 nfsrv_modified(vp
, ctx
);
1757 FREE(uio_bufp
, M_TEMP
);
1762 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1763 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1768 * Loop around generating replies for all write rpcs that have
1769 * now been completed.
1774 nd
->nd_repstat
= error
;
1775 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1776 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1777 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1778 preattrerr
, &preattr
, postattrerr
, &postattr
);
1781 nd
->nd_repstat
= error
;
1782 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1783 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1784 NFSX_WRITEVERF(nd
->nd_vers
));
1785 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1786 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1787 preattrerr
, &preattr
, postattrerr
, &postattr
);
1788 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_len
);
1789 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_stable
);
1790 /* write verifier */
1791 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1792 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1793 } else if (!error
) {
1794 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1797 nfsm_chain_build_done(error
, &nmrep
);
1799 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1802 * Done. Put it at the head of the timer queue so that
1803 * the final phase can return the reply.
1807 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1809 nd
= swp
->nd_coalesce
.lh_first
;
1811 LIST_REMOVE(nd
, nd_tq
);
1815 LIST_INSERT_HEAD(&slp
->ns_tq
, swp
, nd_tq
);
1820 * Search for a reply to return.
1822 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= nd
->nd_tq
.le_next
) {
1824 LIST_REMOVE(nd
, nd_tq
);
1825 *mrepp
= nd
->nd_mrep
;
1830 slp
->ns_wgtime
= slp
->ns_tq
.lh_first
? slp
->ns_tq
.lh_first
->nd_time
: 0;
1831 lck_mtx_unlock(&slp
->ns_wgmutex
);
1834 * If we've just created a write pending gather,
1835 * start the timer to check on it soon to make sure
1836 * the write will be completed.
1838 * Add/Remove the socket in the nfsrv_sockwg queue as needed.
1840 lck_mtx_lock(&nfsd_mutex
);
1841 if (slp
->ns_wgtime
) {
1842 if (slp
->ns_wgq
.tqe_next
== SLPNOLIST
) {
1843 TAILQ_INSERT_HEAD(&nfsrv_sockwg
, slp
, ns_wgq
);
1845 if (!nfsrv_wg_timer_on
) {
1846 nfsrv_wg_timer_on
= 1;
1847 nfs_interval_timer_start(nfsrv_wg_timer_call
,
1848 NFSRV_WGATHERDELAY
);
1850 } else if (slp
->ns_wgq
.tqe_next
!= SLPNOLIST
) {
1851 TAILQ_REMOVE(&nfsrv_sockwg
, slp
, ns_wgq
);
1852 slp
->ns_wgq
.tqe_next
= SLPNOLIST
;
1854 lck_mtx_unlock(&nfsd_mutex
);
1860 * Coalesce the write request nd into owp. To do this we must:
1861 * - remove nd from the queues
1862 * - merge nd->nd_nmreq into owp->nd_nmreq
1863 * - update the nd_eoff and nd_stable for owp
1864 * - put nd on owp's nd_coalesce list
1867 nfsrv_wg_coalesce(struct nfsrv_descript
*owp
, struct nfsrv_descript
*nd
)
1872 struct nfsrv_descript
*p
;
1874 LIST_REMOVE(nd
, nd_hash
);
1875 LIST_REMOVE(nd
, nd_tq
);
1876 if (owp
->nd_eoff
< nd
->nd_eoff
) {
1877 overlap
= owp
->nd_eoff
- nd
->nd_off
;
1882 mbuf_adj(nd
->nd_nmreq
.nmc_mhead
, (int)overlap
);
1884 mp
= owp
->nd_nmreq
.nmc_mhead
;
1885 while ((mpnext
= mbuf_next(mp
))) {
1888 error
= mbuf_setnext(mp
, nd
->nd_nmreq
.nmc_mhead
);
1892 owp
->nd_eoff
= nd
->nd_eoff
;
1894 mbuf_freem(nd
->nd_nmreq
.nmc_mhead
);
1896 nd
->nd_nmreq
.nmc_mhead
= NULL
;
1897 nd
->nd_nmreq
.nmc_mcur
= NULL
;
1898 if (nd
->nd_stable
== NFS_WRITE_FILESYNC
) {
1899 owp
->nd_stable
= NFS_WRITE_FILESYNC
;
1900 } else if ((nd
->nd_stable
== NFS_WRITE_DATASYNC
) &&
1901 (owp
->nd_stable
== NFS_WRITE_UNSTABLE
)) {
1902 owp
->nd_stable
= NFS_WRITE_DATASYNC
;
1904 LIST_INSERT_HEAD(&owp
->nd_coalesce
, nd
, nd_tq
);
1907 * If nd had anything else coalesced into it, transfer them
1908 * to owp, otherwise their replies will never get sent.
1910 while ((p
= nd
->nd_coalesce
.lh_first
)) {
1911 LIST_REMOVE(p
, nd_tq
);
1912 LIST_INSERT_HEAD(&owp
->nd_coalesce
, p
, nd_tq
);
1918 * Scan the write gathering queues for writes that need to be
1922 nfsrv_wg_timer(__unused
void *param0
, __unused
void *param1
)
1925 time_t cur_usec
, next_usec
;
1927 struct nfsrv_sock
*slp
;
1928 int writes_pending
= 0;
1931 cur_usec
= now
.tv_sec
* 1000000 + now
.tv_usec
;
1932 next_usec
= cur_usec
+ (NFSRV_WGATHERDELAY
* 1000);
1934 lck_mtx_lock(&nfsd_mutex
);
1935 TAILQ_FOREACH(slp
, &nfsrv_sockwg
, ns_wgq
) {
1936 if (slp
->ns_wgtime
) {
1938 if (slp
->ns_wgtime
<= cur_usec
) {
1939 lck_rw_lock_exclusive(&slp
->ns_rwlock
);
1940 slp
->ns_flag
|= SLP_DOWRITES
;
1941 lck_rw_done(&slp
->ns_rwlock
);
1942 nfsrv_wakenfsd(slp
);
1945 if (slp
->ns_wgtime
< next_usec
) {
1946 next_usec
= slp
->ns_wgtime
;
1951 if (writes_pending
== 0) {
1952 nfsrv_wg_timer_on
= 0;
1953 lck_mtx_unlock(&nfsd_mutex
);
1956 lck_mtx_unlock(&nfsd_mutex
);
1959 * Return the number of msec to wait again
1961 interval
= (next_usec
- cur_usec
) / 1000;
1965 nfs_interval_timer_start(nfsrv_wg_timer_call
, interval
);
1969 * Sort the group list in increasing numerical order.
1970 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1971 * that used to be here.)
1974 nfsrv_group_sort(gid_t
*list
, int num
)
1979 /* Insertion sort. */
1980 for (i
= 1; i
< num
; i
++) {
1982 /* find correct slot for value v, moving others up */
1983 for (j
= i
; --j
>= 0 && v
< list
[j
];) {
1984 list
[j
+ 1] = list
[j
];
1991 * nfs create service
1992 * now does a truncate to 0 length via. setattr if it already exists
1996 struct nfsrv_descript
*nd
,
1997 struct nfsrv_sock
*slp
,
2001 struct vnode_attr dpreattr
, dpostattr
, postattr
;
2002 struct vnode_attr va
, *vap
= &va
;
2003 struct nameidata ni
;
2004 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
2005 int how
, exclusive_flag
;
2006 uint32_t len
= 0, cnflags
;
2008 vnode_t vp
, dvp
, dirp
;
2009 struct nfs_filehandle nfh
;
2010 struct nfs_export
*nx
= NULL
;
2011 struct nfs_export_options
*nxo
= NULL
;
2013 u_char cverf
[NFSX_V3CREATEVERF
];
2015 struct nfsm_chain
*nmreq
, nmrep
;
2018 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
2019 nmreq
= &nd
->nd_nmreq
;
2020 nfsm_chain_null(&nmrep
);
2021 vp
= dvp
= dirp
= NULL
;
2023 ni
.ni_cnd
.cn_nameiop
= 0;
2026 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2028 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2029 nfsm_chain_get_32(error
, nmreq
, len
);
2030 nfsm_name_len_check(error
, nd
, len
);
2033 ni
.ni_cnd
.cn_nameiop
= CREATE
;
2037 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2038 ni
.ni_cnd
.cn_ndp
= &ni
;
2040 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2042 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2044 /* update export stats */
2045 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2047 /* update active user stats */
2048 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2052 if (nd
->nd_vers
== NFS_VER3
) {
2053 nfsm_srv_pre_vattr_init(&dpreattr
);
2054 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2062 ni
.ni_cnd
.cn_nameiop
= 0;
2070 if (nd
->nd_vers
== NFS_VER3
) {
2071 nfsm_chain_get_32(error
, nmreq
, how
);
2074 case NFS_CREATE_GUARDED
:
2080 case NFS_CREATE_UNCHECKED
:
2081 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2083 case NFS_CREATE_EXCLUSIVE
:
2084 nfsm_chain_get_opaque(error
, nmreq
, NFSX_V3CREATEVERF
, cverf
);
2087 VATTR_SET(vap
, va_mode
, 0);
2092 VATTR_SET(vap
, va_type
, VREG
);
2096 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2098 v_type
= vap
->va_type
;
2099 if (v_type
== VNON
) {
2102 VATTR_SET(vap
, va_type
, v_type
);
2108 rdev
= vap
->va_data_size
;
2109 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2119 * If it doesn't exist, create it
2120 * otherwise just truncate to 0 length
2121 * should I set the mode too ??
2124 kauth_acl_t xacl
= NULL
;
2126 /* authorize before creating */
2127 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
2129 /* construct ACL and handle inheritance */
2131 error
= kauth_acl_inherit(dvp
,
2137 if (!error
&& xacl
!= NULL
) {
2138 VATTR_SET(vap
, va_acl
, xacl
);
2141 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2142 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
2144 * Server policy is to alway use the mapped rpc credential for
2145 * file system object creation. This has the nice side effect of
2146 * enforcing BSD creation semantics
2148 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2149 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2151 /* validate new-file security information */
2153 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2157 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
2163 if (vap
->va_type
== VREG
|| vap
->va_type
== VSOCK
) {
2165 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2168 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
2170 * If some of the requested attributes weren't handled by the VNOP,
2171 * use our fallback code.
2173 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
2177 kauth_acl_free(xacl
);
2181 if (exclusive_flag
) {
2184 bcopy(cverf
, (caddr_t
)&vap
->va_access_time
,
2186 VATTR_SET_ACTIVE(vap
, va_access_time
);
2187 // skip authorization, as this is an
2188 // NFS internal implementation detail.
2189 error
= vnode_setattr(vp
, vap
, ctx
);
2193 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_CREATE_FILE
, vp
)) {
2194 add_fsevent(FSE_CREATE_FILE
, ctx
,
2200 } else if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
||
2201 vap
->va_type
== VFIFO
) {
2202 if (vap
->va_type
== VCHR
&& rdev
== 0xffffffff) {
2203 VATTR_SET(vap
, va_type
, VFIFO
);
2205 if (vap
->va_type
!= VFIFO
) {
2206 error
= suser(nd
->nd_cr
, NULL
);
2209 VATTR_SET(vap
, va_rdev
, (dev_t
)rdev
);
2211 error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2214 kauth_acl_free(xacl
);
2224 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2226 ni
.ni_op
= OP_LOOKUP
;
2228 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2229 ni
.ni_cnd
.cn_context
= ctx
;
2230 ni
.ni_startdir
= dvp
;
2232 ni
.ni_rootdir
= rootvnode
;
2233 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
2234 while ((error
= lookup(&ni
)) == ERECYCLE
) {
2235 ni
.ni_cnd
.cn_flags
= cnflags
;
2236 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
2237 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
2240 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
) {
2250 * nameidone has to happen before we vnode_put(dvp)
2251 * since it may need to release the fs_nodelock on the dvp
2254 ni
.ni_cnd
.cn_nameiop
= 0;
2259 * nameidone has to happen before we vnode_put(dvp)
2260 * since it may need to release the fs_nodelock on the dvp
2263 ni
.ni_cnd
.cn_nameiop
= 0;
2268 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2269 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
2270 error
= mac_vnode_check_truncate(ctx
, NOCRED
, vp
);
2276 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2277 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
,
2280 tempsize
= vap
->va_data_size
;
2282 VATTR_SET(vap
, va_data_size
, tempsize
);
2283 error
= vnode_setattr(vp
, vap
, ctx
);
2288 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
2290 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
2291 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2292 if (nd
->nd_vers
== NFS_VER2
) {
2293 error
= postattrerr
;
2301 if (nd
->nd_vers
== NFS_VER3
) {
2302 if (exclusive_flag
&& !error
&&
2303 bcmp(cverf
, &postattr
.va_access_time
, NFSX_V3CREATEVERF
)) {
2306 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2307 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2313 /* assemble reply */
2314 nd
->nd_repstat
= error
;
2315 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
2316 NFSX_FATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
2318 *mrepp
= nmrep
.nmc_mhead
;
2319 nfsmout_on_status(nd
, error
);
2320 if (nd
->nd_vers
== NFS_VER3
) {
2321 if (!nd
->nd_repstat
) {
2322 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2323 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2325 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2326 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2328 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2330 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
2334 nfsm_chain_build_done(error
, &nmrep
);
2335 if (ni
.ni_cnd
.cn_nameiop
) {
2337 * nameidone has to happen before we vnode_put(dvp)
2338 * since it may need to release the fs_nodelock on the dvp
2351 nfsm_chain_cleanup(&nmrep
);
2358 * nfs v3 mknod service
2362 struct nfsrv_descript
*nd
,
2363 struct nfsrv_sock
*slp
,
2367 struct vnode_attr dpreattr
, dpostattr
, postattr
;
2368 struct vnode_attr va
, *vap
= &va
;
2369 struct nameidata ni
;
2370 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
2371 uint32_t len
= 0, cnflags
;
2372 u_int32_t major
= 0, minor
= 0;
2375 vnode_t vp
, dvp
, dirp
;
2376 struct nfs_filehandle nfh
;
2377 struct nfs_export
*nx
= NULL
;
2378 struct nfs_export_options
*nxo
= NULL
;
2380 kauth_acl_t xacl
= NULL
;
2381 struct nfsm_chain
*nmreq
, nmrep
;
2384 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
2385 nmreq
= &nd
->nd_nmreq
;
2386 nfsm_chain_null(&nmrep
);
2387 vp
= dvp
= dirp
= NULL
;
2388 ni
.ni_cnd
.cn_nameiop
= 0;
2390 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2392 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2393 nfsm_chain_get_32(error
, nmreq
, len
);
2394 nfsm_name_len_check(error
, nd
, len
);
2397 ni
.ni_cnd
.cn_nameiop
= CREATE
;
2401 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2402 ni
.ni_cnd
.cn_ndp
= &ni
;
2403 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2405 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2407 /* update export stats */
2408 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2410 /* update active user stats */
2411 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2415 nfsm_srv_pre_vattr_init(&dpreattr
);
2416 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2419 ni
.ni_cnd
.cn_nameiop
= 0;
2426 nfsm_chain_get_32(error
, nmreq
, nvtype
);
2428 vtyp
= nfstov_type(nvtype
, NFS_VER3
);
2429 if (!error
&& (vtyp
!= VCHR
) && (vtyp
!= VBLK
) && (vtyp
!= VSOCK
) && (vtyp
!= VFIFO
)) {
2430 error
= NFSERR_BADTYPE
;
2435 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2436 if ((vtyp
== VCHR
) || (vtyp
== VBLK
)) {
2437 nfsm_chain_get_32(error
, nmreq
, major
);
2438 nfsm_chain_get_32(error
, nmreq
, minor
);
2440 VATTR_SET(vap
, va_rdev
, makedev(major
, minor
));
2445 * If it doesn't exist, create it.
2451 VATTR_SET(vap
, va_type
, vtyp
);
2453 /* authorize before creating */
2454 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
2456 /* construct ACL and handle inheritance */
2458 error
= kauth_acl_inherit(dvp
,
2464 if (!error
&& xacl
!= NULL
) {
2465 VATTR_SET(vap
, va_acl
, xacl
);
2468 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2469 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
2471 * Server policy is to alway use the mapped rpc credential for
2472 * file system object creation. This has the nice side effect of
2473 * enforcing BSD creation semantics
2475 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2476 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2478 /* validate new-file security information */
2480 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2483 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
2492 if (vtyp
== VSOCK
) {
2493 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2495 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
2497 * If some of the requested attributes weren't handled by the VNOP,
2498 * use our fallback code.
2500 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
2503 if (vtyp
!= VFIFO
&& (error
= suser(nd
->nd_cr
, (u_short
*)0))) {
2506 if ((error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
))) {
2514 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2516 ni
.ni_op
= OP_LOOKUP
;
2518 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2519 ni
.ni_cnd
.cn_context
= vfs_context_current();
2520 ni
.ni_startdir
= dvp
;
2522 ni
.ni_rootdir
= rootvnode
;
2523 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
2524 while ((error
= lookup(&ni
)) == ERECYCLE
) {
2525 ni
.ni_cnd
.cn_flags
= cnflags
;
2526 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
2527 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
2531 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
) {
2538 kauth_acl_free(xacl
);
2542 * nameidone has to happen before we vnode_put(dvp)
2543 * since it may need to release the fs_nodelock on the dvp
2546 ni
.ni_cnd
.cn_nameiop
= 0;
2552 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
2554 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
2555 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2563 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2564 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2569 /* assemble reply */
2570 nd
->nd_repstat
= error
;
2571 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(NFS_VER3
, &nfh
) +
2572 NFSX_POSTOPATTR(NFS_VER3
) + NFSX_WCCDATA(NFS_VER3
));
2574 *mrepp
= nmrep
.nmc_mhead
;
2575 nfsmout_on_status(nd
, error
);
2576 if (!nd
->nd_repstat
) {
2577 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2578 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2580 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2581 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2583 nfsm_chain_build_done(error
, &nmrep
);
2584 if (ni
.ni_cnd
.cn_nameiop
) {
2586 * nameidone has to happen before we vnode_put(dvp)
2587 * since it may need to release the fs_nodelock on the dvp
2606 nfsm_chain_cleanup(&nmrep
);
2613 * nfs remove service
2617 struct nfsrv_descript
*nd
,
2618 struct nfsrv_sock
*slp
,
2622 struct nameidata ni
;
2623 int error
, dpreattrerr
, dpostattrerr
;
2626 vnode_t vp
, dvp
, dirp
= NULL
;
2627 struct vnode_attr dpreattr
, dpostattr
;
2628 struct nfs_filehandle nfh
;
2629 struct nfs_export
*nx
= NULL
;
2630 struct nfs_export_options
*nxo
= NULL
;
2631 struct nfsm_chain
*nmreq
, nmrep
;
2634 dpreattrerr
= dpostattrerr
= ENOENT
;
2635 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2636 dvp
= vp
= dirp
= NULL
;
2637 nmreq
= &nd
->nd_nmreq
;
2638 nfsm_chain_null(&nmrep
);
2640 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2641 nfsm_chain_get_32(error
, nmreq
, len
);
2642 nfsm_name_len_check(error
, nd
, len
);
2645 ni
.ni_cnd
.cn_nameiop
= DELETE
;
2647 ni
.ni_op
= OP_UNLINK
;
2649 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2650 ni
.ni_cnd
.cn_ndp
= &ni
;
2651 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2653 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2655 /* update export stats */
2656 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2658 /* update active user stats */
2659 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2663 if (nd
->nd_vers
== NFS_VER3
) {
2664 nfsm_srv_pre_vattr_init(&dpreattr
);
2665 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2676 if (vnode_vtype(vp
) == VDIR
) {
2677 error
= EPERM
; /* POSIX */
2678 } else if (vnode_isvroot(vp
)) {
2680 * The root of a mounted filesystem cannot be deleted.
2684 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
2688 error
= vn_authorize_unlink(dvp
, vp
, &ni
.ni_cnd
, ctx
, NULL
);
2700 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
2702 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
2703 get_fse_info(vp
, &finfo
, ctx
);
2705 release_pathbuff(path
);
2710 error
= VNOP_REMOVE(dvp
, vp
, &ni
.ni_cnd
, 0, ctx
);
2715 add_fsevent(FSE_DELETE
, ctx
,
2716 FSE_ARG_STRING
, plen
, path
,
2717 FSE_ARG_FINFO
, &finfo
,
2720 release_pathbuff(path
);
2726 * nameidone has to happen before we vnode_put(dvp)
2727 * since it may need to release the fs_nodelock on the dvp
2737 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
2738 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2742 /* assemble reply */
2743 nd
->nd_repstat
= error
;
2744 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
2746 *mrepp
= nmrep
.nmc_mhead
;
2747 nfsmout_on_status(nd
, error
);
2748 if (nd
->nd_vers
== NFS_VER3
) {
2749 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2750 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2753 nfsm_chain_build_done(error
, &nmrep
);
2755 nfsm_chain_cleanup(&nmrep
);
2762 * nfs rename service
2766 struct nfsrv_descript
*nd
,
2767 struct nfsrv_sock
*slp
,
2771 kauth_cred_t saved_cred
= NULL
;
2774 uint32_t fromlen
, tolen
;
2775 int fdpreattrerr
, fdpostattrerr
;
2776 int tdpreattrerr
, tdpostattrerr
;
2777 char *frompath
= NULL
, *topath
= NULL
;
2778 struct nameidata fromni
, toni
;
2779 vnode_t fvp
, tvp
, tdvp
, fdvp
, fdirp
, tdirp
;
2780 struct vnode_attr fdpreattr
, fdpostattr
;
2781 struct vnode_attr tdpreattr
, tdpostattr
;
2782 struct nfs_filehandle fnfh
, tnfh
;
2783 struct nfs_export
*fnx
, *tnx
;
2784 struct nfs_export_options
*fnxo
, *tnxo
;
2785 enum vtype fvtype
, tvtype
;
2786 int holding_mntlock
;
2788 struct nfsm_chain
*nmreq
, nmrep
;
2789 char *from_name
, *to_name
;
2791 int from_len
= 0, to_len
= 0;
2792 fse_info from_finfo
, to_finfo
;
2794 u_char didstats
= 0;
2798 fdpreattrerr
= fdpostattrerr
= ENOENT
;
2799 tdpreattrerr
= tdpostattrerr
= ENOENT
;
2800 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2801 fromlen
= tolen
= 0;
2802 frompath
= topath
= NULL
;
2803 fdirp
= tdirp
= NULL
;
2804 nmreq
= &nd
->nd_nmreq
;
2805 nfsm_chain_null(&nmrep
);
2808 * these need to be set before calling any code
2809 * that they may take us out through the error path.
2811 holding_mntlock
= 0;
2816 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, fnfh
.nfh_fhp
, fnfh
.nfh_len
);
2817 nfsm_chain_get_32(error
, nmreq
, fromlen
);
2818 nfsm_name_len_check(error
, nd
, fromlen
);
2820 error
= nfsm_chain_get_path_namei(nmreq
, fromlen
, &fromni
);
2822 frompath
= fromni
.ni_cnd
.cn_pnbuf
;
2824 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, tnfh
.nfh_fhp
, tnfh
.nfh_len
);
2825 nfsm_chain_get_32(error
, nmreq
, tolen
);
2826 nfsm_name_len_check(error
, nd
, tolen
);
2828 error
= nfsm_chain_get_path_namei(nmreq
, tolen
, &toni
);
2830 topath
= toni
.ni_cnd
.cn_pnbuf
;
2833 * Remember our original uid so that we can reset cr_uid before
2834 * the second nfsrv_namei() call, in case it is remapped.
2836 saved_cred
= nd
->nd_cr
;
2837 kauth_cred_ref(saved_cred
);
2839 fromni
.ni_cnd
.cn_nameiop
= DELETE
;
2841 fromni
.ni_op
= OP_UNLINK
;
2843 fromni
.ni_cnd
.cn_flags
= WANTPARENT
;
2845 fromni
.ni_cnd
.cn_pnbuf
= frompath
;
2847 fromni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2848 fromni
.ni_cnd
.cn_flags
|= HASBUF
;
2849 fromni
.ni_cnd
.cn_ndp
= &fromni
;
2851 error
= nfsrv_namei(nd
, ctx
, &fromni
, &fnfh
, &fdirp
, &fnx
, &fnxo
);
2855 fdvp
= fromni
.ni_dvp
;
2859 if (nd
->nd_vers
== NFS_VER3
) {
2860 nfsm_srv_pre_vattr_init(&fdpreattr
);
2861 fdpreattrerr
= vnode_getattr(fdirp
, &fdpreattr
, ctx
);
2867 fvtype
= vnode_vtype(fvp
);
2869 /* reset credential if it was remapped */
2870 if (nd
->nd_cr
!= saved_cred
) {
2871 kauth_cred_ref(saved_cred
);
2872 kauth_cred_unref(&nd
->nd_cr
);
2873 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
2876 toni
.ni_cnd
.cn_nameiop
= RENAME
;
2878 toni
.ni_op
= OP_RENAME
;
2880 toni
.ni_cnd
.cn_flags
= WANTPARENT
;
2882 toni
.ni_cnd
.cn_pnbuf
= topath
;
2884 toni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2885 toni
.ni_cnd
.cn_flags
|= HASBUF
;
2886 toni
.ni_cnd
.cn_ndp
= &toni
;
2888 if (fvtype
== VDIR
) {
2889 toni
.ni_cnd
.cn_flags
|= WILLBEDIR
;
2893 error
= nfsrv_namei(nd
, ctx
, &toni
, &tnfh
, &tdirp
, &tnx
, &tnxo
);
2896 * Translate error code for rename("dir1", "dir2/.").
2898 if (error
== EISDIR
&& fvtype
== VDIR
) {
2899 if (nd
->nd_vers
== NFS_VER3
) {
2911 /* update export stats once only */
2913 /* update export stats */
2914 NFSStatAdd64(&tnx
->nx_stats
.ops
, 1);
2916 /* update active user stats */
2917 nfsrv_update_user_stat(tnx
, nd
, saved_uid
, 1, 0, 0);
2923 if (nd
->nd_vers
== NFS_VER3
) {
2924 nfsm_srv_pre_vattr_init(&tdpreattr
);
2925 tdpreattrerr
= vnode_getattr(tdirp
, &tdpreattr
, ctx
);
2933 tvtype
= vnode_vtype(tvp
);
2935 if (fvtype
== VDIR
&& tvtype
!= VDIR
) {
2936 if (nd
->nd_vers
== NFS_VER3
) {
2942 } else if (fvtype
!= VDIR
&& tvtype
== VDIR
) {
2943 if (nd
->nd_vers
== NFS_VER3
) {
2950 if (tvtype
== VDIR
&& vnode_mountedhere(tvp
)) {
2951 if (nd
->nd_vers
== NFS_VER3
) {
2960 if (nd
->nd_vers
== NFS_VER3
) {
2971 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2972 * the node is moving between directories and we need rights to remove from the
2973 * old and add to the new.
2975 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2977 * Note that we do not inherit when renaming. XXX this needs to be revisited to
2978 * implement the deferred-inherit bit.
2984 if ((tvp
!= NULL
) && vnode_isdir(tvp
)) {
2988 } else if (tdvp
!= fdvp
) {
2992 /* moving out of fdvp, must have delete rights */
2993 if ((error
= nfsrv_authorize(fvp
, fdvp
, KAUTH_VNODE_DELETE
, ctx
, fnxo
, 0)) != 0) {
2996 /* moving into tdvp or tvp, must have rights to add */
2997 if ((error
= nfsrv_authorize(((tvp
!= NULL
) && vnode_isdir(tvp
)) ? tvp
: tdvp
,
2999 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
3000 ctx
, tnxo
, 0)) != 0) {
3004 /* node staying in same directory, must be allowed to add new name */
3005 if ((error
= nfsrv_authorize(fdvp
, NULL
,
3006 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
3007 ctx
, fnxo
, 0)) != 0) {
3011 /* overwriting tvp */
3012 if ((tvp
!= NULL
) && !vnode_isdir(tvp
) &&
3013 ((error
= nfsrv_authorize(tvp
, tdvp
, KAUTH_VNODE_DELETE
, ctx
, tnxo
, 0)) != 0)) {
3018 ((error
= vn_authorize_rename(fdvp
, fvp
, &fromni
.ni_cnd
, tdvp
, tvp
, &toni
.ni_cnd
, ctx
, NULL
)) != 0)) {
3024 /* XXX more checks? */
3027 /* authorization denied */
3033 if ((vnode_mount(fvp
) != vnode_mount(tdvp
)) ||
3034 (tvp
&& (vnode_mount(fvp
) != vnode_mount(tvp
)))) {
3035 if (nd
->nd_vers
== NFS_VER3
) {
3043 * The following edge case is caught here:
3044 * (to cannot be a descendent of from)
3057 if (tdvp
->v_parent
== fvp
) {
3058 if (nd
->nd_vers
== NFS_VER3
) {
3065 if (fvtype
== VDIR
&& vnode_mountedhere(fvp
)) {
3066 if (nd
->nd_vers
== NFS_VER3
) {
3074 * If source is the same as the destination (that is the
3075 * same vnode) then there is nothing to do...
3076 * EXCEPT if the underlying file system supports case
3077 * insensitivity and is case preserving. In this case
3078 * the file system needs to handle the special case of
3079 * getting the same vnode as target (fvp) and source (tvp).
3081 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
3082 * and _PC_CASE_PRESERVING can have this exception, and they need to
3083 * handle the special case of getting the same vnode as target and
3084 * source. NOTE: Then the target is unlocked going into vnop_rename,
3085 * so not to cause locking problems. There is a single reference on tvp.
3087 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
3088 * that correct behaviour then is just to remove the source (link)
3090 if ((fvp
== tvp
) && (fdvp
== tdvp
)) {
3091 if (fromni
.ni_cnd
.cn_namelen
== toni
.ni_cnd
.cn_namelen
&&
3092 !bcmp(fromni
.ni_cnd
.cn_nameptr
, toni
.ni_cnd
.cn_nameptr
,
3093 fromni
.ni_cnd
.cn_namelen
)) {
3098 if (holding_mntlock
&& vnode_mount(fvp
) != locked_mp
) {
3100 * we're holding a reference and lock
3101 * on locked_mp, but it no longer matches
3102 * what we want to do... so drop our hold
3104 mount_unlock_renames(locked_mp
);
3105 mount_drop(locked_mp
, 0);
3106 holding_mntlock
= 0;
3108 if (tdvp
!= fdvp
&& fvtype
== VDIR
) {
3110 * serialize renames that re-shape
3111 * the tree... if holding_mntlock is
3112 * set, then we're ready to go...
3114 * first need to drop the iocounts
3115 * we picked up, second take the
3116 * lock to serialize the access,
3117 * then finally start the lookup
3118 * process over with the lock held
3120 if (!holding_mntlock
) {
3122 * need to grab a reference on
3123 * the mount point before we
3124 * drop all the iocounts... once
3125 * the iocounts are gone, the mount
3128 locked_mp
= vnode_mount(fvp
);
3129 mount_ref(locked_mp
, 0);
3131 /* make a copy of to path to pass to nfsrv_namei() again */
3132 topath
= zalloc(ZV_NAMEI
);
3133 bcopy(toni
.ni_cnd
.cn_pnbuf
, topath
, tolen
+ 1);
3136 * nameidone has to happen before we vnode_put(tdvp)
3137 * since it may need to release the fs_nodelock on the tdvp
3146 /* make a copy of from path to pass to nfsrv_namei() again */
3147 frompath
= zalloc(ZV_NAMEI
);
3148 bcopy(fromni
.ni_cnd
.cn_pnbuf
, frompath
, fromlen
+ 1);
3151 * nameidone has to happen before we vnode_put(fdvp)
3152 * since it may need to release the fs_nodelock on the fdvp
3167 mount_lock_renames(locked_mp
);
3168 holding_mntlock
= 1;
3173 fdpreattrerr
= tdpreattrerr
= ENOENT
;
3175 if (!topath
|| !frompath
) {
3176 /* we couldn't allocate a path, so bail */
3181 /* reset credential if it was remapped */
3182 if (nd
->nd_cr
!= saved_cred
) {
3183 kauth_cred_ref(saved_cred
);
3184 kauth_cred_unref(&nd
->nd_cr
);
3185 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
3192 * when we dropped the iocounts to take
3193 * the lock, we allowed the identity of
3194 * the various vnodes to change... if they did,
3195 * we may no longer be dealing with a rename
3196 * that reshapes the tree... once we're holding
3197 * the iocounts, the vnodes can't change type
3198 * so we're free to drop the lock at this point
3201 if (holding_mntlock
) {
3202 mount_unlock_renames(locked_mp
);
3203 mount_drop(locked_mp
, 0);
3204 holding_mntlock
= 0;
3208 // save these off so we can later verify that fvp is the same
3210 oname
= fvp
->v_name
;
3211 oparent
= fvp
->v_parent
;
3214 * If generating an fsevent, then
3215 * stash any pre-rename info we may need.
3218 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_RENAME
, fvp
)) {
3219 int from_truncated
= 0, to_truncated
= 0;
3221 get_fse_info(fvp
, &from_finfo
, ctx
);
3223 get_fse_info(tvp
, &to_finfo
, ctx
);
3226 from_name
= get_pathbuff();
3228 from_len
= safe_getpath(fdvp
, fromni
.ni_cnd
.cn_nameptr
, from_name
, MAXPATHLEN
, &from_truncated
);
3231 to_name
= from_name
? get_pathbuff() : NULL
;
3233 to_len
= safe_getpath(tdvp
, toni
.ni_cnd
.cn_nameptr
, to_name
, MAXPATHLEN
, &to_truncated
);
3236 if (from_truncated
|| to_truncated
) {
3237 from_finfo
.mode
|= FSE_TRUNCATED_PATH
;
3243 #else /* CONFIG_FSE */
3246 #endif /* CONFIG_FSE */
3248 error
= VNOP_RENAME(fromni
.ni_dvp
, fromni
.ni_vp
, &fromni
.ni_cnd
,
3249 toni
.ni_dvp
, toni
.ni_vp
, &toni
.ni_cnd
, ctx
);
3251 * fix up name & parent pointers. note that we first
3252 * check that fvp has the same name/parent pointers it
3253 * had before the rename call... this is a 'weak' check
3256 if (oname
== fvp
->v_name
&& oparent
== fvp
->v_parent
) {
3258 update_flags
= VNODE_UPDATE_NAME
;
3260 update_flags
|= VNODE_UPDATE_PARENT
;
3262 vnode_update_identity(fvp
, tdvp
, toni
.ni_cnd
.cn_nameptr
,
3263 toni
.ni_cnd
.cn_namelen
, toni
.ni_cnd
.cn_hash
, update_flags
);
3267 * If the rename is OK and we've got the paths
3268 * then add an fsevent.
3271 if (nfsrv_fsevents_enabled
&& !error
&& from_name
&& to_name
) {
3273 add_fsevent(FSE_RENAME
, ctx
,
3274 FSE_ARG_STRING
, from_len
, from_name
,
3275 FSE_ARG_FINFO
, &from_finfo
,
3276 FSE_ARG_STRING
, to_len
, to_name
,
3277 FSE_ARG_FINFO
, &to_finfo
,
3280 add_fsevent(FSE_RENAME
, ctx
,
3281 FSE_ARG_STRING
, from_len
, from_name
,
3282 FSE_ARG_FINFO
, &from_finfo
,
3283 FSE_ARG_STRING
, to_len
, to_name
,
3288 release_pathbuff(from_name
);
3291 release_pathbuff(to_name
);
3293 #endif /* CONFIG_FSE */
3294 from_name
= to_name
= NULL
;
3297 if (holding_mntlock
) {
3298 mount_unlock_renames(locked_mp
);
3299 mount_drop(locked_mp
, 0);
3300 holding_mntlock
= 0;
3304 * nameidone has to happen before we vnode_put(tdvp)
3305 * since it may need to release the fs_nodelock on the tdvp
3317 * nameidone has to happen before we vnode_put(fdvp)
3318 * since it may need to release the fs_nodelock on the fdvp
3330 nfsm_srv_vattr_init(&fdpostattr
, nd
->nd_vers
);
3331 fdpostattrerr
= vnode_getattr(fdirp
, &fdpostattr
, ctx
);
3336 nfsm_srv_vattr_init(&tdpostattr
, nd
->nd_vers
);
3337 tdpostattrerr
= vnode_getattr(tdirp
, &tdpostattr
, ctx
);
3343 /* assemble reply */
3344 nd
->nd_repstat
= error
;
3345 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 2 * NFSX_WCCDATA(nd
->nd_vers
));
3347 *mrepp
= nmrep
.nmc_mhead
;
3348 nfsmout_on_status(nd
, error
);
3349 if (nd
->nd_vers
== NFS_VER3
) {
3350 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3351 fdpreattrerr
, &fdpreattr
, fdpostattrerr
, &fdpostattr
);
3352 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3353 tdpreattrerr
, &tdpreattr
, tdpostattrerr
, &tdpostattr
);
3356 nfsm_chain_build_done(error
, &nmrep
);
3357 if (holding_mntlock
) {
3358 mount_unlock_renames(locked_mp
);
3359 mount_drop(locked_mp
, 0);
3363 * nameidone has to happen before we vnode_put(tdvp)
3364 * since it may need to release the fs_nodelock on the tdvp
3375 * nameidone has to happen before we vnode_put(fdvp)
3376 * since it may need to release the fs_nodelock on the fdvp
3392 NFS_ZFREE(ZV_NAMEI
, frompath
);
3395 NFS_ZFREE(ZV_NAMEI
, topath
);
3398 kauth_cred_unref(&saved_cred
);
3401 nfsm_chain_cleanup(&nmrep
);
3412 struct nfsrv_descript
*nd
,
3413 struct nfsrv_sock
*slp
,
3417 struct nameidata ni
;
3418 int error
, dpreattrerr
, dpostattrerr
, attrerr
;
3420 vnode_t vp
, xp
, dvp
, dirp
;
3421 struct vnode_attr dpreattr
, dpostattr
, attr
;
3422 struct nfs_filehandle nfh
, dnfh
;
3423 struct nfs_export
*nx
;
3424 struct nfs_export_options
*nxo
;
3425 struct nfsm_chain
*nmreq
, nmrep
;
3428 dpreattrerr
= dpostattrerr
= attrerr
= ENOENT
;
3429 vp
= xp
= dvp
= dirp
= NULL
;
3430 nmreq
= &nd
->nd_nmreq
;
3431 nfsm_chain_null(&nmrep
);
3433 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3434 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
3435 nfsm_chain_get_32(error
, nmreq
, len
);
3436 nfsm_name_len_check(error
, nd
, len
);
3438 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
3441 /* update export stats */
3442 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3444 /* update active user stats */
3445 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
3447 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
3450 /* we're not allowed to link to directories... */
3451 if (vnode_vtype(vp
) == VDIR
) {
3452 error
= EPERM
; /* POSIX */
3456 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3457 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LINKTARGET
, ctx
, nxo
, 0)) != 0) {
3461 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3465 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3466 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3468 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
3471 if (nd
->nd_vers
== NFS_VER3
) {
3472 nfsm_srv_pre_vattr_init(&dpreattr
);
3473 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3487 } else if (vnode_mount(vp
) != vnode_mount(dvp
)) {
3490 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3495 error
= mac_vnode_check_link(ctx
, dvp
, vp
, &ni
.ni_cnd
);
3502 error
= VNOP_LINK(vp
, dvp
, &ni
.ni_cnd
, ctx
);
3506 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CREATE_FILE
, dvp
)) {
3507 char *target_path
= NULL
;
3508 int plen
, truncated
= 0;
3511 /* build the path to the new link file */
3512 target_path
= get_pathbuff();
3514 plen
= safe_getpath(dvp
, ni
.ni_cnd
.cn_nameptr
, target_path
, MAXPATHLEN
, &truncated
);
3516 if (get_fse_info(vp
, &finfo
, ctx
) == 0) {
3518 finfo
.mode
|= FSE_TRUNCATED_PATH
;
3520 add_fsevent(FSE_CREATE_FILE
, ctx
,
3521 FSE_ARG_STRING
, plen
, target_path
,
3522 FSE_ARG_FINFO
, &finfo
,
3526 release_pathbuff(target_path
);
3532 * nameidone has to happen before we vnode_put(dvp)
3533 * since it may need to release the fs_nodelock on the dvp
3542 if (nd
->nd_vers
== NFS_VER3
) {
3543 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
3544 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
3547 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3548 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3556 /* assemble reply */
3557 nd
->nd_repstat
= error
;
3558 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3560 *mrepp
= nmrep
.nmc_mhead
;
3561 nfsmout_on_status(nd
, error
);
3562 if (nd
->nd_vers
== NFS_VER3
) {
3563 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
3564 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3565 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3568 nfsm_chain_build_done(error
, &nmrep
);
3573 nfsm_chain_cleanup(&nmrep
);
3580 * nfs symbolic link service
3584 struct nfsrv_descript
*nd
,
3585 struct nfsrv_sock
*slp
,
3589 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3590 struct vnode_attr va
, *vap
= &va
;
3591 struct nameidata ni
;
3592 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3593 uint32_t len
= 0, linkdatalen
, cnflags
;
3596 vnode_t vp
, dvp
, dirp
;
3597 struct nfs_filehandle nfh
;
3598 struct nfs_export
*nx
= NULL
;
3599 struct nfs_export_options
*nxo
= NULL
;
3601 char uio_buf
[UIO_SIZEOF(1)];
3602 struct nfsm_chain
*nmreq
, nmrep
;
3605 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3606 nmreq
= &nd
->nd_nmreq
;
3607 nfsm_chain_null(&nmrep
);
3611 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3613 ni
.ni_cnd
.cn_nameiop
= 0;
3616 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3617 nfsm_chain_get_32(error
, nmreq
, len
);
3618 nfsm_name_len_check(error
, nd
, len
);
3621 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3625 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3627 ni
.ni_cnd
.cn_ndp
= &ni
;
3628 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3630 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3632 /* update export stats */
3633 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3635 /* update active user stats */
3636 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3640 if (nd
->nd_vers
== NFS_VER3
) {
3641 nfsm_srv_pre_vattr_init(&dpreattr
);
3642 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3649 ni
.ni_cnd
.cn_nameiop
= 0;
3656 if (nd
->nd_vers
== NFS_VER3
) {
3657 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3659 nfsm_chain_get_32(error
, nmreq
, linkdatalen
);
3660 if (!error
&& (((nd
->nd_vers
== NFS_VER2
) && (linkdatalen
> NFS_MAXPATHLEN
)) ||
3661 ((nd
->nd_vers
== NFS_VER3
) && (linkdatalen
> MAXPATHLEN
)))) {
3662 error
= NFSERR_NAMETOL
;
3665 MALLOC(linkdata
, caddr_t
, linkdatalen
+ 1, M_TEMP
, M_WAITOK
);
3667 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
3668 &uio_buf
[0], sizeof(uio_buf
));
3670 if (!linkdata
|| !auio
) {
3674 uio_addiov(auio
, CAST_USER_ADDR_T(linkdata
), linkdatalen
);
3675 error
= nfsm_chain_get_uio(nmreq
, linkdatalen
, auio
);
3676 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
3677 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3680 *(linkdata
+ linkdatalen
) = '\0';
3686 VATTR_SET(vap
, va_type
, VLNK
);
3687 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3688 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3690 * Server policy is to alway use the mapped rpc credential for
3691 * file system object creation. This has the nice side effect of
3692 * enforcing BSD creation semantics
3694 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3695 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3697 /* authorize before creating */
3698 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3700 /* validate given attributes */
3702 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3705 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
3712 error
= VNOP_SYMLINK(dvp
, &vp
, &ni
.ni_cnd
, vap
, linkdata
, ctx
);
3715 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
3717 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
3719 ni
.ni_op
= OP_LOOKUP
;
3721 ni
.ni_cnd
.cn_flags
&= ~(LOCKPARENT
| FOLLOW
);
3722 ni
.ni_cnd
.cn_flags
|= (NOFOLLOW
| LOCKLEAF
);
3723 ni
.ni_cnd
.cn_context
= ctx
;
3724 ni
.ni_startdir
= dvp
;
3726 ni
.ni_rootdir
= rootvnode
;
3727 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
3728 while ((error
= lookup(&ni
)) == ERECYCLE
) {
3729 ni
.ni_cnd
.cn_flags
= cnflags
;
3730 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
3731 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
3738 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
3740 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
3741 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
3747 if (nfsrv_fsevents_enabled
&& !error
&& vp
) {
3748 add_fsevent(FSE_CREATE_FILE
, ctx
,
3755 * nameidone has to happen before we vnode_put(dvp)
3756 * since it may need to release the fs_nodelock on the dvp
3759 ni
.ni_cnd
.cn_nameiop
= 0;
3766 FREE(linkdata
, M_TEMP
);
3770 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3771 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3777 /* assemble reply */
3778 nd
->nd_repstat
= error
;
3779 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
3780 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3782 *mrepp
= nmrep
.nmc_mhead
;
3783 nfsmout_on_status(nd
, error
);
3784 if (nd
->nd_vers
== NFS_VER3
) {
3785 if (!nd
->nd_repstat
) {
3786 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3787 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
3789 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3790 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3793 nfsm_chain_build_done(error
, &nmrep
);
3794 if (ni
.ni_cnd
.cn_nameiop
) {
3796 * nameidone has to happen before we vnode_put(dvp)
3797 * since it may need to release the fs_nodelock on the dvp
3810 FREE(linkdata
, M_TEMP
);
3813 nfsm_chain_cleanup(&nmrep
);
3825 struct nfsrv_descript
*nd
,
3826 struct nfsrv_sock
*slp
,
3830 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3831 struct vnode_attr va
, *vap
= &va
;
3832 struct nameidata ni
;
3833 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3835 vnode_t vp
, dvp
, dirp
;
3836 struct nfs_filehandle nfh
;
3837 struct nfs_export
*nx
= NULL
;
3838 struct nfs_export_options
*nxo
= NULL
;
3840 kauth_acl_t xacl
= NULL
;
3841 struct nfsm_chain
*nmreq
, nmrep
;
3844 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3845 nmreq
= &nd
->nd_nmreq
;
3846 nfsm_chain_null(&nmrep
);
3848 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3850 ni
.ni_cnd
.cn_nameiop
= 0;
3851 vp
= dvp
= dirp
= NULL
;
3853 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3854 nfsm_chain_get_32(error
, nmreq
, len
);
3855 nfsm_name_len_check(error
, nd
, len
);
3858 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3862 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| WILLBEDIR
;
3863 ni
.ni_cnd
.cn_ndp
= &ni
;
3864 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3866 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3868 /* update export stats */
3869 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3871 /* update active user stats */
3872 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3876 if (nd
->nd_vers
== NFS_VER3
) {
3877 nfsm_srv_pre_vattr_init(&dpreattr
);
3878 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3885 ni
.ni_cnd
.cn_nameiop
= 0;
3892 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3894 VATTR_SET(vap
, va_type
, VDIR
);
3898 * nameidone has to happen before we vnode_put(dvp)
3899 * since it may need to release the fs_nodelock on the dvp
3908 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_SUBDIRECTORY
, ctx
, nxo
, 0);
3910 /* construct ACL and handle inheritance */
3912 error
= kauth_acl_inherit(dvp
,
3918 if (!error
&& xacl
!= NULL
) {
3919 VATTR_SET(vap
, va_acl
, xacl
);
3923 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3924 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3926 * We don't support the S_ISGID bit for directories. Solaris and other
3927 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3930 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
3931 vap
->va_mode
&= ~S_ISGID
;
3934 * Server policy is to alway use the mapped rpc credential for
3935 * file system object creation. This has the nice side effect of
3936 * enforcing BSD creation semantics
3938 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3939 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3941 /* validate new-file security information */
3943 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3946 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3947 * sit well with our clients so we map all errors to EPERM.
3954 error
= vn_authorize_mkdir(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
3961 error
= VNOP_MKDIR(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
3965 if (nfsrv_fsevents_enabled
&& !error
) {
3966 add_fsevent(FSE_CREATE_DIR
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
3970 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
3972 * If some of the requested attributes weren't handled by the VNOP,
3973 * use our fallback code.
3975 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
3979 kauth_acl_free(xacl
);
3983 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
3985 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
3986 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
3987 if (nd
->nd_vers
== NFS_VER2
) {
3988 error
= postattrerr
;
3995 * nameidone has to happen before we vnode_put(dvp)
3996 * since it may need to release the fs_nodelock on the dvp
4001 ni
.ni_cnd
.cn_nameiop
= 0;
4004 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
4005 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
4011 /* assemble reply */
4012 nd
->nd_repstat
= error
;
4013 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
4014 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
4016 *mrepp
= nmrep
.nmc_mhead
;
4017 nfsmout_on_status(nd
, error
);
4018 if (nd
->nd_vers
== NFS_VER3
) {
4019 if (!nd
->nd_repstat
) {
4020 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4021 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
4023 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4024 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
4026 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4028 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
4032 nfsm_chain_build_done(error
, &nmrep
);
4033 if (ni
.ni_cnd
.cn_nameiop
) {
4035 * nameidone has to happen before we vnode_put(dvp)
4036 * since it may need to release the fs_nodelock on the dvp
4048 nfsm_chain_cleanup(&nmrep
);
4059 struct nfsrv_descript
*nd
,
4060 struct nfsrv_sock
*slp
,
4064 int error
, dpreattrerr
, dpostattrerr
;
4067 vnode_t vp
, dvp
, dirp
;
4068 struct vnode_attr dpreattr
, dpostattr
;
4069 struct nfs_filehandle nfh
;
4070 struct nfs_export
*nx
= NULL
;
4071 struct nfs_export_options
*nxo
= NULL
;
4072 struct nameidata ni
;
4073 struct nfsm_chain
*nmreq
, nmrep
;
4076 dpreattrerr
= dpostattrerr
= ENOENT
;
4077 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
4078 nmreq
= &nd
->nd_nmreq
;
4079 nfsm_chain_null(&nmrep
);
4081 vp
= dvp
= dirp
= NULL
;
4083 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4084 nfsm_chain_get_32(error
, nmreq
, len
);
4085 nfsm_name_len_check(error
, nd
, len
);
4088 ni
.ni_cnd
.cn_nameiop
= DELETE
;
4090 ni
.ni_op
= OP_UNLINK
;
4092 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
4093 ni
.ni_cnd
.cn_ndp
= &ni
;
4094 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
4096 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
4098 /* update export stats */
4099 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4101 /* update active user stats */
4102 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
4106 if (nd
->nd_vers
== NFS_VER3
) {
4107 nfsm_srv_pre_vattr_init(&dpreattr
);
4108 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
4119 if (vnode_vtype(vp
) != VDIR
) {
4124 * No rmdir "." please.
4131 * The root of a mounted filesystem cannot be deleted.
4133 if (vnode_isvroot(vp
)) {
4137 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
4140 error
= vn_authorize_rmdir(dvp
, vp
, &ni
.ni_cnd
, ctx
, NULL
);
4152 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
4154 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
4155 get_fse_info(vp
, &finfo
, ctx
);
4157 release_pathbuff(path
);
4161 #endif /* CONFIG_FSE */
4163 error
= VNOP_RMDIR(dvp
, vp
, &ni
.ni_cnd
, ctx
);
4168 add_fsevent(FSE_DELETE
, ctx
,
4169 FSE_ARG_STRING
, plen
, path
,
4170 FSE_ARG_FINFO
, &finfo
,
4173 release_pathbuff(path
);
4175 #endif /* CONFIG_FSE */
4179 * nameidone has to happen before we vnode_put(dvp)
4180 * since it may need to release the fs_nodelock on the dvp
4188 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
4189 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
4195 /* assemble reply */
4196 nd
->nd_repstat
= error
;
4197 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
4199 *mrepp
= nmrep
.nmc_mhead
;
4200 nfsmout_on_status(nd
, error
);
4201 if (nd
->nd_vers
== NFS_VER3
) {
4202 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4203 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
4206 nfsm_chain_build_done(error
, &nmrep
);
4211 nfsm_chain_cleanup(&nmrep
);
4218 * nfs readdir service
4219 * - mallocs what it thinks is enough to read
4220 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4221 * - calls VNOP_READDIR()
4222 * - loops around building the reply
4223 * if the output generated exceeds count break out of loop
4224 * The nfsm_clget macro is used here so that the reply will be packed
4225 * tightly in mbuf clusters.
4226 * - it only knows that it has encountered eof when the VNOP_READDIR()
4228 * - as such one readdir rpc will return eof false although you are there
4229 * and then the next will return eof
4230 * - it trims out records with d_fileno == 0
4231 * this doesn't matter for Unix clients, but they might confuse clients
4233 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4234 * than requested, but this may not apply to all filesystems. For
4235 * example, client NFS does not { although it is never remote mounted
4237 * The alternate call nfsrv_readdirplus() does lookups as well.
4238 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
4239 * are supposed to cover. For readdir, the count is the total number of
4240 * bytes included in everything from the directory's postopattr through
4241 * the EOF flag. For readdirplus, the maxcount is the same, and the
4242 * dircount includes all that except for the entry attributes and handles.
4246 struct nfsrv_descript
*nd
,
4247 struct nfsrv_sock
*slp
,
4251 struct direntry
*dp
;
4252 char *cpos
, *cend
, *rbuf
;
4254 struct vnode_attr attr
;
4255 struct nfs_filehandle nfh
;
4256 struct nfs_export
*nx
;
4257 struct nfs_export_options
*nxo
;
4259 char uio_buf
[UIO_SIZEOF(1)];
4260 int len
, nlen
, rem
, xfer
, error
, attrerr
;
4261 int siz
, count
, fullsiz
, eofflag
, nentries
;
4262 u_quad_t off
, toff
, verf
= 0;
4264 struct nfsm_chain
*nmreq
, nmrep
;
4268 count
= nentries
= 0;
4269 nmreq
= &nd
->nd_nmreq
;
4270 nfsm_chain_null(&nmrep
);
4274 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
4276 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4277 if (nd
->nd_vers
== NFS_VER3
) {
4278 nfsm_chain_get_64(error
, nmreq
, toff
);
4279 nfsm_chain_get_64(error
, nmreq
, verf
);
4281 nfsm_chain_get_32(error
, nmreq
, toff
);
4283 nfsm_chain_get_32(error
, nmreq
, count
);
4287 siz
= ((count
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4288 xfer
= NFSRV_NDMAXDATA(nd
);
4294 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4297 /* update export stats */
4298 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4300 /* update active user stats */
4301 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4303 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4306 if (nxo
->nxo_flags
& NX_MANGLEDNAMES
|| nd
->nd_vers
== NFS_VER2
) {
4307 vnopflag
|= VNODE_READDIR_NAMEMAX
;
4310 if ((nd
->nd_vers
== NFS_VER2
) || (nxo
->nxo_flags
& NX_32BITCLIENTS
)) {
4311 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
4314 if (nd
->nd_vers
== NFS_VER3
) {
4315 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4316 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4317 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
)) {
4318 error
= NFSERR_BAD_COOKIE
;
4322 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
4326 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
)) {
4331 error
= mac_vnode_check_readdir(ctx
, vp
);
4337 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
4339 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
4340 &uio_buf
[0], sizeof(uio_buf
));
4342 if (!rbuf
|| !auio
) {
4347 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
4348 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
4350 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
4351 off
= uio_offset(auio
);
4353 if (nd
->nd_vers
== NFS_VER3
) {
4354 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4355 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4359 if (uio_resid(auio
) != 0) {
4360 siz
-= uio_resid(auio
);
4362 /* If nothing read, return empty reply with eof set */
4367 /* assemble reply */
4368 nd
->nd_repstat
= error
;
4369 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
4370 NFSX_COOKIEVERF(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
);
4372 *mrepp
= nmrep
.nmc_mhead
;
4373 nfsmout_on_status(nd
, error
);
4374 if (nd
->nd_vers
== NFS_VER3
) {
4375 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4376 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4378 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4379 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4380 nfsm_chain_build_done(error
, &nmrep
);
4386 * Check for degenerate cases of nothing useful read.
4387 * If so go try again
4391 dp
= (struct direntry
*)cpos
;
4392 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4393 cpos
+= dp
->d_reclen
;
4394 dp
= (struct direntry
*)cpos
;
4397 if ((cpos
>= cend
) || (nentries
== 0)) {
4406 /* assemble reply */
4407 nd
->nd_repstat
= error
;
4408 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
4409 NFSX_COOKIEVERF(nd
->nd_vers
) + siz
);
4411 *mrepp
= nmrep
.nmc_mhead
;
4412 nfsmout_on_status(nd
, error
);
4413 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4415 len
= 2 * NFSX_UNSIGNED
;
4416 if (nd
->nd_vers
== NFS_VER3
) {
4417 len
+= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
;
4418 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4419 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4423 /* Loop through the records and build reply */
4424 while ((cpos
< cend
) && (nentries
> 0)) {
4425 if (dp
->d_fileno
!= 0) {
4426 nlen
= dp
->d_namlen
;
4427 if ((nd
->nd_vers
== NFS_VER2
) && (nlen
> NFS_MAXNAMLEN
)) {
4428 nlen
= NFS_MAXNAMLEN
;
4430 rem
= nfsm_rndup(nlen
) - nlen
;
4431 len
+= (4 * NFSX_UNSIGNED
+ nlen
+ rem
);
4432 if (nd
->nd_vers
== NFS_VER3
) {
4433 len
+= 2 * NFSX_UNSIGNED
;
4439 /* Build the directory record xdr from the direntry. */
4440 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4441 if (nd
->nd_vers
== NFS_VER3
) {
4442 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4444 nfsm_chain_add_32(error
, &nmrep
, dp
->d_fileno
);
4446 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4447 if (nd
->nd_vers
== NFS_VER3
) {
4448 if (vnopflag
& VNODE_READDIR_SEEKOFF32
) {
4449 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4451 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4453 nfsm_chain_add_32(error
, &nmrep
, dp
->d_seekoff
);
4457 cpos
+= dp
->d_reclen
;
4458 dp
= (struct direntry
*)cpos
;
4461 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4462 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4472 nd
->nd_repstat
= error
;
4473 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
));
4475 *mrepp
= nmrep
.nmc_mhead
;
4476 nfsmout_on_status(nd
, error
);
4477 if (nd
->nd_vers
== NFS_VER3
) {
4478 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4481 nfsm_chain_build_done(error
, &nmrep
);
4483 nfsm_chain_cleanup(&nmrep
);
4491 struct nfsrv_descript
*nd
,
4492 struct nfsrv_sock
*slp
,
4496 struct direntry
*dp
;
4497 char *cpos
, *cend
, *rbuf
;
4499 struct nfs_filehandle dnfh
, nfh
;
4500 struct nfs_export
*nx
;
4501 struct nfs_export_options
*nxo
;
4503 char uio_buf
[UIO_SIZEOF(1)];
4504 struct vnode_attr attr
, va
, *vap
= &va
;
4505 int len
, nlen
, rem
, xfer
, error
, attrerr
, gotfh
, gotattr
;
4506 int siz
, dircount
, maxcount
, fullsiz
, eofflag
, dirlen
, nentries
, isdotdot
;
4507 u_quad_t off
, toff
, verf
;
4509 struct nfsm_chain
*nmreq
, nmrep
;
4514 nmreq
= &nd
->nd_nmreq
;
4515 nfsm_chain_null(&nmrep
);
4518 dircount
= maxcount
= 0;
4520 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
4522 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
4523 nfsm_chain_get_64(error
, nmreq
, toff
);
4524 nfsm_chain_get_64(error
, nmreq
, verf
);
4525 nfsm_chain_get_32(error
, nmreq
, dircount
);
4526 nfsm_chain_get_32(error
, nmreq
, maxcount
);
4530 xfer
= NFSRV_NDMAXDATA(nd
);
4531 dircount
= ((dircount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4532 if (dircount
> xfer
) {
4535 fullsiz
= siz
= dircount
;
4536 maxcount
= ((maxcount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4537 if (maxcount
> xfer
) {
4541 error
= nfsrv_fhtovp(&dnfh
, nd
, &vp
, &nx
, &nxo
);
4544 /* update export stats */
4545 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4547 /* update active user stats */
4548 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4550 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4553 if (nxo
->nxo_flags
& NX_32BITCLIENTS
) {
4554 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
4557 if (nxo
->nxo_flags
& NX_MANGLEDNAMES
) {
4558 vnopflag
|= VNODE_READDIR_NAMEMAX
;
4561 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4562 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4563 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
)) {
4564 error
= NFSERR_BAD_COOKIE
;
4567 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
4571 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
)) {
4576 error
= mac_vnode_check_readdir(ctx
, vp
);
4582 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
4584 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
4585 &uio_buf
[0], sizeof(uio_buf
));
4587 if (!rbuf
|| !auio
) {
4593 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
4594 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
4596 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
4597 off
= uio_offset(auio
);
4598 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4599 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4602 if (uio_resid(auio
) != 0) {
4603 siz
-= uio_resid(auio
);
4605 /* If nothing read, return empty reply with eof set */
4610 /* assemble reply */
4611 nd
->nd_repstat
= error
;
4612 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+
4613 NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
);
4615 *mrepp
= nmrep
.nmc_mhead
;
4616 nfsmout_on_status(nd
, error
);
4617 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4618 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4619 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4620 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4621 nfsm_chain_build_done(error
, &nmrep
);
4627 * Check for degenerate cases of nothing useful read.
4628 * If so go try again
4632 dp
= (struct direntry
*)cpos
;
4633 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4634 cpos
+= dp
->d_reclen
;
4635 dp
= (struct direntry
*)cpos
;
4638 if ((cpos
>= cend
) || (nentries
== 0)) {
4645 * Probe one of the directory entries to see if the filesystem
4648 if ((error
= VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
))) {
4649 if (error
== ENOTSUP
) { /* let others get passed back */
4650 error
= NFSERR_NOTSUPP
;
4656 /* assemble reply */
4657 nd
->nd_repstat
= error
;
4658 error
= nfsrv_rephead(nd
, slp
, &nmrep
, maxcount
);
4660 *mrepp
= nmrep
.nmc_mhead
;
4661 nfsmout_on_status(nd
, error
);
4662 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4664 dirlen
= len
= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
;
4665 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4666 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4669 /* Loop through the records and build reply */
4670 while ((cpos
< cend
) && (nentries
> 0)) {
4671 if (dp
->d_fileno
!= 0) {
4672 nlen
= dp
->d_namlen
;
4673 rem
= nfsm_rndup(nlen
) - nlen
;
4674 gotfh
= gotattr
= 1;
4676 /* Got to get the vnode for lookup per entry. */
4677 if (VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
)) {
4678 /* Can't get the vnode... so no fh or attrs */
4679 gotfh
= gotattr
= 0;
4681 isdotdot
= ((dp
->d_namlen
== 2) &&
4682 (dp
->d_name
[0] == '.') && (dp
->d_name
[1] == '.'));
4683 if (nfsrv_vptofh(nx
, 0, (isdotdot
? &dnfh
: NULL
), nvp
, ctx
, &nfh
)) {
4686 nfsm_srv_vattr_init(vap
, NFS_VER3
);
4687 if (vnode_getattr(nvp
, vap
, ctx
)) {
4694 * If either the dircount or maxcount will be
4695 * exceeded, get out now. Both of these lengths
4696 * are calculated conservatively, including all
4699 len
+= 8 * NFSX_UNSIGNED
+ nlen
+ rem
;
4701 len
+= NFSX_V3FATTR
;
4704 len
+= NFSX_UNSIGNED
+ nfsm_rndup(nfh
.nfh_len
);
4706 dirlen
+= 6 * NFSX_UNSIGNED
+ nlen
+ rem
;
4707 if ((len
> maxcount
) || (dirlen
> dircount
)) {
4712 /* Build the directory record xdr from the direntry. */
4713 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4714 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4715 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4716 if (vnopflag
& VNODE_READDIR_SEEKOFF32
) {
4717 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4719 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4720 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, (gotattr
? 0 : ENOENT
), vap
);
4722 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4724 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4728 cpos
+= dp
->d_reclen
;
4729 dp
= (struct direntry
*)cpos
;
4734 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4735 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4742 nd
->nd_repstat
= error
;
4743 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
);
4745 *mrepp
= nmrep
.nmc_mhead
;
4746 nfsmout_on_status(nd
, error
);
4747 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4749 nfsm_chain_build_done(error
, &nmrep
);
4754 nfsm_chain_cleanup(&nmrep
);
4761 * nfs commit service
4765 struct nfsrv_descript
*nd
,
4766 struct nfsrv_sock
*slp
,
4771 struct nfs_filehandle nfh
;
4772 struct nfs_export
*nx
= NULL
;
4773 struct nfs_export_options
*nxo
;
4774 int error
, preattrerr
, postattrerr
, count
;
4775 struct vnode_attr preattr
, postattr
;
4777 struct nfsm_chain
*nmreq
, nmrep
;
4780 preattrerr
= postattrerr
= ENOENT
;
4781 nmreq
= &nd
->nd_nmreq
;
4782 nfsm_chain_null(&nmrep
);
4786 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4787 * count parameters, so those arguments are useless (someday maybe).
4790 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4791 nfsm_chain_get_64(error
, nmreq
, off
);
4792 nfsm_chain_get_32(error
, nmreq
, count
);
4795 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4798 /* update export stats */
4799 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4801 /* update active user stats */
4802 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4804 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4807 nfsm_srv_pre_vattr_init(&preattr
);
4808 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
4810 error
= VNOP_FSYNC(vp
, MNT_WAIT
, ctx
);
4812 nfsm_srv_vattr_init(&postattr
, 1);
4813 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
4820 /* assemble reply */
4821 nd
->nd_repstat
= error
;
4822 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3WCCDATA
+ NFSX_V3WRITEVERF
);
4824 *mrepp
= nmrep
.nmc_mhead
;
4825 nfsmout_on_status(nd
, error
);
4826 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4827 preattrerr
, &preattr
, postattrerr
, &postattr
);
4828 if (!nd
->nd_repstat
) {
4829 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
4830 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
4833 nfsm_chain_build_done(error
, &nmrep
);
4835 nfsm_chain_cleanup(&nmrep
);
4842 * nfs statfs service
4846 struct nfsrv_descript
*nd
,
4847 struct nfsrv_sock
*slp
,
4854 struct vnode_attr attr
;
4855 struct nfs_filehandle nfh
;
4856 struct nfs_export
*nx
;
4857 struct nfs_export_options
*nxo
;
4859 struct nfsm_chain
*nmreq
, nmrep
;
4863 nmreq
= &nd
->nd_nmreq
;
4864 nfsm_chain_null(&nmrep
);
4868 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4870 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4873 /* update export stats */
4874 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4876 /* update active user stats */
4877 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4879 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4883 VFSATTR_WANTED(&va
, f_blocks
);
4884 VFSATTR_WANTED(&va
, f_bfree
);
4885 VFSATTR_WANTED(&va
, f_bavail
);
4886 VFSATTR_WANTED(&va
, f_files
);
4887 VFSATTR_WANTED(&va
, f_ffree
);
4888 error
= vfs_getattr(vnode_mount(vp
), &va
, ctx
);
4889 blksize
= vnode_mount(vp
)->mnt_vfsstat
.f_bsize
;
4891 if (nd
->nd_vers
== NFS_VER3
) {
4892 nfsm_srv_vattr_init(&attr
, nd
->nd_vers
);
4893 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4901 /* assemble reply */
4902 nd
->nd_repstat
= error
;
4903 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_STATFS(nd
->nd_vers
));
4905 *mrepp
= nmrep
.nmc_mhead
;
4906 nfsmout_on_status(nd
, error
);
4907 if (nd
->nd_vers
== NFS_VER3
) {
4908 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4910 nfsmout_if(nd
->nd_repstat
);
4912 if (nd
->nd_vers
== NFS_VER3
) {
4913 nfsm_chain_add_64(error
, &nmrep
, va
.f_blocks
* blksize
);
4914 nfsm_chain_add_64(error
, &nmrep
, va
.f_bfree
* blksize
);
4915 nfsm_chain_add_64(error
, &nmrep
, va
.f_bavail
* blksize
);
4916 nfsm_chain_add_64(error
, &nmrep
, va
.f_files
);
4917 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4918 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4919 nfsm_chain_add_32(error
, &nmrep
, 0); /* invarsec */
4921 nfsm_chain_add_32(error
, &nmrep
, NFS_V2MAXDATA
);
4922 nfsm_chain_add_32(error
, &nmrep
, blksize
);
4923 nfsm_chain_add_32(error
, &nmrep
, va
.f_blocks
);
4924 nfsm_chain_add_32(error
, &nmrep
, va
.f_bfree
);
4925 nfsm_chain_add_32(error
, &nmrep
, va
.f_bavail
);
4928 nfsm_chain_build_done(error
, &nmrep
);
4930 nfsm_chain_cleanup(&nmrep
);
4937 * nfs fsinfo service
4941 struct nfsrv_descript
*nd
,
4942 struct nfsrv_sock
*slp
,
4946 int error
, attrerr
, prefsize
, maxsize
;
4948 struct vnode_attr attr
;
4949 struct nfs_filehandle nfh
;
4950 struct nfs_export
*nx
;
4951 struct nfs_export_options
*nxo
;
4952 struct nfsm_chain
*nmreq
, nmrep
;
4956 nmreq
= &nd
->nd_nmreq
;
4957 nfsm_chain_null(&nmrep
);
4960 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4962 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4965 /* update export stats */
4966 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4968 /* update active user stats */
4969 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4971 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4974 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4975 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4982 /* assemble reply */
4983 nd
->nd_repstat
= error
;
4984 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3FSINFO
);
4986 *mrepp
= nmrep
.nmc_mhead
;
4987 nfsmout_on_status(nd
, error
);
4988 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4989 nfsmout_if(nd
->nd_repstat
);
4992 * XXX There should be file system VFS OP(s) to get this information.
4993 * For now, assume our usual NFS defaults.
4995 if (slp
->ns_sotype
== SOCK_DGRAM
) {
4996 maxsize
= NFS_MAXDGRAMDATA
;
4997 prefsize
= NFS_PREFDGRAMDATA
;
4999 maxsize
= prefsize
= slp
->ns_sobufsize
? slp
->ns_sobufsize
/ 2 : NFSRV_MAXDATA
;
5002 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
5003 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5004 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
5005 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
5006 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5007 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
5008 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5009 nfsm_chain_add_64(error
, &nmrep
, 0xffffffffffffffffULL
);
5010 nfsm_chain_add_32(error
, &nmrep
, 0);
5011 nfsm_chain_add_32(error
, &nmrep
, 1);
5012 /* XXX link/symlink support should be taken from volume capabilities */
5013 nfsm_chain_add_32(error
, &nmrep
,
5014 NFSV3FSINFO_LINK
| NFSV3FSINFO_SYMLINK
|
5015 NFSV3FSINFO_HOMOGENEOUS
| NFSV3FSINFO_CANSETTIME
);
5018 nfsm_chain_build_done(error
, &nmrep
);
5020 nfsm_chain_cleanup(&nmrep
);
5027 * nfs pathconf service
5031 struct nfsrv_descript
*nd
,
5032 struct nfsrv_sock
*slp
,
5036 int error
, attrerr
, linkmax
= 0, namemax
= 0;
5037 int chownres
= 0, notrunc
= 0, case_sensitive
= 0, case_preserving
= 0;
5039 struct vnode_attr attr
;
5040 struct nfs_filehandle nfh
;
5041 struct nfs_export
*nx
;
5042 struct nfs_export_options
*nxo
;
5043 struct nfsm_chain
*nmreq
, nmrep
;
5047 nmreq
= &nd
->nd_nmreq
;
5048 nfsm_chain_null(&nmrep
);
5051 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
5053 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
5056 /* update export stats */
5057 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
5059 /* update active user stats */
5060 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
5062 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
5065 error
= VNOP_PATHCONF(vp
, _PC_LINK_MAX
, &linkmax
, ctx
);
5067 error
= VNOP_PATHCONF(vp
, _PC_NAME_MAX
, &namemax
, ctx
);
5070 error
= VNOP_PATHCONF(vp
, _PC_CHOWN_RESTRICTED
, &chownres
, ctx
);
5073 error
= VNOP_PATHCONF(vp
, _PC_NO_TRUNC
, ¬runc
, ctx
);
5076 error
= VNOP_PATHCONF(vp
, _PC_CASE_SENSITIVE
, &case_sensitive
, ctx
);
5079 error
= VNOP_PATHCONF(vp
, _PC_CASE_PRESERVING
, &case_preserving
, ctx
);
5082 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
5083 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
5090 /* assemble reply */
5091 nd
->nd_repstat
= error
;
5092 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3PATHCONF
);
5094 *mrepp
= nmrep
.nmc_mhead
;
5095 nfsmout_on_status(nd
, error
);
5096 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
5097 nfsmout_if(nd
->nd_repstat
);
5099 nfsm_chain_add_32(error
, &nmrep
, linkmax
);
5100 nfsm_chain_add_32(error
, &nmrep
, namemax
);
5101 nfsm_chain_add_32(error
, &nmrep
, notrunc
);
5102 nfsm_chain_add_32(error
, &nmrep
, chownres
);
5103 nfsm_chain_add_32(error
, &nmrep
, !case_sensitive
);
5104 nfsm_chain_add_32(error
, &nmrep
, case_preserving
);
5107 nfsm_chain_build_done(error
, &nmrep
);
5109 nfsm_chain_cleanup(&nmrep
);
5116 * Null operation, used by clients to ping server
5121 struct nfsrv_descript
*nd
,
5122 struct nfsrv_sock
*slp
,
5123 __unused vfs_context_t ctx
,
5126 int error
= NFSERR_RETVOID
;
5127 struct nfsm_chain nmrep
;
5130 * RPCSEC_GSS context setup ?
5132 if (nd
->nd_gss_context
) {
5133 return nfs_gss_svc_ctx_init(nd
, slp
, mrepp
);
5136 nfsm_chain_null(&nmrep
);
5138 /* assemble reply */
5139 nd
->nd_repstat
= error
;
5140 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
5142 *mrepp
= nmrep
.nmc_mhead
;
5144 nfsm_chain_build_done(error
, &nmrep
);
5146 nfsm_chain_cleanup(&nmrep
);
5153 * No operation, used for obsolete procedures
5158 struct nfsrv_descript
*nd
,
5159 struct nfsrv_sock
*slp
,
5160 __unused vfs_context_t ctx
,
5164 struct nfsm_chain nmrep
;
5166 nfsm_chain_null(&nmrep
);
5168 if (nd
->nd_repstat
) {
5169 error
= nd
->nd_repstat
;
5171 error
= EPROCUNAVAIL
;
5174 /* assemble reply */
5175 nd
->nd_repstat
= error
;
5176 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
5178 *mrepp
= nmrep
.nmc_mhead
;
5180 nfsm_chain_build_done(error
, &nmrep
);
5182 nfsm_chain_cleanup(&nmrep
);
5188 const nfsrv_proc_t nfsrv_procs
[NFS_NPROCS
] = {
5215 * Perform access checking for vnodes obtained from file handles that would
5216 * refer to files already opened by a Unix client. You cannot just use
5217 * vnode_authorize() for two reasons.
5218 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5219 * 2 - The owner is to be given access irrespective of mode bits so that
5220 * processes that chmod after opening a file don't break. I don't like
5221 * this because it opens a security hole, but since the nfs server opens
5222 * a security hole the size of a barn door anyhow, what the heck.
5224 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5225 * will return EPERM instead of EACCESS. EPERM is always an error.
5232 kauth_action_t action
,
5234 struct nfs_export_options
*nxo
,
5237 struct vnode_attr vattr
;
5240 if (action
& KAUTH_VNODE_WRITE_RIGHTS
) {
5242 * Disallow write attempts on read-only exports;
5243 * unless the file is a socket or a block or character
5244 * device resident on the file system.
5246 if (nxo
->nxo_flags
& NX_READONLY
) {
5247 switch (vnode_vtype(vp
)) {
5248 case VREG
: case VDIR
: case VLNK
: case VCPLX
:
5255 error
= vnode_authorize(vp
, dvp
, action
, ctx
);
5257 * Allow certain operations for the owner (reads and writes
5258 * on files that are already open). Picking up from FreeBSD.
5260 if (override
&& (error
== EACCES
)) {
5262 VATTR_WANTED(&vattr
, va_uid
);
5263 if ((vnode_getattr(vp
, &vattr
, ctx
) == 0) &&
5264 (kauth_cred_getuid(vfs_context_ucred(ctx
)) == vattr
.va_uid
)) {
5271 #endif /* CONFIG_NFS_SERVER */