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 lck_grp_t
*nfsd_lck_grp
;
120 lck_mtx_t
*nfsd_mutex
;
121 struct nfsd_head nfsd_head
, nfsd_queue
;
123 lck_grp_t
*nfsrv_slp_rwlock_group
;
124 lck_grp_t
*nfsrv_slp_mutex_group
;
125 struct nfsrv_sockhead nfsrv_socklist
, nfsrv_sockwg
,
126 nfsrv_sockwait
, nfsrv_sockwork
;
127 struct nfsrv_sock
*nfsrv_udpsock
= NULL
;
128 struct nfsrv_sock
*nfsrv_udp6sock
= NULL
;
131 struct nfsrv_expfs_list nfsrv_exports
;
132 struct nfsrv_export_hashhead
*nfsrv_export_hashtbl
= NULL
;
133 int nfsrv_export_hash_size
= NFSRVEXPHASHSZ
;
134 u_long nfsrv_export_hash
;
135 lck_grp_t
*nfsrv_export_rwlock_group
;
136 lck_rw_t nfsrv_export_rwlock
;
139 /* NFS server file modification event generator */
140 struct nfsrv_fmod_hashhead
*nfsrv_fmod_hashtbl
;
141 u_long nfsrv_fmod_hash
;
142 lck_grp_t
*nfsrv_fmod_grp
;
143 lck_mtx_t
*nfsrv_fmod_mutex
;
144 static int nfsrv_fmod_timer_on
= 0;
145 int nfsrv_fsevents_enabled
= 1;
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_t
*nfsrv_active_user_mutex_group
;
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 nfsd mutex */
207 nfsd_lck_grp
= lck_grp_alloc_init("nfsd", LCK_GRP_ATTR_NULL
);
208 nfsd_mutex
= lck_mtx_alloc_init(nfsd_lck_grp
, LCK_ATTR_NULL
);
210 /* init slp rwlock */
211 nfsrv_slp_rwlock_group
= lck_grp_alloc_init("nfsrv-slp-rwlock", LCK_GRP_ATTR_NULL
);
212 nfsrv_slp_mutex_group
= lck_grp_alloc_init("nfsrv-slp-mutex", LCK_GRP_ATTR_NULL
);
214 /* init export data structures */
215 LIST_INIT(&nfsrv_exports
);
216 nfsrv_export_rwlock_group
= lck_grp_alloc_init("nfsrv-export-rwlock", LCK_GRP_ATTR_NULL
);
217 lck_rw_init(&nfsrv_export_rwlock
, nfsrv_export_rwlock_group
, LCK_ATTR_NULL
);
219 /* init active user list mutex structures */
220 nfsrv_active_user_mutex_group
= lck_grp_alloc_init("nfs-active-user-mutex", LCK_GRP_ATTR_NULL
);
222 /* init nfs server request cache mutex */
223 nfsrv_reqcache_lck_grp
= lck_grp_alloc_init("nfsrv_reqcache", LCK_GRP_ATTR_NULL
);
224 nfsrv_reqcache_mutex
= lck_mtx_alloc_init(nfsrv_reqcache_lck_grp
, LCK_ATTR_NULL
);
227 /* init NFS server file modified event generation */
228 nfsrv_fmod_hashtbl
= hashinit(NFSRVFMODHASHSZ
, M_TEMP
, &nfsrv_fmod_hash
);
229 nfsrv_fmod_grp
= lck_grp_alloc_init("nfsrv_fmod", LCK_GRP_ATTR_NULL
);
230 nfsrv_fmod_mutex
= lck_mtx_alloc_init(nfsrv_fmod_grp
, LCK_ATTR_NULL
);
233 /* initialize NFS server timer callouts */
235 nfsrv_fmod_timer_call
= thread_call_allocate(nfsrv_fmod_timer
, NULL
);
237 nfsrv_idlesock_timer_call
= thread_call_allocate(nfsrv_idlesock_timer
, NULL
);
238 nfsrv_wg_timer_call
= thread_call_allocate(nfsrv_wg_timer
, NULL
);
240 /* Init server data structures */
241 TAILQ_INIT(&nfsrv_socklist
);
242 TAILQ_INIT(&nfsrv_sockwait
);
243 TAILQ_INIT(&nfsrv_sockwork
);
244 TAILQ_INIT(&nfsrv_sockwg
);
245 TAILQ_INIT(&nfsd_head
);
246 TAILQ_INIT(&nfsd_queue
);
247 nfsrv_udpsock
= NULL
;
248 nfsrv_udp6sock
= NULL
;
250 /* Setup the up-call handling */
253 /* initialization complete */
254 nfsrv_initted
= NFSRV_INITIALIZED
;
260 * NFS version 2 and 3 server request processing functions
262 * These functions take the following parameters:
264 * struct nfsrv_descript *nd - the NFS request descriptor
265 * struct nfsrv_sock *slp - the NFS socket the request came in on
266 * vfs_context_t ctx - VFS context
267 * mbuf_t *mrepp - pointer to hold the reply mbuf list
269 * These routines generally have 3 phases:
271 * 1 - break down and validate the RPC request in the mbuf chain
272 * provided in nd->nd_nmreq.
273 * 2 - perform the vnode operations for the request
274 * (many are very similar to syscalls in vfs_syscalls.c and
275 * should therefore be kept in sync with those implementations)
276 * 3 - build the RPC reply in an mbuf chain (nmrep) and return the mbuf chain
281 * nfs v3 access service
285 struct nfsrv_descript
*nd
,
286 struct nfsrv_sock
*slp
,
290 struct nfsm_chain
*nmreq
, nmrep
;
293 struct vnode_attr vattr
;
294 struct nfs_filehandle nfh
;
296 kauth_action_t testaction
;
297 struct nfs_export
*nx
;
298 struct nfs_export_options
*nxo
;
303 nmreq
= &nd
->nd_nmreq
;
304 nfsm_chain_null(&nmrep
);
308 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
309 nfsm_chain_get_32(error
, nmreq
, nfsmode
);
311 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
314 /* update export stats */
315 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
317 /* update active user stats */
318 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
320 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
324 * Each NFS mode bit is tested separately.
326 * XXX this code is nominally correct, but returns a pessimistic
327 * rather than optimistic result. It will be necessary to add
328 * an NFS-specific interface to the vnode_authorize code to
329 * obtain good performance in the optimistic mode.
331 if (nfsmode
& NFS_ACCESS_READ
) {
332 testaction
= vnode_isdir(vp
) ? KAUTH_VNODE_LIST_DIRECTORY
: KAUTH_VNODE_READ_DATA
;
333 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
334 nfsmode
&= ~NFS_ACCESS_READ
;
337 if ((nfsmode
& NFS_ACCESS_LOOKUP
) &&
339 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_SEARCH
, ctx
, nxo
, 0))) {
340 nfsmode
&= ~NFS_ACCESS_LOOKUP
;
342 if (nfsmode
& NFS_ACCESS_MODIFY
) {
343 if (vnode_isdir(vp
)) {
345 KAUTH_VNODE_ADD_FILE
|
346 KAUTH_VNODE_ADD_SUBDIRECTORY
|
347 KAUTH_VNODE_DELETE_CHILD
;
350 KAUTH_VNODE_WRITE_DATA
;
352 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
353 nfsmode
&= ~NFS_ACCESS_MODIFY
;
356 if (nfsmode
& NFS_ACCESS_EXTEND
) {
357 if (vnode_isdir(vp
)) {
359 KAUTH_VNODE_ADD_FILE
|
360 KAUTH_VNODE_ADD_SUBDIRECTORY
;
363 KAUTH_VNODE_WRITE_DATA
|
364 KAUTH_VNODE_APPEND_DATA
;
366 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
367 nfsmode
&= ~NFS_ACCESS_EXTEND
;
372 * Note concerning NFS_ACCESS_DELETE:
373 * For hard links, the answer may be wrong if the vnode
374 * has multiple parents with different permissions.
375 * Also, some clients (e.g. MacOSX 10.3) may incorrectly
376 * interpret the missing/cleared DELETE bit.
377 * So we'll just leave the DELETE bit alone. At worst,
378 * we're telling the client it might be able to do
379 * something it really can't.
382 if ((nfsmode
& NFS_ACCESS_EXECUTE
) &&
384 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 0))) {
385 nfsmode
&= ~NFS_ACCESS_EXECUTE
;
388 /* get postop attributes */
389 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
390 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
394 nd
->nd_repstat
= error
;
395 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(NFS_VER3
) + NFSX_UNSIGNED
);
397 *mrepp
= nmrep
.nmc_mhead
;
398 nfsmout_on_status(nd
, error
);
399 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
400 if (!nd
->nd_repstat
) {
401 nfsm_chain_add_32(error
, &nmrep
, nfsmode
);
404 nfsm_chain_build_done(error
, &nmrep
);
409 nfsm_chain_cleanup(&nmrep
);
416 * nfs getattr service
420 struct nfsrv_descript
*nd
,
421 struct nfsrv_sock
*slp
,
425 struct nfsm_chain
*nmreq
, nmrep
;
426 struct vnode_attr vattr
;
429 struct nfs_filehandle nfh
;
430 struct nfs_export
*nx
;
431 struct nfs_export_options
*nxo
;
434 nmreq
= &nd
->nd_nmreq
;
435 nfsm_chain_null(&nmrep
);
439 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
441 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
444 /* update export stats */
445 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
447 /* update active user stats */
448 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
450 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
454 if (mac_vnode_check_open(ctx
, vp
, FREAD
)) {
460 nfsm_srv_vattr_init(&vattr
, nd
->nd_vers
);
461 error
= vnode_getattr(vp
, &vattr
, ctx
);
464 /* XXXab: Comment in the VFS code makes it sound like
465 * some arguments can be filtered out, but not
466 * what it actually means. Hopefully not like
467 * they gonna set mtime to 0 or something. For
468 * now trust there are no shenanigans here.
470 error
= mac_vnode_check_getattr(ctx
, NOCRED
, vp
, &vattr
);
479 nd
->nd_repstat
= error
;
480 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_FATTR(nd
->nd_vers
));
482 *mrepp
= nmrep
.nmc_mhead
;
483 nfsmout_if(nd
->nd_repstat
);
484 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &vattr
);
486 nfsm_chain_build_done(error
, &nmrep
);
491 nfsm_chain_cleanup(&nmrep
);
498 * nfs setattr service
502 struct nfsrv_descript
*nd
,
503 struct nfsrv_sock
*slp
,
507 struct nfsm_chain
*nmreq
, nmrep
;
508 struct vnode_attr preattr
, postattr
;
509 struct vnode_attr vattr
, *vap
= &vattr
;
511 struct nfs_export
*nx
;
512 struct nfs_export_options
*nxo
;
513 int error
, preattrerr
, postattrerr
, gcheck
;
514 struct nfs_filehandle nfh
;
515 struct timespec guard
= { .tv_sec
= 0, .tv_nsec
= 0 };
516 kauth_action_t action
;
520 preattrerr
= postattrerr
= ENOENT
;
522 nmreq
= &nd
->nd_nmreq
;
523 nfsm_chain_null(&nmrep
);
527 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
531 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
532 if (nd
->nd_vers
== NFS_VER3
) {
533 nfsm_chain_get_32(error
, nmreq
, gcheck
);
535 nfsm_chain_get_time(error
, nmreq
, nd
->nd_vers
, guard
.tv_sec
, guard
.tv_nsec
);
541 * Save the original credential UID in case they are
542 * mapped and we need to map the IDs in the attributes.
544 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
547 * Now that we have all the fields, lets do it.
549 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
552 /* update export stats */
553 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
555 /* update active user stats */
556 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
558 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
561 if (nd
->nd_vers
== NFS_VER3
) {
562 nfsm_srv_pre_vattr_init(&preattr
);
563 error
= preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
564 if (!error
&& gcheck
&& VATTR_IS_SUPPORTED(&preattr
, va_change_time
) &&
565 (preattr
.va_change_time
.tv_sec
!= guard
.tv_sec
||
566 preattr
.va_change_time
.tv_nsec
!= guard
.tv_nsec
)) {
567 error
= NFSERR_NOT_SYNC
;
569 if (!preattrerr
&& !VATTR_ALL_SUPPORTED(&preattr
)) {
576 * If the credentials were mapped, we should
577 * map the same values in the attributes.
579 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
581 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
582 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
) {
583 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
587 /* Authorize the attribute changes. */
588 error
= vnode_authattr(vp
, vap
, &action
, ctx
);
590 error
= nfsrv_authorize(vp
, NULL
, action
, ctx
, nxo
, 0);
594 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
| FWRITE
)) {
600 if (VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
)) {
601 error
= mac_vnode_check_setowner(ctx
, vp
,
602 VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: -1,
603 VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: -1);
606 if (!error
&& VATTR_IS_ACTIVE(vap
, va_mode
)) {
607 error
= mac_vnode_check_setmode(ctx
, vp
, (mode_t
)vap
->va_mode
);
610 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
611 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
612 error
= mac_vnode_check_truncate(ctx
, NOCRED
, vp
);
614 /* set utimes case */
615 if (!error
&& (VATTR_IS_ACTIVE(vap
, va_access_time
) || VATTR_IS_ACTIVE(vap
, va_modify_time
))) {
616 struct timespec current_time
;
617 nanotime(¤t_time
);
619 error
= mac_vnode_check_setutimes(ctx
, vp
,
620 VATTR_IS_ACTIVE(vap
, va_access_time
) ? vap
->va_access_time
: current_time
,
621 VATTR_IS_ACTIVE(vap
, va_modify_time
) ? vap
->va_modify_time
: current_time
);
625 /* set the new attributes */
627 error
= vnode_setattr(vp
, vap
, ctx
);
630 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
631 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
632 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
644 nd
->nd_repstat
= error
;
645 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCORFATTR(nd
->nd_vers
));
647 *mrepp
= nmrep
.nmc_mhead
;
648 nfsmout_on_status(nd
, error
);
649 if (nd
->nd_vers
== NFS_VER3
) {
650 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
651 preattrerr
, &preattr
, postattrerr
, &postattr
);
653 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
656 nfsm_chain_build_done(error
, &nmrep
);
658 nfsm_chain_cleanup(&nmrep
);
669 struct nfsrv_descript
*nd
,
670 struct nfsrv_sock
*slp
,
675 vnode_t vp
, dirp
= NULL
;
676 struct nfs_filehandle dnfh
, nfh
;
677 struct nfs_export
*nx
= NULL
;
678 struct nfs_export_options
*nxo
;
679 int error
, attrerr
, dirattrerr
, isdotdot
;
682 struct vnode_attr va
, dirattr
, *vap
= &va
;
683 struct nfsm_chain
*nmreq
, nmrep
;
686 attrerr
= dirattrerr
= ENOENT
;
687 nmreq
= &nd
->nd_nmreq
;
688 nfsm_chain_null(&nmrep
);
689 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
691 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
692 nfsm_chain_get_32(error
, nmreq
, len
);
693 nfsm_name_len_check(error
, nd
, len
);
696 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
698 ni
.ni_op
= OP_LOOKUP
;
700 ni
.ni_cnd
.cn_flags
= LOCKLEAF
;
701 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
702 isdotdot
= ((len
== 2) && (ni
.ni_cnd
.cn_pnbuf
[0] == '.') && (ni
.ni_cnd
.cn_pnbuf
[1] == '.'));
704 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
706 /* update export stats */
707 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
709 /* update active user stats */
710 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
712 if (!error
&& mac_vnode_check_open(ctx
, ni
.ni_vp
, FREAD
)) {
727 if (nd
->nd_vers
== NFS_VER3
) {
728 nfsm_srv_vattr_init(&dirattr
, NFS_VER3
);
729 dirattrerr
= vnode_getattr(dirp
, &dirattr
, ctx
);
738 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, (isdotdot
? &dnfh
: NULL
), vp
, ctx
, &nfh
);
740 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
741 attrerr
= vnode_getattr(vp
, vap
, ctx
);
747 nd
->nd_repstat
= error
;
748 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
749 NFSX_POSTOPORFATTR(nd
->nd_vers
) + NFSX_POSTOPATTR(nd
->nd_vers
));
751 *mrepp
= nmrep
.nmc_mhead
;
752 if (nd
->nd_repstat
) {
753 if (nd
->nd_vers
== NFS_VER3
) {
754 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
758 nfsm_chain_add_fh(error
, &nmrep
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
759 if (nd
->nd_vers
== NFS_VER3
) {
760 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
761 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
763 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
766 nfsm_chain_build_done(error
, &nmrep
);
768 nfsm_chain_cleanup(&nmrep
);
775 * nfs readlink service
779 struct nfsrv_descript
*nd
,
780 struct nfsrv_sock
*slp
,
784 int error
, mpcnt
, tlen
, len
, attrerr
;
786 struct vnode_attr vattr
;
787 struct nfs_filehandle nfh
;
788 struct nfs_export
*nx
;
789 struct nfs_export_options
*nxo
;
790 struct nfsm_chain
*nmreq
, nmrep
;
793 char uio_buf
[UIO_SIZEOF(4)];
794 char *uio_bufp
= &uio_buf
[0];
795 int uio_buflen
= UIO_SIZEOF(4);
799 nmreq
= &nd
->nd_nmreq
;
800 nfsm_chain_null(&nmrep
);
803 len
= NFS_MAXPATHLEN
;
805 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
808 /* get mbuf list to hold symlink path */
809 error
= nfsm_mbuf_get_list(len
, &mpath
, &mpcnt
);
812 uio_buflen
= UIO_SIZEOF(mpcnt
);
813 MALLOC(uio_bufp
, char*, uio_buflen
, M_TEMP
, M_WAITOK
);
819 auio
= uio_createwithbuffer(mpcnt
, 0, UIO_SYSSPACE
, UIO_READ
, uio_bufp
, uio_buflen
);
825 for (mp
= mpath
; mp
; mp
= mbuf_next(mp
)) {
826 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(mp
)), mbuf_len(mp
));
829 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
832 /* update export stats */
833 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
835 /* update active user stats */
836 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
838 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
841 if (vnode_vtype(vp
) != VLNK
) {
842 if (nd
->nd_vers
== NFS_VER3
) {
850 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 0);
853 if (mac_vnode_check_open(ctx
, vp
, FREAD
)) {
858 error
= mac_vnode_check_readlink(ctx
, vp
);
862 error
= VNOP_READLINK(vp
, auio
, ctx
);
865 if (nd
->nd_vers
== NFS_VER3
) {
866 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
867 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
879 nd
->nd_repstat
= error
;
880 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_UNSIGNED
);
882 *mrepp
= nmrep
.nmc_mhead
;
883 nfsmout_on_status(nd
, error
);
884 if (nd
->nd_vers
== NFS_VER3
) {
885 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
887 if (error
|| nd
->nd_repstat
) {
888 nfsm_chain_build_done(error
, &nmrep
);
891 if (auio
&& (uio_resid(auio
) > 0)) {
892 len
-= uio_resid(auio
);
893 tlen
= nfsm_rndup(len
);
894 nfsm_adj(mpath
, NFS_MAXPATHLEN
- tlen
, tlen
- len
);
896 nfsm_chain_add_32(error
, &nmrep
, len
);
897 nfsm_chain_build_done(error
, &nmrep
);
899 error
= mbuf_setnext(nmrep
.nmc_mcur
, mpath
);
910 if (uio_bufp
!= &uio_buf
[0]) {
911 FREE(uio_bufp
, M_TEMP
);
914 nfsm_chain_cleanup(&nmrep
);
925 struct nfsrv_descript
*nd
,
926 struct nfsrv_sock
*slp
,
930 int error
, attrerr
, mreadcnt
;
931 uint32_t reqlen
, maxlen
, count
, len
, tlen
;
934 struct nfs_filehandle nfh
;
935 struct nfs_export
*nx
= NULL
;
936 struct nfs_export_options
*nxo
;
938 char *uio_bufp
= NULL
;
939 struct vnode_attr vattr
, *vap
= &vattr
;
942 char uio_buf
[UIO_SIZEOF(0)];
943 struct nfsm_chain
*nmreq
, nmrep
;
947 nmreq
= &nd
->nd_nmreq
;
948 nfsm_chain_null(&nmrep
);
952 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
954 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
956 if (nd
->nd_vers
== NFS_VER3
) {
957 nfsm_chain_get_64(error
, nmreq
, off
);
959 nfsm_chain_get_32(error
, nmreq
, off
);
961 nfsm_chain_get_32(error
, nmreq
, reqlen
);
962 maxlen
= NFSRV_NDMAXDATA(nd
);
963 if (reqlen
> maxlen
) {
967 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
970 /* update export stats */
971 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
973 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
976 if (vnode_vtype(vp
) != VREG
) {
977 if (nd
->nd_vers
== NFS_VER3
) {
980 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
985 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 1))) {
986 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 1);
991 error
= mac_vnode_check_open(ctx
, vp
, FREAD
);
995 /* XXXab: Do we need to do this?! */
996 error
= mac_vnode_check_read(ctx
, vfs_context_ucred(ctx
), vp
);
1000 /* mac_vnode_check_exec() can't be done here. */
1005 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
1006 attrerr
= vnode_getattr(vp
, vap
, ctx
);
1012 if ((u_quad_t
)off
>= vap
->va_data_size
) {
1014 } else if (((u_quad_t
)off
+ reqlen
) > vap
->va_data_size
) {
1015 count
= (int)nfsm_rndup(vap
->va_data_size
- off
);
1022 /* get mbuf list to hold read data */
1023 error
= nfsm_mbuf_get_list(count
, &mread
, &mreadcnt
);
1025 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mreadcnt
), M_TEMP
, M_WAITOK
);
1027 auio
= uio_createwithbuffer(mreadcnt
, off
, UIO_SYSSPACE
,
1028 UIO_READ
, uio_bufp
, UIO_SIZEOF(mreadcnt
));
1030 if (!uio_bufp
|| !auio
) {
1034 for (m
= mread
; m
; m
= mbuf_next(m
)) {
1035 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mbuf_len(m
));
1037 error
= VNOP_READ(vp
, auio
, IO_NODELOCKED
, ctx
);
1039 auio
= uio_createwithbuffer(0, 0, UIO_SYSSPACE
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
1047 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
1048 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
1049 attrerr
= vnode_getattr(vp
, vap
, ctx
);
1050 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
1051 error
= attrerr
; /* NFSv2 must have attributes to return */
1059 /* trim off any data not actually read */
1060 len
-= uio_resid(auio
);
1061 tlen
= nfsm_rndup(len
);
1062 if (count
!= tlen
|| tlen
!= len
) {
1063 nfsm_adj(mread
, count
- tlen
, tlen
- len
);
1067 /* assemble reply */
1068 nd
->nd_repstat
= error
;
1069 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPORFATTR(nd
->nd_vers
) + 3 * NFSX_UNSIGNED
);
1071 *mrepp
= nmrep
.nmc_mhead
;
1072 nfsmout_on_status(nd
, error
);
1073 if (nd
->nd_vers
== NFS_VER3
) {
1074 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
1076 if (error
|| nd
->nd_repstat
) {
1077 nfsm_chain_build_done(error
, &nmrep
);
1080 if (nd
->nd_vers
== NFS_VER3
) {
1081 nfsm_chain_add_32(error
, &nmrep
, len
);
1082 nfsm_chain_add_32(error
, &nmrep
, (len
< reqlen
) ? TRUE
: FALSE
);
1084 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
1086 nfsm_chain_add_32(error
, &nmrep
, len
);
1087 nfsm_chain_build_done(error
, &nmrep
);
1089 error
= mbuf_setnext(nmrep
.nmc_mcur
, mread
);
1094 /* update export stats */
1095 NFSStatAdd64(&nx
->nx_stats
.bytes_read
, len
);
1097 /* update active user stats */
1098 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, len
, 0);
1106 if (uio_bufp
!= NULL
) {
1107 FREE(uio_bufp
, M_TEMP
);
1110 nfsm_chain_cleanup(&nmrep
);
1118 * NFS File modification reporting
1120 * When the contents of a file are changed, a "content modified"
1121 * fsevent needs to be issued. Normally this would be done at
1122 * file close time. This is difficult for NFS because the protocol
1123 * has no "close" operation. The client sends a stream of write
1124 * requests that just stop. So we keep a hash table full of
1125 * vnodes that have been written to recently, and issue a
1126 * "content modified" fsevent only if there are no writes to
1127 * a vnode for nfsrv_fmod_pendtime milliseconds.
1129 int nfsrv_fmod_pending
; /* count of vnodes being written to */
1130 int nfsrv_fmod_pendtime
= 1000; /* msec to wait */
1131 int nfsrv_fmod_min_interval
= 100; /* msec min interval between callbacks */
1134 * This function is called via the kernel's callout
1135 * mechanism. Calls are made only when there are
1136 * vnodes pending a fsevent creation, and no more
1137 * frequently than every nfsrv_fmod_min_interval ms.
1140 nfsrv_fmod_timer(__unused
void *param0
, __unused
void *param1
)
1142 struct nfsrv_fmod_hashhead
*headp
, firehead
;
1143 struct nfsrv_fmod
*fp
, *nfp
, *pfp
;
1144 uint64_t timenow
, next_deadline
;
1145 time_t interval
= 0;
1148 LIST_INIT(&firehead
);
1149 lck_mtx_lock(nfsrv_fmod_mutex
);
1151 clock_get_uptime(&timenow
);
1152 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000,
1156 * Scan all the hash chains
1159 for (i
= 0; i
< NFSRVFMODHASHSZ
; i
++) {
1161 * For each hash chain, look for an entry
1162 * that has exceeded the deadline.
1164 headp
= &nfsrv_fmod_hashtbl
[i
];
1165 LIST_FOREACH(fp
, headp
, fm_link
) {
1166 if (timenow
>= fp
->fm_deadline
) {
1169 if (fp
->fm_deadline
< next_deadline
) {
1170 next_deadline
= fp
->fm_deadline
;
1175 * If we have an entry that's exceeded the
1176 * deadline, then the same is true for all
1177 * following entries in the chain, since they're
1178 * sorted in time order.
1182 /* move each entry to the fire list */
1183 nfp
= LIST_NEXT(fp
, fm_link
);
1184 LIST_REMOVE(fp
, fm_link
);
1187 LIST_INSERT_AFTER(pfp
, fp
, fm_link
);
1189 LIST_INSERT_HEAD(&firehead
, fp
, fm_link
);
1197 lck_mtx_unlock(nfsrv_fmod_mutex
);
1199 * Fire off the content modified fsevent for each
1200 * entry and free it.
1202 LIST_FOREACH_SAFE(fp
, &firehead
, fm_link
, nfp
) {
1203 if (nfsrv_fsevents_enabled
) {
1204 fp
->fm_context
.vc_thread
= current_thread();
1205 add_fsevent(FSE_CONTENT_MODIFIED
, &fp
->fm_context
,
1206 FSE_ARG_VNODE
, fp
->fm_vp
,
1209 vnode_put(fp
->fm_vp
);
1210 kauth_cred_unref(&fp
->fm_context
.vc_ucred
);
1211 LIST_REMOVE(fp
, fm_link
);
1214 lck_mtx_lock(nfsrv_fmod_mutex
);
1215 nfsrv_fmod_pending
-= fmod_fire
;
1220 * If there are still pending entries, set up another
1221 * callout to handle them later. Set the timeout deadline
1222 * so that the callout happens when the oldest pending
1223 * entry is ready to send its fsevent.
1225 if (nfsrv_fmod_pending
> 0) {
1226 interval
= ((time_t)(next_deadline
- timenow
)) / (1000 * 1000);
1227 if (interval
< nfsrv_fmod_min_interval
) {
1228 interval
= nfsrv_fmod_min_interval
;
1232 nfsrv_fmod_timer_on
= interval
> 0;
1233 if (nfsrv_fmod_timer_on
) {
1234 nfs_interval_timer_start(nfsrv_fmod_timer_call
, interval
);
1237 lck_mtx_unlock(nfsrv_fmod_mutex
);
1241 * When a vnode has been written to, enter it in the hash
1242 * table of vnodes pending creation of an fsevent. If the
1243 * callout timer isn't already running, schedule a callback
1244 * for nfsrv_fmod_pendtime msec from now.
1247 nfsrv_modified(vnode_t vp
, vfs_context_t ctx
)
1250 struct nfsrv_fmod
*fp
;
1251 struct nfsrv_fmod_hashhead
*head
;
1253 lck_mtx_lock(nfsrv_fmod_mutex
);
1256 * Compute the time in the future when the
1257 * content modified fsevent is to be issued.
1259 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000, &deadline
);
1262 * Check if there's already a file content change fsevent
1263 * pending for this vnode. If there is, update its
1264 * timestamp and make sure it's at the front of the hash chain.
1266 head
= &nfsrv_fmod_hashtbl
[NFSRVFMODHASH(vp
)];
1267 LIST_FOREACH(fp
, head
, fm_link
) {
1268 if (vp
== fp
->fm_vp
) {
1269 fp
->fm_deadline
= deadline
;
1270 if (fp
!= LIST_FIRST(head
)) {
1271 LIST_REMOVE(fp
, fm_link
);
1272 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1274 lck_mtx_unlock(nfsrv_fmod_mutex
);
1280 * First content change fsevent for this vnode.
1281 * Allocate a new file mod entry and add it
1282 * on the front of the hash chain.
1284 if (vnode_get(vp
) != 0) {
1287 MALLOC(fp
, struct nfsrv_fmod
*, sizeof(*fp
), M_TEMP
, M_WAITOK
);
1293 kauth_cred_ref(vfs_context_ucred(ctx
));
1294 fp
->fm_context
= *ctx
;
1295 fp
->fm_deadline
= deadline
;
1296 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1299 * If added to an empty hash table, then set the
1300 * callout timer to go off after nfsrv_fmod_pendtime.
1302 nfsrv_fmod_pending
++;
1303 if (!nfsrv_fmod_timer_on
) {
1304 nfsrv_fmod_timer_on
= 1;
1305 nfs_interval_timer_start(nfsrv_fmod_timer_call
,
1306 nfsrv_fmod_pendtime
);
1309 lck_mtx_unlock(nfsrv_fmod_mutex
);
1312 #endif /* CONFIG_FSE */
1319 struct nfsrv_descript
*nd
,
1320 struct nfsrv_sock
*slp
,
1324 struct vnode_attr preattr
, postattr
;
1325 int error
, preattrerr
, postattrerr
;
1326 int ioflags
, len
, retlen
;
1328 int stable
= NFS_WRITE_FILESYNC
;
1331 struct nfs_filehandle nfh
;
1332 struct nfs_export
*nx
= NULL
;
1333 struct nfs_export_options
*nxo
;
1335 char *uio_bufp
= NULL
;
1338 struct nfsm_chain
*nmreq
, nmrep
;
1340 if (nd
->nd_nmreq
.nmc_mhead
== NULL
) {
1346 preattrerr
= postattrerr
= ENOENT
;
1347 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1348 nmreq
= &nd
->nd_nmreq
;
1349 nfsm_chain_null(&nmrep
);
1353 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
1355 if (nd
->nd_vers
== NFS_VER3
) {
1356 nfsm_chain_get_64(error
, nmreq
, off
);
1357 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1358 nfsm_chain_get_32(error
, nmreq
, stable
);
1360 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1361 nfsm_chain_get_32(error
, nmreq
, off
);
1362 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1364 stable
= NFS_WRITE_UNSTABLE
;
1367 nfsm_chain_get_32(error
, nmreq
, len
);
1372 * For NFS Version 2, it is not obvious what a write of zero length
1373 * should do, but I might as well be consistent with Version 3,
1374 * which is to return ok so long as there are no permission problems.
1378 error
= nfsm_chain_trim_data(nmreq
, len
, &mlen
);
1383 if ((len
> NFSRV_MAXDATA
) || (len
< 0) || (mlen
< len
)) {
1387 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
1390 /* update export stats */
1391 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1393 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1396 if (nd
->nd_vers
== NFS_VER3
) {
1397 nfsm_srv_pre_vattr_init(&preattr
);
1398 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1400 if (vnode_vtype(vp
) != VREG
) {
1401 if (nd
->nd_vers
== NFS_VER3
) {
1404 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1408 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1414 error
= mac_vnode_check_open(ctx
, vp
, FWRITE
);
1418 /* XXXab: Do we need to do this?! */
1419 error
= mac_vnode_check_write(ctx
, vfs_context_ucred(ctx
), vp
);
1429 for (mcount
= 0, m
= nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
)) {
1430 if (mbuf_len(m
) > 0) {
1434 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mcount
), M_TEMP
, M_WAITOK
);
1436 auio
= uio_createwithbuffer(mcount
, off
, UIO_SYSSPACE
, UIO_WRITE
, uio_bufp
, UIO_SIZEOF(mcount
));
1438 if (!uio_bufp
|| !auio
) {
1442 for (m
= nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
)) {
1443 if ((mlen
= (int)mbuf_len(m
)) > 0) {
1444 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mlen
);
1448 * XXX The IO_METASYNC flag indicates that all metadata (and not just
1449 * enough to ensure data integrity) mus be written to stable storage
1450 * synchronously. (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
1452 if (stable
== NFS_WRITE_UNSTABLE
) {
1453 ioflags
= IO_NODELOCKED
;
1454 } else if (stable
== NFS_WRITE_DATASYNC
) {
1455 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1457 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1460 error
= VNOP_WRITE(vp
, auio
, ioflags
, ctx
);
1461 OSAddAtomic64(1, &nfsstats
.srvvop_writes
);
1463 /* update export stats */
1464 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, len
);
1466 /* update active user stats */
1467 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, len
);
1470 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
)) {
1471 nfsrv_modified(vp
, ctx
);
1475 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1476 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1477 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
1478 error
= postattrerr
; /* NFSv2 must have attributes to return */
1484 /* assemble reply */
1485 nd
->nd_repstat
= error
;
1486 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1487 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1488 NFSX_WRITEVERF(nd
->nd_vers
));
1490 *mrepp
= nmrep
.nmc_mhead
;
1491 nfsmout_on_status(nd
, error
);
1492 if (nd
->nd_vers
== NFS_VER3
) {
1493 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1494 preattrerr
, &preattr
, postattrerr
, &postattr
);
1495 nfsmout_if(error
|| nd
->nd_repstat
);
1496 nfsm_chain_add_32(error
, &nmrep
, retlen
);
1497 /* If nfsrv_async is set, then pretend the write was FILESYNC. */
1498 if ((stable
== NFS_WRITE_UNSTABLE
) && !nfsrv_async
) {
1499 nfsm_chain_add_32(error
, &nmrep
, stable
);
1501 nfsm_chain_add_32(error
, &nmrep
, NFS_WRITE_FILESYNC
);
1503 /* write verifier */
1504 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1505 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1507 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1510 nfsm_chain_build_done(error
, &nmrep
);
1514 if (uio_bufp
!= NULL
) {
1515 FREE(uio_bufp
, M_TEMP
);
1518 nfsm_chain_cleanup(&nmrep
);
1525 * NFS write service with write gathering support. Called when
1526 * nfsrv_wg_delay > 0.
1527 * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
1528 * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
1532 #define NWDELAYHASH(sock, f) \
1533 (&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ])
1534 /* These macros compare nfsrv_descript structures. */
1535 #define NFSW_CONTIG(o, n) \
1536 (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh))
1538 * XXX The following is an incorrect comparison; it fails to take into account
1539 * XXX scoping of MAC labels, but we currently lack KPI for credential
1542 #define NFSW_SAMECRED(o, n) \
1543 (!bcmp((caddr_t)(o)->nd_cr, (caddr_t)(n)->nd_cr, \
1544 sizeof (struct ucred)))
1548 struct nfsrv_descript
**ndp
,
1549 struct nfsrv_sock
*slp
,
1553 struct nfsrv_descript
*nd
, *wp
, *owp
, *swp
;
1554 struct nfs_export
*nx
;
1555 struct nfs_export_options
*nxo
;
1556 struct nfsrv_wg_delayhash
*wpp
;
1558 struct vnode_attr preattr
, postattr
;
1559 int error
, mlen
, i
, ioflags
;
1561 int preattrerr
, postattrerr
;
1565 char *uio_bufp
= NULL
;
1568 struct nfsm_chain
*nmreq
, nmrep
;
1571 preattrerr
= postattrerr
= ENOENT
;
1572 nfsm_chain_null(&nmrep
);
1579 nmreq
= &nd
->nd_nmreq
;
1580 LIST_INIT(&nd
->nd_coalesce
);
1582 nd
->nd_stable
= NFS_WRITE_FILESYNC
;
1584 cur_usec
= now
.tv_sec
* 1000000 + now
.tv_usec
;
1585 nd
->nd_time
= cur_usec
+
1586 ((nd
->nd_vers
== NFS_VER3
) ? nfsrv_wg_delay_v3
: nfsrv_wg_delay
);
1588 /* Now, get the write header... */
1589 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nd
->nd_fh
.nfh_fhp
, nd
->nd_fh
.nfh_len
);
1590 /* XXX shouldn't we be checking for invalid FHs before doing any more work? */
1592 if (nd
->nd_vers
== NFS_VER3
) {
1593 nfsm_chain_get_64(error
, nmreq
, nd
->nd_off
);
1594 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1595 nfsm_chain_get_32(error
, nmreq
, nd
->nd_stable
);
1597 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1598 nfsm_chain_get_32(error
, nmreq
, nd
->nd_off
);
1599 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1601 nd
->nd_stable
= NFS_WRITE_UNSTABLE
;
1604 nfsm_chain_get_32(error
, nmreq
, nd
->nd_len
);
1606 nd
->nd_eoff
= nd
->nd_off
+ nd
->nd_len
;
1608 if (nd
->nd_len
> 0) {
1609 error
= nfsm_chain_trim_data(nmreq
, nd
->nd_len
, &mlen
);
1615 if ((nd
->nd_len
> NFSRV_MAXDATA
) || (nd
->nd_len
< 0) || (mlen
< nd
->nd_len
)) {
1618 nd
->nd_repstat
= error
;
1619 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1621 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1622 if (nd
->nd_vers
== NFS_VER3
) {
1623 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1624 preattrerr
, &preattr
, postattrerr
, &postattr
);
1627 nfsm_chain_build_done(error
, &nmrep
);
1632 * Add this entry to the hash and time queues.
1634 lck_mtx_lock(&slp
->ns_wgmutex
);
1636 wp
= slp
->ns_tq
.lh_first
;
1637 while (wp
&& wp
->nd_time
< nd
->nd_time
) {
1639 wp
= wp
->nd_tq
.le_next
;
1642 LIST_INSERT_AFTER(owp
, nd
, nd_tq
);
1644 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1647 wpp
= NWDELAYHASH(slp
, nd
->nd_fh
.nfh_fid
);
1650 while (wp
&& !nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1652 wp
= wp
->nd_hash
.le_next
;
1654 while (wp
&& (wp
->nd_off
< nd
->nd_off
) &&
1655 nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1657 wp
= wp
->nd_hash
.le_next
;
1660 LIST_INSERT_AFTER(owp
, nd
, nd_hash
);
1662 * Search the hash list for overlapping entries and
1665 for (; nd
&& NFSW_CONTIG(owp
, nd
); nd
= wp
) {
1666 wp
= nd
->nd_hash
.le_next
;
1667 if (NFSW_SAMECRED(owp
, nd
)) {
1668 nfsrv_wg_coalesce(owp
, nd
);
1672 LIST_INSERT_HEAD(wpp
, nd
, nd_hash
);
1676 lck_mtx_lock(&slp
->ns_wgmutex
);
1680 * Now, do VNOP_WRITE()s for any one(s) that need to be done now
1681 * and generate the associated reply mbuf list(s).
1685 cur_usec
= now
.tv_sec
* 1000000 + now
.tv_usec
;
1686 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= owp
) {
1687 owp
= nd
->nd_tq
.le_next
;
1688 if (nd
->nd_time
> cur_usec
) {
1694 LIST_REMOVE(nd
, nd_tq
);
1695 LIST_REMOVE(nd
, nd_hash
);
1696 nmreq
= &nd
->nd_nmreq
;
1697 preattrerr
= postattrerr
= ENOENT
;
1699 /* save the incoming uid before mapping, */
1700 /* for updating active user stats later */
1701 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1703 error
= nfsrv_fhtovp(&nd
->nd_fh
, nd
, &vp
, &nx
, &nxo
);
1705 /* update per-export stats */
1706 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1708 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1714 if (nd
->nd_vers
== NFS_VER3
) {
1715 nfsm_srv_pre_vattr_init(&preattr
);
1716 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1718 if (vnode_vtype(vp
) != VREG
) {
1719 if (nd
->nd_vers
== NFS_VER3
) {
1722 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1729 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1732 if (nd
->nd_stable
== NFS_WRITE_UNSTABLE
) {
1733 ioflags
= IO_NODELOCKED
;
1734 } else if (nd
->nd_stable
== NFS_WRITE_DATASYNC
) {
1735 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1737 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1740 if (!error
&& ((nd
->nd_eoff
- nd
->nd_off
) > 0)) {
1741 for (i
= 0, m
= nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
)) {
1742 if (mbuf_len(m
) > 0) {
1747 MALLOC(uio_bufp
, char *, UIO_SIZEOF(i
), M_TEMP
, M_WAITOK
);
1749 auio
= uio_createwithbuffer(i
, nd
->nd_off
, UIO_SYSSPACE
,
1750 UIO_WRITE
, uio_bufp
, UIO_SIZEOF(i
));
1752 if (!uio_bufp
|| !auio
) {
1756 for (m
= nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
)) {
1757 if ((tlen
= mbuf_len(m
)) > 0) {
1758 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), tlen
);
1761 error
= VNOP_WRITE(vp
, auio
, ioflags
, ctx
);
1762 OSAddAtomic64(1, &nfsstats
.srvvop_writes
);
1764 /* update export stats */
1765 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, nd
->nd_len
);
1766 /* update active user stats */
1767 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, nd
->nd_len
);
1770 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
)) {
1771 nfsrv_modified(vp
, ctx
);
1776 FREE(uio_bufp
, M_TEMP
);
1781 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1782 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1787 * Loop around generating replies for all write rpcs that have
1788 * now been completed.
1793 nd
->nd_repstat
= error
;
1794 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1795 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1796 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1797 preattrerr
, &preattr
, postattrerr
, &postattr
);
1800 nd
->nd_repstat
= error
;
1801 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1802 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1803 NFSX_WRITEVERF(nd
->nd_vers
));
1804 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1805 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1806 preattrerr
, &preattr
, postattrerr
, &postattr
);
1807 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_len
);
1808 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_stable
);
1809 /* write verifier */
1810 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1811 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1812 } else if (!error
) {
1813 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1816 nfsm_chain_build_done(error
, &nmrep
);
1818 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1821 * Done. Put it at the head of the timer queue so that
1822 * the final phase can return the reply.
1826 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1828 nd
= swp
->nd_coalesce
.lh_first
;
1830 LIST_REMOVE(nd
, nd_tq
);
1834 LIST_INSERT_HEAD(&slp
->ns_tq
, swp
, nd_tq
);
1839 * Search for a reply to return.
1841 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= nd
->nd_tq
.le_next
) {
1843 LIST_REMOVE(nd
, nd_tq
);
1844 *mrepp
= nd
->nd_mrep
;
1849 slp
->ns_wgtime
= slp
->ns_tq
.lh_first
? slp
->ns_tq
.lh_first
->nd_time
: 0;
1850 lck_mtx_unlock(&slp
->ns_wgmutex
);
1853 * If we've just created a write pending gather,
1854 * start the timer to check on it soon to make sure
1855 * the write will be completed.
1857 * Add/Remove the socket in the nfsrv_sockwg queue as needed.
1859 lck_mtx_lock(nfsd_mutex
);
1860 if (slp
->ns_wgtime
) {
1861 if (slp
->ns_wgq
.tqe_next
== SLPNOLIST
) {
1862 TAILQ_INSERT_HEAD(&nfsrv_sockwg
, slp
, ns_wgq
);
1864 if (!nfsrv_wg_timer_on
) {
1865 nfsrv_wg_timer_on
= 1;
1866 nfs_interval_timer_start(nfsrv_wg_timer_call
,
1867 NFSRV_WGATHERDELAY
);
1869 } else if (slp
->ns_wgq
.tqe_next
!= SLPNOLIST
) {
1870 TAILQ_REMOVE(&nfsrv_sockwg
, slp
, ns_wgq
);
1871 slp
->ns_wgq
.tqe_next
= SLPNOLIST
;
1873 lck_mtx_unlock(nfsd_mutex
);
1879 * Coalesce the write request nd into owp. To do this we must:
1880 * - remove nd from the queues
1881 * - merge nd->nd_nmreq into owp->nd_nmreq
1882 * - update the nd_eoff and nd_stable for owp
1883 * - put nd on owp's nd_coalesce list
1886 nfsrv_wg_coalesce(struct nfsrv_descript
*owp
, struct nfsrv_descript
*nd
)
1891 struct nfsrv_descript
*p
;
1893 LIST_REMOVE(nd
, nd_hash
);
1894 LIST_REMOVE(nd
, nd_tq
);
1895 if (owp
->nd_eoff
< nd
->nd_eoff
) {
1896 overlap
= owp
->nd_eoff
- nd
->nd_off
;
1901 mbuf_adj(nd
->nd_nmreq
.nmc_mhead
, (int)overlap
);
1903 mp
= owp
->nd_nmreq
.nmc_mhead
;
1904 while ((mpnext
= mbuf_next(mp
))) {
1907 error
= mbuf_setnext(mp
, nd
->nd_nmreq
.nmc_mhead
);
1911 owp
->nd_eoff
= nd
->nd_eoff
;
1913 mbuf_freem(nd
->nd_nmreq
.nmc_mhead
);
1915 nd
->nd_nmreq
.nmc_mhead
= NULL
;
1916 nd
->nd_nmreq
.nmc_mcur
= NULL
;
1917 if (nd
->nd_stable
== NFS_WRITE_FILESYNC
) {
1918 owp
->nd_stable
= NFS_WRITE_FILESYNC
;
1919 } else if ((nd
->nd_stable
== NFS_WRITE_DATASYNC
) &&
1920 (owp
->nd_stable
== NFS_WRITE_UNSTABLE
)) {
1921 owp
->nd_stable
= NFS_WRITE_DATASYNC
;
1923 LIST_INSERT_HEAD(&owp
->nd_coalesce
, nd
, nd_tq
);
1926 * If nd had anything else coalesced into it, transfer them
1927 * to owp, otherwise their replies will never get sent.
1929 while ((p
= nd
->nd_coalesce
.lh_first
)) {
1930 LIST_REMOVE(p
, nd_tq
);
1931 LIST_INSERT_HEAD(&owp
->nd_coalesce
, p
, nd_tq
);
1937 * Scan the write gathering queues for writes that need to be
1941 nfsrv_wg_timer(__unused
void *param0
, __unused
void *param1
)
1944 time_t cur_usec
, next_usec
;
1946 struct nfsrv_sock
*slp
;
1947 int writes_pending
= 0;
1950 cur_usec
= now
.tv_sec
* 1000000 + now
.tv_usec
;
1951 next_usec
= cur_usec
+ (NFSRV_WGATHERDELAY
* 1000);
1953 lck_mtx_lock(nfsd_mutex
);
1954 TAILQ_FOREACH(slp
, &nfsrv_sockwg
, ns_wgq
) {
1955 if (slp
->ns_wgtime
) {
1957 if (slp
->ns_wgtime
<= cur_usec
) {
1958 lck_rw_lock_exclusive(&slp
->ns_rwlock
);
1959 slp
->ns_flag
|= SLP_DOWRITES
;
1960 lck_rw_done(&slp
->ns_rwlock
);
1961 nfsrv_wakenfsd(slp
);
1964 if (slp
->ns_wgtime
< next_usec
) {
1965 next_usec
= slp
->ns_wgtime
;
1970 if (writes_pending
== 0) {
1971 nfsrv_wg_timer_on
= 0;
1972 lck_mtx_unlock(nfsd_mutex
);
1975 lck_mtx_unlock(nfsd_mutex
);
1978 * Return the number of msec to wait again
1980 interval
= (next_usec
- cur_usec
) / 1000;
1984 nfs_interval_timer_start(nfsrv_wg_timer_call
, interval
);
1988 * Sort the group list in increasing numerical order.
1989 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1990 * that used to be here.)
1993 nfsrv_group_sort(gid_t
*list
, int num
)
1998 /* Insertion sort. */
1999 for (i
= 1; i
< num
; i
++) {
2001 /* find correct slot for value v, moving others up */
2002 for (j
= i
; --j
>= 0 && v
< list
[j
];) {
2003 list
[j
+ 1] = list
[j
];
2010 * nfs create service
2011 * now does a truncate to 0 length via. setattr if it already exists
2015 struct nfsrv_descript
*nd
,
2016 struct nfsrv_sock
*slp
,
2020 struct vnode_attr dpreattr
, dpostattr
, postattr
;
2021 struct vnode_attr va
, *vap
= &va
;
2022 struct nameidata ni
;
2023 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
2024 int how
, exclusive_flag
;
2025 uint32_t len
= 0, cnflags
;
2027 vnode_t vp
, dvp
, dirp
;
2028 struct nfs_filehandle nfh
;
2029 struct nfs_export
*nx
= NULL
;
2030 struct nfs_export_options
*nxo
= NULL
;
2032 u_char cverf
[NFSX_V3CREATEVERF
];
2034 struct nfsm_chain
*nmreq
, nmrep
;
2037 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
2038 nmreq
= &nd
->nd_nmreq
;
2039 nfsm_chain_null(&nmrep
);
2040 vp
= dvp
= dirp
= NULL
;
2042 ni
.ni_cnd
.cn_nameiop
= 0;
2045 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2047 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2048 nfsm_chain_get_32(error
, nmreq
, len
);
2049 nfsm_name_len_check(error
, nd
, len
);
2052 ni
.ni_cnd
.cn_nameiop
= CREATE
;
2056 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2057 ni
.ni_cnd
.cn_ndp
= &ni
;
2059 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2061 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2063 /* update export stats */
2064 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2066 /* update active user stats */
2067 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2071 if (nd
->nd_vers
== NFS_VER3
) {
2072 nfsm_srv_pre_vattr_init(&dpreattr
);
2073 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2081 ni
.ni_cnd
.cn_nameiop
= 0;
2089 if (nd
->nd_vers
== NFS_VER3
) {
2090 nfsm_chain_get_32(error
, nmreq
, how
);
2093 case NFS_CREATE_GUARDED
:
2099 case NFS_CREATE_UNCHECKED
:
2100 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2102 case NFS_CREATE_EXCLUSIVE
:
2103 nfsm_chain_get_opaque(error
, nmreq
, NFSX_V3CREATEVERF
, cverf
);
2106 VATTR_SET(vap
, va_mode
, 0);
2111 VATTR_SET(vap
, va_type
, VREG
);
2115 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2117 v_type
= vap
->va_type
;
2118 if (v_type
== VNON
) {
2121 VATTR_SET(vap
, va_type
, v_type
);
2127 rdev
= vap
->va_data_size
;
2128 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2138 * If it doesn't exist, create it
2139 * otherwise just truncate to 0 length
2140 * should I set the mode too ??
2143 kauth_acl_t xacl
= NULL
;
2145 /* authorize before creating */
2146 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
2148 /* construct ACL and handle inheritance */
2150 error
= kauth_acl_inherit(dvp
,
2156 if (!error
&& xacl
!= NULL
) {
2157 VATTR_SET(vap
, va_acl
, xacl
);
2160 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2161 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
2163 * Server policy is to alway use the mapped rpc credential for
2164 * file system object creation. This has the nice side effect of
2165 * enforcing BSD creation semantics
2167 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2168 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2170 /* validate new-file security information */
2172 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2176 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
2182 if (vap
->va_type
== VREG
|| vap
->va_type
== VSOCK
) {
2184 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2187 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
2189 * If some of the requested attributes weren't handled by the VNOP,
2190 * use our fallback code.
2192 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
2196 kauth_acl_free(xacl
);
2200 if (exclusive_flag
) {
2203 bcopy(cverf
, (caddr_t
)&vap
->va_access_time
,
2205 VATTR_SET_ACTIVE(vap
, va_access_time
);
2206 // skip authorization, as this is an
2207 // NFS internal implementation detail.
2208 error
= vnode_setattr(vp
, vap
, ctx
);
2212 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_CREATE_FILE
, vp
)) {
2213 add_fsevent(FSE_CREATE_FILE
, ctx
,
2219 } else if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
||
2220 vap
->va_type
== VFIFO
) {
2221 if (vap
->va_type
== VCHR
&& rdev
== 0xffffffff) {
2222 VATTR_SET(vap
, va_type
, VFIFO
);
2224 if (vap
->va_type
!= VFIFO
) {
2225 error
= suser(nd
->nd_cr
, NULL
);
2228 VATTR_SET(vap
, va_rdev
, (dev_t
)rdev
);
2230 error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2233 kauth_acl_free(xacl
);
2243 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2245 ni
.ni_op
= OP_LOOKUP
;
2247 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2248 ni
.ni_cnd
.cn_context
= ctx
;
2249 ni
.ni_startdir
= dvp
;
2251 ni
.ni_rootdir
= rootvnode
;
2252 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
2253 while ((error
= lookup(&ni
)) == ERECYCLE
) {
2254 ni
.ni_cnd
.cn_flags
= cnflags
;
2255 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
2256 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
2259 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
) {
2269 * nameidone has to happen before we vnode_put(dvp)
2270 * since it may need to release the fs_nodelock on the dvp
2273 ni
.ni_cnd
.cn_nameiop
= 0;
2278 * nameidone has to happen before we vnode_put(dvp)
2279 * since it may need to release the fs_nodelock on the dvp
2282 ni
.ni_cnd
.cn_nameiop
= 0;
2287 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2288 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
2289 error
= mac_vnode_check_truncate(ctx
, NOCRED
, vp
);
2295 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2296 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
,
2299 tempsize
= vap
->va_data_size
;
2301 VATTR_SET(vap
, va_data_size
, tempsize
);
2302 error
= vnode_setattr(vp
, vap
, ctx
);
2307 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
2309 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
2310 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2311 if (nd
->nd_vers
== NFS_VER2
) {
2312 error
= postattrerr
;
2320 if (nd
->nd_vers
== NFS_VER3
) {
2321 if (exclusive_flag
&& !error
&&
2322 bcmp(cverf
, &postattr
.va_access_time
, NFSX_V3CREATEVERF
)) {
2325 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2326 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2332 /* assemble reply */
2333 nd
->nd_repstat
= error
;
2334 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
2335 NFSX_FATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
2337 *mrepp
= nmrep
.nmc_mhead
;
2338 nfsmout_on_status(nd
, error
);
2339 if (nd
->nd_vers
== NFS_VER3
) {
2340 if (!nd
->nd_repstat
) {
2341 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2342 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2344 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2345 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2347 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2349 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
2353 nfsm_chain_build_done(error
, &nmrep
);
2354 if (ni
.ni_cnd
.cn_nameiop
) {
2356 * nameidone has to happen before we vnode_put(dvp)
2357 * since it may need to release the fs_nodelock on the dvp
2370 nfsm_chain_cleanup(&nmrep
);
2377 * nfs v3 mknod service
2381 struct nfsrv_descript
*nd
,
2382 struct nfsrv_sock
*slp
,
2386 struct vnode_attr dpreattr
, dpostattr
, postattr
;
2387 struct vnode_attr va
, *vap
= &va
;
2388 struct nameidata ni
;
2389 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
2390 uint32_t len
= 0, cnflags
;
2391 u_int32_t major
= 0, minor
= 0;
2394 vnode_t vp
, dvp
, dirp
;
2395 struct nfs_filehandle nfh
;
2396 struct nfs_export
*nx
= NULL
;
2397 struct nfs_export_options
*nxo
= NULL
;
2399 kauth_acl_t xacl
= NULL
;
2400 struct nfsm_chain
*nmreq
, nmrep
;
2403 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
2404 nmreq
= &nd
->nd_nmreq
;
2405 nfsm_chain_null(&nmrep
);
2406 vp
= dvp
= dirp
= NULL
;
2407 ni
.ni_cnd
.cn_nameiop
= 0;
2409 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2411 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2412 nfsm_chain_get_32(error
, nmreq
, len
);
2413 nfsm_name_len_check(error
, nd
, len
);
2416 ni
.ni_cnd
.cn_nameiop
= CREATE
;
2420 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2421 ni
.ni_cnd
.cn_ndp
= &ni
;
2422 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2424 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2426 /* update export stats */
2427 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2429 /* update active user stats */
2430 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2434 nfsm_srv_pre_vattr_init(&dpreattr
);
2435 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2438 ni
.ni_cnd
.cn_nameiop
= 0;
2445 nfsm_chain_get_32(error
, nmreq
, nvtype
);
2447 vtyp
= nfstov_type(nvtype
, NFS_VER3
);
2448 if (!error
&& (vtyp
!= VCHR
) && (vtyp
!= VBLK
) && (vtyp
!= VSOCK
) && (vtyp
!= VFIFO
)) {
2449 error
= NFSERR_BADTYPE
;
2454 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2455 if ((vtyp
== VCHR
) || (vtyp
== VBLK
)) {
2456 nfsm_chain_get_32(error
, nmreq
, major
);
2457 nfsm_chain_get_32(error
, nmreq
, minor
);
2459 VATTR_SET(vap
, va_rdev
, makedev(major
, minor
));
2464 * If it doesn't exist, create it.
2470 VATTR_SET(vap
, va_type
, vtyp
);
2472 /* authorize before creating */
2473 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
2475 /* construct ACL and handle inheritance */
2477 error
= kauth_acl_inherit(dvp
,
2483 if (!error
&& xacl
!= NULL
) {
2484 VATTR_SET(vap
, va_acl
, xacl
);
2487 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2488 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
2490 * Server policy is to alway use the mapped rpc credential for
2491 * file system object creation. This has the nice side effect of
2492 * enforcing BSD creation semantics
2494 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2495 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2497 /* validate new-file security information */
2499 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2502 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
2511 if (vtyp
== VSOCK
) {
2512 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2514 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
2516 * If some of the requested attributes weren't handled by the VNOP,
2517 * use our fallback code.
2519 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
2522 if (vtyp
!= VFIFO
&& (error
= suser(nd
->nd_cr
, (u_short
*)0))) {
2525 if ((error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
))) {
2533 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2535 ni
.ni_op
= OP_LOOKUP
;
2537 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2538 ni
.ni_cnd
.cn_context
= vfs_context_current();
2539 ni
.ni_startdir
= dvp
;
2541 ni
.ni_rootdir
= rootvnode
;
2542 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
2543 while ((error
= lookup(&ni
)) == ERECYCLE
) {
2544 ni
.ni_cnd
.cn_flags
= cnflags
;
2545 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
2546 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
2550 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
) {
2557 kauth_acl_free(xacl
);
2561 * nameidone has to happen before we vnode_put(dvp)
2562 * since it may need to release the fs_nodelock on the dvp
2565 ni
.ni_cnd
.cn_nameiop
= 0;
2571 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
2573 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
2574 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2582 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2583 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2588 /* assemble reply */
2589 nd
->nd_repstat
= error
;
2590 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(NFS_VER3
, &nfh
) +
2591 NFSX_POSTOPATTR(NFS_VER3
) + NFSX_WCCDATA(NFS_VER3
));
2593 *mrepp
= nmrep
.nmc_mhead
;
2594 nfsmout_on_status(nd
, error
);
2595 if (!nd
->nd_repstat
) {
2596 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2597 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2599 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2600 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2602 nfsm_chain_build_done(error
, &nmrep
);
2603 if (ni
.ni_cnd
.cn_nameiop
) {
2605 * nameidone has to happen before we vnode_put(dvp)
2606 * since it may need to release the fs_nodelock on the dvp
2625 nfsm_chain_cleanup(&nmrep
);
2632 * nfs remove service
2636 struct nfsrv_descript
*nd
,
2637 struct nfsrv_sock
*slp
,
2641 struct nameidata ni
;
2642 int error
, dpreattrerr
, dpostattrerr
;
2645 vnode_t vp
, dvp
, dirp
= NULL
;
2646 struct vnode_attr dpreattr
, dpostattr
;
2647 struct nfs_filehandle nfh
;
2648 struct nfs_export
*nx
= NULL
;
2649 struct nfs_export_options
*nxo
= NULL
;
2650 struct nfsm_chain
*nmreq
, nmrep
;
2653 dpreattrerr
= dpostattrerr
= ENOENT
;
2654 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2655 dvp
= vp
= dirp
= NULL
;
2656 nmreq
= &nd
->nd_nmreq
;
2657 nfsm_chain_null(&nmrep
);
2659 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2660 nfsm_chain_get_32(error
, nmreq
, len
);
2661 nfsm_name_len_check(error
, nd
, len
);
2664 ni
.ni_cnd
.cn_nameiop
= DELETE
;
2666 ni
.ni_op
= OP_UNLINK
;
2668 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2669 ni
.ni_cnd
.cn_ndp
= &ni
;
2670 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2672 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2674 /* update export stats */
2675 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2677 /* update active user stats */
2678 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2682 if (nd
->nd_vers
== NFS_VER3
) {
2683 nfsm_srv_pre_vattr_init(&dpreattr
);
2684 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2695 if (vnode_vtype(vp
) == VDIR
) {
2696 error
= EPERM
; /* POSIX */
2697 } else if (vnode_isvroot(vp
)) {
2699 * The root of a mounted filesystem cannot be deleted.
2703 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
2707 error
= vn_authorize_unlink(dvp
, vp
, &ni
.ni_cnd
, ctx
, NULL
);
2719 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
2721 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
2722 get_fse_info(vp
, &finfo
, ctx
);
2724 release_pathbuff(path
);
2729 error
= VNOP_REMOVE(dvp
, vp
, &ni
.ni_cnd
, 0, ctx
);
2734 add_fsevent(FSE_DELETE
, ctx
,
2735 FSE_ARG_STRING
, plen
, path
,
2736 FSE_ARG_FINFO
, &finfo
,
2739 release_pathbuff(path
);
2745 * nameidone has to happen before we vnode_put(dvp)
2746 * since it may need to release the fs_nodelock on the dvp
2756 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
2757 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2761 /* assemble reply */
2762 nd
->nd_repstat
= error
;
2763 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
2765 *mrepp
= nmrep
.nmc_mhead
;
2766 nfsmout_on_status(nd
, error
);
2767 if (nd
->nd_vers
== NFS_VER3
) {
2768 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2769 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2772 nfsm_chain_build_done(error
, &nmrep
);
2774 nfsm_chain_cleanup(&nmrep
);
2781 * nfs rename service
2785 struct nfsrv_descript
*nd
,
2786 struct nfsrv_sock
*slp
,
2790 kauth_cred_t saved_cred
= NULL
;
2793 uint32_t fromlen
, tolen
;
2794 int fdpreattrerr
, fdpostattrerr
;
2795 int tdpreattrerr
, tdpostattrerr
;
2796 char *frompath
= NULL
, *topath
= NULL
;
2797 struct nameidata fromni
, toni
;
2798 vnode_t fvp
, tvp
, tdvp
, fdvp
, fdirp
, tdirp
;
2799 struct vnode_attr fdpreattr
, fdpostattr
;
2800 struct vnode_attr tdpreattr
, tdpostattr
;
2801 struct nfs_filehandle fnfh
, tnfh
;
2802 struct nfs_export
*fnx
, *tnx
;
2803 struct nfs_export_options
*fnxo
, *tnxo
;
2804 enum vtype fvtype
, tvtype
;
2805 int holding_mntlock
;
2807 struct nfsm_chain
*nmreq
, nmrep
;
2808 char *from_name
, *to_name
;
2810 int from_len
= 0, to_len
= 0;
2811 fse_info from_finfo
, to_finfo
;
2813 u_char didstats
= 0;
2817 fdpreattrerr
= fdpostattrerr
= ENOENT
;
2818 tdpreattrerr
= tdpostattrerr
= ENOENT
;
2819 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2820 fromlen
= tolen
= 0;
2821 frompath
= topath
= NULL
;
2822 fdirp
= tdirp
= NULL
;
2823 nmreq
= &nd
->nd_nmreq
;
2824 nfsm_chain_null(&nmrep
);
2827 * these need to be set before calling any code
2828 * that they may take us out through the error path.
2830 holding_mntlock
= 0;
2835 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, fnfh
.nfh_fhp
, fnfh
.nfh_len
);
2836 nfsm_chain_get_32(error
, nmreq
, fromlen
);
2837 nfsm_name_len_check(error
, nd
, fromlen
);
2839 error
= nfsm_chain_get_path_namei(nmreq
, fromlen
, &fromni
);
2841 frompath
= fromni
.ni_cnd
.cn_pnbuf
;
2843 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, tnfh
.nfh_fhp
, tnfh
.nfh_len
);
2844 nfsm_chain_get_32(error
, nmreq
, tolen
);
2845 nfsm_name_len_check(error
, nd
, tolen
);
2847 error
= nfsm_chain_get_path_namei(nmreq
, tolen
, &toni
);
2849 topath
= toni
.ni_cnd
.cn_pnbuf
;
2852 * Remember our original uid so that we can reset cr_uid before
2853 * the second nfsrv_namei() call, in case it is remapped.
2855 saved_cred
= nd
->nd_cr
;
2856 kauth_cred_ref(saved_cred
);
2858 fromni
.ni_cnd
.cn_nameiop
= DELETE
;
2860 fromni
.ni_op
= OP_UNLINK
;
2862 fromni
.ni_cnd
.cn_flags
= WANTPARENT
;
2864 fromni
.ni_cnd
.cn_pnbuf
= frompath
;
2866 fromni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2867 fromni
.ni_cnd
.cn_flags
|= HASBUF
;
2868 fromni
.ni_cnd
.cn_ndp
= &fromni
;
2870 error
= nfsrv_namei(nd
, ctx
, &fromni
, &fnfh
, &fdirp
, &fnx
, &fnxo
);
2874 fdvp
= fromni
.ni_dvp
;
2878 if (nd
->nd_vers
== NFS_VER3
) {
2879 nfsm_srv_pre_vattr_init(&fdpreattr
);
2880 fdpreattrerr
= vnode_getattr(fdirp
, &fdpreattr
, ctx
);
2886 fvtype
= vnode_vtype(fvp
);
2888 /* reset credential if it was remapped */
2889 if (nd
->nd_cr
!= saved_cred
) {
2890 kauth_cred_ref(saved_cred
);
2891 kauth_cred_unref(&nd
->nd_cr
);
2892 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
2895 toni
.ni_cnd
.cn_nameiop
= RENAME
;
2897 toni
.ni_op
= OP_RENAME
;
2899 toni
.ni_cnd
.cn_flags
= WANTPARENT
;
2901 toni
.ni_cnd
.cn_pnbuf
= topath
;
2903 toni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2904 toni
.ni_cnd
.cn_flags
|= HASBUF
;
2905 toni
.ni_cnd
.cn_ndp
= &toni
;
2907 if (fvtype
== VDIR
) {
2908 toni
.ni_cnd
.cn_flags
|= WILLBEDIR
;
2912 error
= nfsrv_namei(nd
, ctx
, &toni
, &tnfh
, &tdirp
, &tnx
, &tnxo
);
2915 * Translate error code for rename("dir1", "dir2/.").
2917 if (error
== EISDIR
&& fvtype
== VDIR
) {
2918 if (nd
->nd_vers
== NFS_VER3
) {
2930 /* update export stats once only */
2932 /* update export stats */
2933 NFSStatAdd64(&tnx
->nx_stats
.ops
, 1);
2935 /* update active user stats */
2936 nfsrv_update_user_stat(tnx
, nd
, saved_uid
, 1, 0, 0);
2942 if (nd
->nd_vers
== NFS_VER3
) {
2943 nfsm_srv_pre_vattr_init(&tdpreattr
);
2944 tdpreattrerr
= vnode_getattr(tdirp
, &tdpreattr
, ctx
);
2952 tvtype
= vnode_vtype(tvp
);
2954 if (fvtype
== VDIR
&& tvtype
!= VDIR
) {
2955 if (nd
->nd_vers
== NFS_VER3
) {
2961 } else if (fvtype
!= VDIR
&& tvtype
== VDIR
) {
2962 if (nd
->nd_vers
== NFS_VER3
) {
2969 if (tvtype
== VDIR
&& vnode_mountedhere(tvp
)) {
2970 if (nd
->nd_vers
== NFS_VER3
) {
2979 if (nd
->nd_vers
== NFS_VER3
) {
2990 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2991 * the node is moving between directories and we need rights to remove from the
2992 * old and add to the new.
2994 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2996 * Note that we do not inherit when renaming. XXX this needs to be revisited to
2997 * implement the deferred-inherit bit.
3003 if ((tvp
!= NULL
) && vnode_isdir(tvp
)) {
3007 } else if (tdvp
!= fdvp
) {
3011 /* moving out of fdvp, must have delete rights */
3012 if ((error
= nfsrv_authorize(fvp
, fdvp
, KAUTH_VNODE_DELETE
, ctx
, fnxo
, 0)) != 0) {
3015 /* moving into tdvp or tvp, must have rights to add */
3016 if ((error
= nfsrv_authorize(((tvp
!= NULL
) && vnode_isdir(tvp
)) ? tvp
: tdvp
,
3018 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
3019 ctx
, tnxo
, 0)) != 0) {
3023 /* node staying in same directory, must be allowed to add new name */
3024 if ((error
= nfsrv_authorize(fdvp
, NULL
,
3025 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
3026 ctx
, fnxo
, 0)) != 0) {
3030 /* overwriting tvp */
3031 if ((tvp
!= NULL
) && !vnode_isdir(tvp
) &&
3032 ((error
= nfsrv_authorize(tvp
, tdvp
, KAUTH_VNODE_DELETE
, ctx
, tnxo
, 0)) != 0)) {
3037 ((error
= vn_authorize_rename(fdvp
, fvp
, &fromni
.ni_cnd
, tdvp
, tvp
, &toni
.ni_cnd
, ctx
, NULL
)) != 0)) {
3043 /* XXX more checks? */
3046 /* authorization denied */
3052 if ((vnode_mount(fvp
) != vnode_mount(tdvp
)) ||
3053 (tvp
&& (vnode_mount(fvp
) != vnode_mount(tvp
)))) {
3054 if (nd
->nd_vers
== NFS_VER3
) {
3062 * The following edge case is caught here:
3063 * (to cannot be a descendent of from)
3076 if (tdvp
->v_parent
== fvp
) {
3077 if (nd
->nd_vers
== NFS_VER3
) {
3084 if (fvtype
== VDIR
&& vnode_mountedhere(fvp
)) {
3085 if (nd
->nd_vers
== NFS_VER3
) {
3093 * If source is the same as the destination (that is the
3094 * same vnode) then there is nothing to do...
3095 * EXCEPT if the underlying file system supports case
3096 * insensitivity and is case preserving. In this case
3097 * the file system needs to handle the special case of
3098 * getting the same vnode as target (fvp) and source (tvp).
3100 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
3101 * and _PC_CASE_PRESERVING can have this exception, and they need to
3102 * handle the special case of getting the same vnode as target and
3103 * source. NOTE: Then the target is unlocked going into vnop_rename,
3104 * so not to cause locking problems. There is a single reference on tvp.
3106 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
3107 * that correct behaviour then is just to remove the source (link)
3109 if ((fvp
== tvp
) && (fdvp
== tdvp
)) {
3110 if (fromni
.ni_cnd
.cn_namelen
== toni
.ni_cnd
.cn_namelen
&&
3111 !bcmp(fromni
.ni_cnd
.cn_nameptr
, toni
.ni_cnd
.cn_nameptr
,
3112 fromni
.ni_cnd
.cn_namelen
)) {
3117 if (holding_mntlock
&& vnode_mount(fvp
) != locked_mp
) {
3119 * we're holding a reference and lock
3120 * on locked_mp, but it no longer matches
3121 * what we want to do... so drop our hold
3123 mount_unlock_renames(locked_mp
);
3124 mount_drop(locked_mp
, 0);
3125 holding_mntlock
= 0;
3127 if (tdvp
!= fdvp
&& fvtype
== VDIR
) {
3129 * serialize renames that re-shape
3130 * the tree... if holding_mntlock is
3131 * set, then we're ready to go...
3133 * first need to drop the iocounts
3134 * we picked up, second take the
3135 * lock to serialize the access,
3136 * then finally start the lookup
3137 * process over with the lock held
3139 if (!holding_mntlock
) {
3141 * need to grab a reference on
3142 * the mount point before we
3143 * drop all the iocounts... once
3144 * the iocounts are gone, the mount
3147 locked_mp
= vnode_mount(fvp
);
3148 mount_ref(locked_mp
, 0);
3150 /* make a copy of to path to pass to nfsrv_namei() again */
3151 topath
= zalloc(ZV_NAMEI
);
3152 bcopy(toni
.ni_cnd
.cn_pnbuf
, topath
, tolen
+ 1);
3155 * nameidone has to happen before we vnode_put(tdvp)
3156 * since it may need to release the fs_nodelock on the tdvp
3165 /* make a copy of from path to pass to nfsrv_namei() again */
3166 frompath
= zalloc(ZV_NAMEI
);
3167 bcopy(fromni
.ni_cnd
.cn_pnbuf
, frompath
, fromlen
+ 1);
3170 * nameidone has to happen before we vnode_put(fdvp)
3171 * since it may need to release the fs_nodelock on the fdvp
3186 mount_lock_renames(locked_mp
);
3187 holding_mntlock
= 1;
3192 fdpreattrerr
= tdpreattrerr
= ENOENT
;
3194 if (!topath
|| !frompath
) {
3195 /* we couldn't allocate a path, so bail */
3200 /* reset credential if it was remapped */
3201 if (nd
->nd_cr
!= saved_cred
) {
3202 kauth_cred_ref(saved_cred
);
3203 kauth_cred_unref(&nd
->nd_cr
);
3204 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
3211 * when we dropped the iocounts to take
3212 * the lock, we allowed the identity of
3213 * the various vnodes to change... if they did,
3214 * we may no longer be dealing with a rename
3215 * that reshapes the tree... once we're holding
3216 * the iocounts, the vnodes can't change type
3217 * so we're free to drop the lock at this point
3220 if (holding_mntlock
) {
3221 mount_unlock_renames(locked_mp
);
3222 mount_drop(locked_mp
, 0);
3223 holding_mntlock
= 0;
3227 // save these off so we can later verify that fvp is the same
3229 oname
= fvp
->v_name
;
3230 oparent
= fvp
->v_parent
;
3233 * If generating an fsevent, then
3234 * stash any pre-rename info we may need.
3237 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_RENAME
, fvp
)) {
3238 int from_truncated
= 0, to_truncated
= 0;
3240 get_fse_info(fvp
, &from_finfo
, ctx
);
3242 get_fse_info(tvp
, &to_finfo
, ctx
);
3245 from_name
= get_pathbuff();
3247 from_len
= safe_getpath(fdvp
, fromni
.ni_cnd
.cn_nameptr
, from_name
, MAXPATHLEN
, &from_truncated
);
3250 to_name
= from_name
? get_pathbuff() : NULL
;
3252 to_len
= safe_getpath(tdvp
, toni
.ni_cnd
.cn_nameptr
, to_name
, MAXPATHLEN
, &to_truncated
);
3255 if (from_truncated
|| to_truncated
) {
3256 from_finfo
.mode
|= FSE_TRUNCATED_PATH
;
3262 #else /* CONFIG_FSE */
3265 #endif /* CONFIG_FSE */
3267 error
= VNOP_RENAME(fromni
.ni_dvp
, fromni
.ni_vp
, &fromni
.ni_cnd
,
3268 toni
.ni_dvp
, toni
.ni_vp
, &toni
.ni_cnd
, ctx
);
3270 * fix up name & parent pointers. note that we first
3271 * check that fvp has the same name/parent pointers it
3272 * had before the rename call... this is a 'weak' check
3275 if (oname
== fvp
->v_name
&& oparent
== fvp
->v_parent
) {
3277 update_flags
= VNODE_UPDATE_NAME
;
3279 update_flags
|= VNODE_UPDATE_PARENT
;
3281 vnode_update_identity(fvp
, tdvp
, toni
.ni_cnd
.cn_nameptr
,
3282 toni
.ni_cnd
.cn_namelen
, toni
.ni_cnd
.cn_hash
, update_flags
);
3286 * If the rename is OK and we've got the paths
3287 * then add an fsevent.
3290 if (nfsrv_fsevents_enabled
&& !error
&& from_name
&& to_name
) {
3292 add_fsevent(FSE_RENAME
, ctx
,
3293 FSE_ARG_STRING
, from_len
, from_name
,
3294 FSE_ARG_FINFO
, &from_finfo
,
3295 FSE_ARG_STRING
, to_len
, to_name
,
3296 FSE_ARG_FINFO
, &to_finfo
,
3299 add_fsevent(FSE_RENAME
, ctx
,
3300 FSE_ARG_STRING
, from_len
, from_name
,
3301 FSE_ARG_FINFO
, &from_finfo
,
3302 FSE_ARG_STRING
, to_len
, to_name
,
3307 release_pathbuff(from_name
);
3310 release_pathbuff(to_name
);
3312 #endif /* CONFIG_FSE */
3313 from_name
= to_name
= NULL
;
3316 if (holding_mntlock
) {
3317 mount_unlock_renames(locked_mp
);
3318 mount_drop(locked_mp
, 0);
3319 holding_mntlock
= 0;
3323 * nameidone has to happen before we vnode_put(tdvp)
3324 * since it may need to release the fs_nodelock on the tdvp
3336 * nameidone has to happen before we vnode_put(fdvp)
3337 * since it may need to release the fs_nodelock on the fdvp
3349 nfsm_srv_vattr_init(&fdpostattr
, nd
->nd_vers
);
3350 fdpostattrerr
= vnode_getattr(fdirp
, &fdpostattr
, ctx
);
3355 nfsm_srv_vattr_init(&tdpostattr
, nd
->nd_vers
);
3356 tdpostattrerr
= vnode_getattr(tdirp
, &tdpostattr
, ctx
);
3362 /* assemble reply */
3363 nd
->nd_repstat
= error
;
3364 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 2 * NFSX_WCCDATA(nd
->nd_vers
));
3366 *mrepp
= nmrep
.nmc_mhead
;
3367 nfsmout_on_status(nd
, error
);
3368 if (nd
->nd_vers
== NFS_VER3
) {
3369 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3370 fdpreattrerr
, &fdpreattr
, fdpostattrerr
, &fdpostattr
);
3371 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3372 tdpreattrerr
, &tdpreattr
, tdpostattrerr
, &tdpostattr
);
3375 nfsm_chain_build_done(error
, &nmrep
);
3376 if (holding_mntlock
) {
3377 mount_unlock_renames(locked_mp
);
3378 mount_drop(locked_mp
, 0);
3382 * nameidone has to happen before we vnode_put(tdvp)
3383 * since it may need to release the fs_nodelock on the tdvp
3394 * nameidone has to happen before we vnode_put(fdvp)
3395 * since it may need to release the fs_nodelock on the fdvp
3411 NFS_ZFREE(ZV_NAMEI
, frompath
);
3414 NFS_ZFREE(ZV_NAMEI
, topath
);
3417 kauth_cred_unref(&saved_cred
);
3420 nfsm_chain_cleanup(&nmrep
);
3431 struct nfsrv_descript
*nd
,
3432 struct nfsrv_sock
*slp
,
3436 struct nameidata ni
;
3437 int error
, dpreattrerr
, dpostattrerr
, attrerr
;
3439 vnode_t vp
, xp
, dvp
, dirp
;
3440 struct vnode_attr dpreattr
, dpostattr
, attr
;
3441 struct nfs_filehandle nfh
, dnfh
;
3442 struct nfs_export
*nx
;
3443 struct nfs_export_options
*nxo
;
3444 struct nfsm_chain
*nmreq
, nmrep
;
3447 dpreattrerr
= dpostattrerr
= attrerr
= ENOENT
;
3448 vp
= xp
= dvp
= dirp
= NULL
;
3449 nmreq
= &nd
->nd_nmreq
;
3450 nfsm_chain_null(&nmrep
);
3452 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3453 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
3454 nfsm_chain_get_32(error
, nmreq
, len
);
3455 nfsm_name_len_check(error
, nd
, len
);
3457 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
3460 /* update export stats */
3461 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3463 /* update active user stats */
3464 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
3466 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
3469 /* we're not allowed to link to directories... */
3470 if (vnode_vtype(vp
) == VDIR
) {
3471 error
= EPERM
; /* POSIX */
3475 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3476 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LINKTARGET
, ctx
, nxo
, 0)) != 0) {
3480 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3484 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3485 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3487 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
3490 if (nd
->nd_vers
== NFS_VER3
) {
3491 nfsm_srv_pre_vattr_init(&dpreattr
);
3492 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3506 } else if (vnode_mount(vp
) != vnode_mount(dvp
)) {
3509 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3514 error
= mac_vnode_check_link(ctx
, dvp
, vp
, &ni
.ni_cnd
);
3521 error
= VNOP_LINK(vp
, dvp
, &ni
.ni_cnd
, ctx
);
3525 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CREATE_FILE
, dvp
)) {
3526 char *target_path
= NULL
;
3527 int plen
, truncated
= 0;
3530 /* build the path to the new link file */
3531 target_path
= get_pathbuff();
3533 plen
= safe_getpath(dvp
, ni
.ni_cnd
.cn_nameptr
, target_path
, MAXPATHLEN
, &truncated
);
3535 if (get_fse_info(vp
, &finfo
, ctx
) == 0) {
3537 finfo
.mode
|= FSE_TRUNCATED_PATH
;
3539 add_fsevent(FSE_CREATE_FILE
, ctx
,
3540 FSE_ARG_STRING
, plen
, target_path
,
3541 FSE_ARG_FINFO
, &finfo
,
3545 release_pathbuff(target_path
);
3551 * nameidone has to happen before we vnode_put(dvp)
3552 * since it may need to release the fs_nodelock on the dvp
3561 if (nd
->nd_vers
== NFS_VER3
) {
3562 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
3563 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
3566 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3567 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3575 /* assemble reply */
3576 nd
->nd_repstat
= error
;
3577 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3579 *mrepp
= nmrep
.nmc_mhead
;
3580 nfsmout_on_status(nd
, error
);
3581 if (nd
->nd_vers
== NFS_VER3
) {
3582 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
3583 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3584 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3587 nfsm_chain_build_done(error
, &nmrep
);
3592 nfsm_chain_cleanup(&nmrep
);
3599 * nfs symbolic link service
3603 struct nfsrv_descript
*nd
,
3604 struct nfsrv_sock
*slp
,
3608 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3609 struct vnode_attr va
, *vap
= &va
;
3610 struct nameidata ni
;
3611 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3612 uint32_t len
= 0, linkdatalen
, cnflags
;
3615 vnode_t vp
, dvp
, dirp
;
3616 struct nfs_filehandle nfh
;
3617 struct nfs_export
*nx
= NULL
;
3618 struct nfs_export_options
*nxo
= NULL
;
3620 char uio_buf
[UIO_SIZEOF(1)];
3621 struct nfsm_chain
*nmreq
, nmrep
;
3624 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3625 nmreq
= &nd
->nd_nmreq
;
3626 nfsm_chain_null(&nmrep
);
3630 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3632 ni
.ni_cnd
.cn_nameiop
= 0;
3635 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3636 nfsm_chain_get_32(error
, nmreq
, len
);
3637 nfsm_name_len_check(error
, nd
, len
);
3640 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3644 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3646 ni
.ni_cnd
.cn_ndp
= &ni
;
3647 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3649 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3651 /* update export stats */
3652 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3654 /* update active user stats */
3655 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3659 if (nd
->nd_vers
== NFS_VER3
) {
3660 nfsm_srv_pre_vattr_init(&dpreattr
);
3661 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3668 ni
.ni_cnd
.cn_nameiop
= 0;
3675 if (nd
->nd_vers
== NFS_VER3
) {
3676 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3678 nfsm_chain_get_32(error
, nmreq
, linkdatalen
);
3679 if (!error
&& (((nd
->nd_vers
== NFS_VER2
) && (linkdatalen
> NFS_MAXPATHLEN
)) ||
3680 ((nd
->nd_vers
== NFS_VER3
) && (linkdatalen
> MAXPATHLEN
)))) {
3681 error
= NFSERR_NAMETOL
;
3684 MALLOC(linkdata
, caddr_t
, linkdatalen
+ 1, M_TEMP
, M_WAITOK
);
3686 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
3687 &uio_buf
[0], sizeof(uio_buf
));
3689 if (!linkdata
|| !auio
) {
3693 uio_addiov(auio
, CAST_USER_ADDR_T(linkdata
), linkdatalen
);
3694 error
= nfsm_chain_get_uio(nmreq
, linkdatalen
, auio
);
3695 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
3696 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3699 *(linkdata
+ linkdatalen
) = '\0';
3705 VATTR_SET(vap
, va_type
, VLNK
);
3706 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3707 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3709 * Server policy is to alway use the mapped rpc credential for
3710 * file system object creation. This has the nice side effect of
3711 * enforcing BSD creation semantics
3713 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3714 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3716 /* authorize before creating */
3717 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3719 /* validate given attributes */
3721 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3724 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
3731 error
= VNOP_SYMLINK(dvp
, &vp
, &ni
.ni_cnd
, vap
, linkdata
, ctx
);
3734 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
3736 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
3738 ni
.ni_op
= OP_LOOKUP
;
3740 ni
.ni_cnd
.cn_flags
&= ~(LOCKPARENT
| FOLLOW
);
3741 ni
.ni_cnd
.cn_flags
|= (NOFOLLOW
| LOCKLEAF
);
3742 ni
.ni_cnd
.cn_context
= ctx
;
3743 ni
.ni_startdir
= dvp
;
3745 ni
.ni_rootdir
= rootvnode
;
3746 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
3747 while ((error
= lookup(&ni
)) == ERECYCLE
) {
3748 ni
.ni_cnd
.cn_flags
= cnflags
;
3749 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
3750 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
3757 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
3759 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
3760 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
3766 if (nfsrv_fsevents_enabled
&& !error
&& vp
) {
3767 add_fsevent(FSE_CREATE_FILE
, ctx
,
3774 * nameidone has to happen before we vnode_put(dvp)
3775 * since it may need to release the fs_nodelock on the dvp
3778 ni
.ni_cnd
.cn_nameiop
= 0;
3785 FREE(linkdata
, M_TEMP
);
3789 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3790 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3796 /* assemble reply */
3797 nd
->nd_repstat
= error
;
3798 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
3799 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3801 *mrepp
= nmrep
.nmc_mhead
;
3802 nfsmout_on_status(nd
, error
);
3803 if (nd
->nd_vers
== NFS_VER3
) {
3804 if (!nd
->nd_repstat
) {
3805 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3806 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
3808 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3809 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3812 nfsm_chain_build_done(error
, &nmrep
);
3813 if (ni
.ni_cnd
.cn_nameiop
) {
3815 * nameidone has to happen before we vnode_put(dvp)
3816 * since it may need to release the fs_nodelock on the dvp
3829 FREE(linkdata
, M_TEMP
);
3832 nfsm_chain_cleanup(&nmrep
);
3844 struct nfsrv_descript
*nd
,
3845 struct nfsrv_sock
*slp
,
3849 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3850 struct vnode_attr va
, *vap
= &va
;
3851 struct nameidata ni
;
3852 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3854 vnode_t vp
, dvp
, dirp
;
3855 struct nfs_filehandle nfh
;
3856 struct nfs_export
*nx
= NULL
;
3857 struct nfs_export_options
*nxo
= NULL
;
3859 kauth_acl_t xacl
= NULL
;
3860 struct nfsm_chain
*nmreq
, nmrep
;
3863 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3864 nmreq
= &nd
->nd_nmreq
;
3865 nfsm_chain_null(&nmrep
);
3867 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3869 ni
.ni_cnd
.cn_nameiop
= 0;
3870 vp
= dvp
= dirp
= NULL
;
3872 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3873 nfsm_chain_get_32(error
, nmreq
, len
);
3874 nfsm_name_len_check(error
, nd
, len
);
3877 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3881 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| WILLBEDIR
;
3882 ni
.ni_cnd
.cn_ndp
= &ni
;
3883 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3885 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3887 /* update export stats */
3888 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3890 /* update active user stats */
3891 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3895 if (nd
->nd_vers
== NFS_VER3
) {
3896 nfsm_srv_pre_vattr_init(&dpreattr
);
3897 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3904 ni
.ni_cnd
.cn_nameiop
= 0;
3911 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3913 VATTR_SET(vap
, va_type
, VDIR
);
3917 * nameidone has to happen before we vnode_put(dvp)
3918 * since it may need to release the fs_nodelock on the dvp
3927 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_SUBDIRECTORY
, ctx
, nxo
, 0);
3929 /* construct ACL and handle inheritance */
3931 error
= kauth_acl_inherit(dvp
,
3937 if (!error
&& xacl
!= NULL
) {
3938 VATTR_SET(vap
, va_acl
, xacl
);
3942 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3943 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3945 * We don't support the S_ISGID bit for directories. Solaris and other
3946 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3949 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
3950 vap
->va_mode
&= ~S_ISGID
;
3953 * Server policy is to alway use the mapped rpc credential for
3954 * file system object creation. This has the nice side effect of
3955 * enforcing BSD creation semantics
3957 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3958 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3960 /* validate new-file security information */
3962 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3965 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3966 * sit well with our clients so we map all errors to EPERM.
3973 error
= vn_authorize_mkdir(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
3980 error
= VNOP_MKDIR(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
3984 if (nfsrv_fsevents_enabled
&& !error
) {
3985 add_fsevent(FSE_CREATE_DIR
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
3989 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
3991 * If some of the requested attributes weren't handled by the VNOP,
3992 * use our fallback code.
3994 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
3998 kauth_acl_free(xacl
);
4002 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
4004 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
4005 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
4006 if (nd
->nd_vers
== NFS_VER2
) {
4007 error
= postattrerr
;
4014 * nameidone has to happen before we vnode_put(dvp)
4015 * since it may need to release the fs_nodelock on the dvp
4020 ni
.ni_cnd
.cn_nameiop
= 0;
4023 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
4024 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
4030 /* assemble reply */
4031 nd
->nd_repstat
= error
;
4032 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
4033 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
4035 *mrepp
= nmrep
.nmc_mhead
;
4036 nfsmout_on_status(nd
, error
);
4037 if (nd
->nd_vers
== NFS_VER3
) {
4038 if (!nd
->nd_repstat
) {
4039 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4040 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
4042 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4043 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
4045 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4047 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
4051 nfsm_chain_build_done(error
, &nmrep
);
4052 if (ni
.ni_cnd
.cn_nameiop
) {
4054 * nameidone has to happen before we vnode_put(dvp)
4055 * since it may need to release the fs_nodelock on the dvp
4067 nfsm_chain_cleanup(&nmrep
);
4078 struct nfsrv_descript
*nd
,
4079 struct nfsrv_sock
*slp
,
4083 int error
, dpreattrerr
, dpostattrerr
;
4086 vnode_t vp
, dvp
, dirp
;
4087 struct vnode_attr dpreattr
, dpostattr
;
4088 struct nfs_filehandle nfh
;
4089 struct nfs_export
*nx
= NULL
;
4090 struct nfs_export_options
*nxo
= NULL
;
4091 struct nameidata ni
;
4092 struct nfsm_chain
*nmreq
, nmrep
;
4095 dpreattrerr
= dpostattrerr
= ENOENT
;
4096 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
4097 nmreq
= &nd
->nd_nmreq
;
4098 nfsm_chain_null(&nmrep
);
4100 vp
= dvp
= dirp
= NULL
;
4102 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4103 nfsm_chain_get_32(error
, nmreq
, len
);
4104 nfsm_name_len_check(error
, nd
, len
);
4107 ni
.ni_cnd
.cn_nameiop
= DELETE
;
4109 ni
.ni_op
= OP_UNLINK
;
4111 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
4112 ni
.ni_cnd
.cn_ndp
= &ni
;
4113 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
4115 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
4117 /* update export stats */
4118 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4120 /* update active user stats */
4121 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
4125 if (nd
->nd_vers
== NFS_VER3
) {
4126 nfsm_srv_pre_vattr_init(&dpreattr
);
4127 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
4138 if (vnode_vtype(vp
) != VDIR
) {
4143 * No rmdir "." please.
4150 * The root of a mounted filesystem cannot be deleted.
4152 if (vnode_isvroot(vp
)) {
4156 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
4159 error
= vn_authorize_rmdir(dvp
, vp
, &ni
.ni_cnd
, ctx
, NULL
);
4171 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
4173 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
4174 get_fse_info(vp
, &finfo
, ctx
);
4176 release_pathbuff(path
);
4180 #endif /* CONFIG_FSE */
4182 error
= VNOP_RMDIR(dvp
, vp
, &ni
.ni_cnd
, ctx
);
4187 add_fsevent(FSE_DELETE
, ctx
,
4188 FSE_ARG_STRING
, plen
, path
,
4189 FSE_ARG_FINFO
, &finfo
,
4192 release_pathbuff(path
);
4194 #endif /* CONFIG_FSE */
4198 * nameidone has to happen before we vnode_put(dvp)
4199 * since it may need to release the fs_nodelock on the dvp
4207 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
4208 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
4214 /* assemble reply */
4215 nd
->nd_repstat
= error
;
4216 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
4218 *mrepp
= nmrep
.nmc_mhead
;
4219 nfsmout_on_status(nd
, error
);
4220 if (nd
->nd_vers
== NFS_VER3
) {
4221 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4222 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
4225 nfsm_chain_build_done(error
, &nmrep
);
4230 nfsm_chain_cleanup(&nmrep
);
4237 * nfs readdir service
4238 * - mallocs what it thinks is enough to read
4239 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4240 * - calls VNOP_READDIR()
4241 * - loops around building the reply
4242 * if the output generated exceeds count break out of loop
4243 * The nfsm_clget macro is used here so that the reply will be packed
4244 * tightly in mbuf clusters.
4245 * - it only knows that it has encountered eof when the VNOP_READDIR()
4247 * - as such one readdir rpc will return eof false although you are there
4248 * and then the next will return eof
4249 * - it trims out records with d_fileno == 0
4250 * this doesn't matter for Unix clients, but they might confuse clients
4252 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4253 * than requested, but this may not apply to all filesystems. For
4254 * example, client NFS does not { although it is never remote mounted
4256 * The alternate call nfsrv_readdirplus() does lookups as well.
4257 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
4258 * are supposed to cover. For readdir, the count is the total number of
4259 * bytes included in everything from the directory's postopattr through
4260 * the EOF flag. For readdirplus, the maxcount is the same, and the
4261 * dircount includes all that except for the entry attributes and handles.
4265 struct nfsrv_descript
*nd
,
4266 struct nfsrv_sock
*slp
,
4270 struct direntry
*dp
;
4271 char *cpos
, *cend
, *rbuf
;
4273 struct vnode_attr attr
;
4274 struct nfs_filehandle nfh
;
4275 struct nfs_export
*nx
;
4276 struct nfs_export_options
*nxo
;
4278 char uio_buf
[UIO_SIZEOF(1)];
4279 int len
, nlen
, rem
, xfer
, error
, attrerr
;
4280 int siz
, count
, fullsiz
, eofflag
, nentries
;
4281 u_quad_t off
, toff
, verf
= 0;
4283 struct nfsm_chain
*nmreq
, nmrep
;
4287 count
= nentries
= 0;
4288 nmreq
= &nd
->nd_nmreq
;
4289 nfsm_chain_null(&nmrep
);
4293 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
4295 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4296 if (nd
->nd_vers
== NFS_VER3
) {
4297 nfsm_chain_get_64(error
, nmreq
, toff
);
4298 nfsm_chain_get_64(error
, nmreq
, verf
);
4300 nfsm_chain_get_32(error
, nmreq
, toff
);
4302 nfsm_chain_get_32(error
, nmreq
, count
);
4306 siz
= ((count
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4307 xfer
= NFSRV_NDMAXDATA(nd
);
4313 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4316 /* update export stats */
4317 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4319 /* update active user stats */
4320 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4322 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4325 if (nxo
->nxo_flags
& NX_MANGLEDNAMES
|| nd
->nd_vers
== NFS_VER2
) {
4326 vnopflag
|= VNODE_READDIR_NAMEMAX
;
4329 if ((nd
->nd_vers
== NFS_VER2
) || (nxo
->nxo_flags
& NX_32BITCLIENTS
)) {
4330 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
4333 if (nd
->nd_vers
== NFS_VER3
) {
4334 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4335 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4336 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
)) {
4337 error
= NFSERR_BAD_COOKIE
;
4341 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
4345 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
)) {
4350 error
= mac_vnode_check_readdir(ctx
, vp
);
4356 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
4358 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
4359 &uio_buf
[0], sizeof(uio_buf
));
4361 if (!rbuf
|| !auio
) {
4366 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
4367 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
4369 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
4370 off
= uio_offset(auio
);
4372 if (nd
->nd_vers
== NFS_VER3
) {
4373 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4374 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4378 if (uio_resid(auio
) != 0) {
4379 siz
-= uio_resid(auio
);
4381 /* If nothing read, return empty reply with eof set */
4386 /* assemble reply */
4387 nd
->nd_repstat
= error
;
4388 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
4389 NFSX_COOKIEVERF(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
);
4391 *mrepp
= nmrep
.nmc_mhead
;
4392 nfsmout_on_status(nd
, error
);
4393 if (nd
->nd_vers
== NFS_VER3
) {
4394 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4395 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4397 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4398 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4399 nfsm_chain_build_done(error
, &nmrep
);
4405 * Check for degenerate cases of nothing useful read.
4406 * If so go try again
4410 dp
= (struct direntry
*)cpos
;
4411 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4412 cpos
+= dp
->d_reclen
;
4413 dp
= (struct direntry
*)cpos
;
4416 if ((cpos
>= cend
) || (nentries
== 0)) {
4425 /* assemble reply */
4426 nd
->nd_repstat
= error
;
4427 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
4428 NFSX_COOKIEVERF(nd
->nd_vers
) + siz
);
4430 *mrepp
= nmrep
.nmc_mhead
;
4431 nfsmout_on_status(nd
, error
);
4432 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4434 len
= 2 * NFSX_UNSIGNED
;
4435 if (nd
->nd_vers
== NFS_VER3
) {
4436 len
+= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
;
4437 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4438 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4442 /* Loop through the records and build reply */
4443 while ((cpos
< cend
) && (nentries
> 0)) {
4444 if (dp
->d_fileno
!= 0) {
4445 nlen
= dp
->d_namlen
;
4446 if ((nd
->nd_vers
== NFS_VER2
) && (nlen
> NFS_MAXNAMLEN
)) {
4447 nlen
= NFS_MAXNAMLEN
;
4449 rem
= nfsm_rndup(nlen
) - nlen
;
4450 len
+= (4 * NFSX_UNSIGNED
+ nlen
+ rem
);
4451 if (nd
->nd_vers
== NFS_VER3
) {
4452 len
+= 2 * NFSX_UNSIGNED
;
4458 /* Build the directory record xdr from the direntry. */
4459 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4460 if (nd
->nd_vers
== NFS_VER3
) {
4461 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4463 nfsm_chain_add_32(error
, &nmrep
, dp
->d_fileno
);
4465 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4466 if (nd
->nd_vers
== NFS_VER3
) {
4467 if (vnopflag
& VNODE_READDIR_SEEKOFF32
) {
4468 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4470 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4472 nfsm_chain_add_32(error
, &nmrep
, dp
->d_seekoff
);
4476 cpos
+= dp
->d_reclen
;
4477 dp
= (struct direntry
*)cpos
;
4480 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4481 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4491 nd
->nd_repstat
= error
;
4492 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
));
4494 *mrepp
= nmrep
.nmc_mhead
;
4495 nfsmout_on_status(nd
, error
);
4496 if (nd
->nd_vers
== NFS_VER3
) {
4497 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4500 nfsm_chain_build_done(error
, &nmrep
);
4502 nfsm_chain_cleanup(&nmrep
);
4510 struct nfsrv_descript
*nd
,
4511 struct nfsrv_sock
*slp
,
4515 struct direntry
*dp
;
4516 char *cpos
, *cend
, *rbuf
;
4518 struct nfs_filehandle dnfh
, nfh
;
4519 struct nfs_export
*nx
;
4520 struct nfs_export_options
*nxo
;
4522 char uio_buf
[UIO_SIZEOF(1)];
4523 struct vnode_attr attr
, va
, *vap
= &va
;
4524 int len
, nlen
, rem
, xfer
, error
, attrerr
, gotfh
, gotattr
;
4525 int siz
, dircount
, maxcount
, fullsiz
, eofflag
, dirlen
, nentries
, isdotdot
;
4526 u_quad_t off
, toff
, verf
;
4528 struct nfsm_chain
*nmreq
, nmrep
;
4533 nmreq
= &nd
->nd_nmreq
;
4534 nfsm_chain_null(&nmrep
);
4537 dircount
= maxcount
= 0;
4539 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
4541 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
4542 nfsm_chain_get_64(error
, nmreq
, toff
);
4543 nfsm_chain_get_64(error
, nmreq
, verf
);
4544 nfsm_chain_get_32(error
, nmreq
, dircount
);
4545 nfsm_chain_get_32(error
, nmreq
, maxcount
);
4549 xfer
= NFSRV_NDMAXDATA(nd
);
4550 dircount
= ((dircount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4551 if (dircount
> xfer
) {
4554 fullsiz
= siz
= dircount
;
4555 maxcount
= ((maxcount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4556 if (maxcount
> xfer
) {
4560 error
= nfsrv_fhtovp(&dnfh
, nd
, &vp
, &nx
, &nxo
);
4563 /* update export stats */
4564 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4566 /* update active user stats */
4567 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4569 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4572 if (nxo
->nxo_flags
& NX_32BITCLIENTS
) {
4573 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
4576 if (nxo
->nxo_flags
& NX_MANGLEDNAMES
) {
4577 vnopflag
|= VNODE_READDIR_NAMEMAX
;
4580 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4581 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4582 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
)) {
4583 error
= NFSERR_BAD_COOKIE
;
4586 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
4590 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
)) {
4595 error
= mac_vnode_check_readdir(ctx
, vp
);
4601 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
4603 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
4604 &uio_buf
[0], sizeof(uio_buf
));
4606 if (!rbuf
|| !auio
) {
4612 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
4613 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
4615 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
4616 off
= uio_offset(auio
);
4617 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4618 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4621 if (uio_resid(auio
) != 0) {
4622 siz
-= uio_resid(auio
);
4624 /* If nothing read, return empty reply with eof set */
4629 /* assemble reply */
4630 nd
->nd_repstat
= error
;
4631 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+
4632 NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
);
4634 *mrepp
= nmrep
.nmc_mhead
;
4635 nfsmout_on_status(nd
, error
);
4636 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4637 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4638 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4639 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4640 nfsm_chain_build_done(error
, &nmrep
);
4646 * Check for degenerate cases of nothing useful read.
4647 * If so go try again
4651 dp
= (struct direntry
*)cpos
;
4652 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4653 cpos
+= dp
->d_reclen
;
4654 dp
= (struct direntry
*)cpos
;
4657 if ((cpos
>= cend
) || (nentries
== 0)) {
4664 * Probe one of the directory entries to see if the filesystem
4667 if ((error
= VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
))) {
4668 if (error
== ENOTSUP
) { /* let others get passed back */
4669 error
= NFSERR_NOTSUPP
;
4675 /* assemble reply */
4676 nd
->nd_repstat
= error
;
4677 error
= nfsrv_rephead(nd
, slp
, &nmrep
, maxcount
);
4679 *mrepp
= nmrep
.nmc_mhead
;
4680 nfsmout_on_status(nd
, error
);
4681 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4683 dirlen
= len
= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
;
4684 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4685 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4688 /* Loop through the records and build reply */
4689 while ((cpos
< cend
) && (nentries
> 0)) {
4690 if (dp
->d_fileno
!= 0) {
4691 nlen
= dp
->d_namlen
;
4692 rem
= nfsm_rndup(nlen
) - nlen
;
4693 gotfh
= gotattr
= 1;
4695 /* Got to get the vnode for lookup per entry. */
4696 if (VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
)) {
4697 /* Can't get the vnode... so no fh or attrs */
4698 gotfh
= gotattr
= 0;
4700 isdotdot
= ((dp
->d_namlen
== 2) &&
4701 (dp
->d_name
[0] == '.') && (dp
->d_name
[1] == '.'));
4702 if (nfsrv_vptofh(nx
, 0, (isdotdot
? &dnfh
: NULL
), nvp
, ctx
, &nfh
)) {
4705 nfsm_srv_vattr_init(vap
, NFS_VER3
);
4706 if (vnode_getattr(nvp
, vap
, ctx
)) {
4713 * If either the dircount or maxcount will be
4714 * exceeded, get out now. Both of these lengths
4715 * are calculated conservatively, including all
4718 len
+= 8 * NFSX_UNSIGNED
+ nlen
+ rem
;
4720 len
+= NFSX_V3FATTR
;
4723 len
+= NFSX_UNSIGNED
+ nfsm_rndup(nfh
.nfh_len
);
4725 dirlen
+= 6 * NFSX_UNSIGNED
+ nlen
+ rem
;
4726 if ((len
> maxcount
) || (dirlen
> dircount
)) {
4731 /* Build the directory record xdr from the direntry. */
4732 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4733 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4734 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4735 if (vnopflag
& VNODE_READDIR_SEEKOFF32
) {
4736 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4738 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4739 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, (gotattr
? 0 : ENOENT
), vap
);
4741 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4743 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4747 cpos
+= dp
->d_reclen
;
4748 dp
= (struct direntry
*)cpos
;
4753 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4754 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4761 nd
->nd_repstat
= error
;
4762 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
);
4764 *mrepp
= nmrep
.nmc_mhead
;
4765 nfsmout_on_status(nd
, error
);
4766 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4768 nfsm_chain_build_done(error
, &nmrep
);
4773 nfsm_chain_cleanup(&nmrep
);
4780 * nfs commit service
4784 struct nfsrv_descript
*nd
,
4785 struct nfsrv_sock
*slp
,
4790 struct nfs_filehandle nfh
;
4791 struct nfs_export
*nx
= NULL
;
4792 struct nfs_export_options
*nxo
;
4793 int error
, preattrerr
, postattrerr
, count
;
4794 struct vnode_attr preattr
, postattr
;
4796 struct nfsm_chain
*nmreq
, nmrep
;
4799 preattrerr
= postattrerr
= ENOENT
;
4800 nmreq
= &nd
->nd_nmreq
;
4801 nfsm_chain_null(&nmrep
);
4805 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4806 * count parameters, so those arguments are useless (someday maybe).
4809 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4810 nfsm_chain_get_64(error
, nmreq
, off
);
4811 nfsm_chain_get_32(error
, nmreq
, count
);
4814 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4817 /* update export stats */
4818 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4820 /* update active user stats */
4821 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4823 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4826 nfsm_srv_pre_vattr_init(&preattr
);
4827 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
4829 error
= VNOP_FSYNC(vp
, MNT_WAIT
, ctx
);
4831 nfsm_srv_vattr_init(&postattr
, 1);
4832 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
4839 /* assemble reply */
4840 nd
->nd_repstat
= error
;
4841 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3WCCDATA
+ NFSX_V3WRITEVERF
);
4843 *mrepp
= nmrep
.nmc_mhead
;
4844 nfsmout_on_status(nd
, error
);
4845 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4846 preattrerr
, &preattr
, postattrerr
, &postattr
);
4847 if (!nd
->nd_repstat
) {
4848 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
4849 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
4852 nfsm_chain_build_done(error
, &nmrep
);
4854 nfsm_chain_cleanup(&nmrep
);
4861 * nfs statfs service
4865 struct nfsrv_descript
*nd
,
4866 struct nfsrv_sock
*slp
,
4873 struct vnode_attr attr
;
4874 struct nfs_filehandle nfh
;
4875 struct nfs_export
*nx
;
4876 struct nfs_export_options
*nxo
;
4878 struct nfsm_chain
*nmreq
, nmrep
;
4882 nmreq
= &nd
->nd_nmreq
;
4883 nfsm_chain_null(&nmrep
);
4887 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4889 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4892 /* update export stats */
4893 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4895 /* update active user stats */
4896 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4898 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4902 VFSATTR_WANTED(&va
, f_blocks
);
4903 VFSATTR_WANTED(&va
, f_bfree
);
4904 VFSATTR_WANTED(&va
, f_bavail
);
4905 VFSATTR_WANTED(&va
, f_files
);
4906 VFSATTR_WANTED(&va
, f_ffree
);
4907 error
= vfs_getattr(vnode_mount(vp
), &va
, ctx
);
4908 blksize
= vnode_mount(vp
)->mnt_vfsstat
.f_bsize
;
4910 if (nd
->nd_vers
== NFS_VER3
) {
4911 nfsm_srv_vattr_init(&attr
, nd
->nd_vers
);
4912 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4920 /* assemble reply */
4921 nd
->nd_repstat
= error
;
4922 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_STATFS(nd
->nd_vers
));
4924 *mrepp
= nmrep
.nmc_mhead
;
4925 nfsmout_on_status(nd
, error
);
4926 if (nd
->nd_vers
== NFS_VER3
) {
4927 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4929 nfsmout_if(nd
->nd_repstat
);
4931 if (nd
->nd_vers
== NFS_VER3
) {
4932 nfsm_chain_add_64(error
, &nmrep
, va
.f_blocks
* blksize
);
4933 nfsm_chain_add_64(error
, &nmrep
, va
.f_bfree
* blksize
);
4934 nfsm_chain_add_64(error
, &nmrep
, va
.f_bavail
* blksize
);
4935 nfsm_chain_add_64(error
, &nmrep
, va
.f_files
);
4936 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4937 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4938 nfsm_chain_add_32(error
, &nmrep
, 0); /* invarsec */
4940 nfsm_chain_add_32(error
, &nmrep
, NFS_V2MAXDATA
);
4941 nfsm_chain_add_32(error
, &nmrep
, blksize
);
4942 nfsm_chain_add_32(error
, &nmrep
, va
.f_blocks
);
4943 nfsm_chain_add_32(error
, &nmrep
, va
.f_bfree
);
4944 nfsm_chain_add_32(error
, &nmrep
, va
.f_bavail
);
4947 nfsm_chain_build_done(error
, &nmrep
);
4949 nfsm_chain_cleanup(&nmrep
);
4956 * nfs fsinfo service
4960 struct nfsrv_descript
*nd
,
4961 struct nfsrv_sock
*slp
,
4965 int error
, attrerr
, prefsize
, maxsize
;
4967 struct vnode_attr attr
;
4968 struct nfs_filehandle nfh
;
4969 struct nfs_export
*nx
;
4970 struct nfs_export_options
*nxo
;
4971 struct nfsm_chain
*nmreq
, nmrep
;
4975 nmreq
= &nd
->nd_nmreq
;
4976 nfsm_chain_null(&nmrep
);
4979 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4981 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4984 /* update export stats */
4985 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4987 /* update active user stats */
4988 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4990 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4993 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4994 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
5001 /* assemble reply */
5002 nd
->nd_repstat
= error
;
5003 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3FSINFO
);
5005 *mrepp
= nmrep
.nmc_mhead
;
5006 nfsmout_on_status(nd
, error
);
5007 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
5008 nfsmout_if(nd
->nd_repstat
);
5011 * XXX There should be file system VFS OP(s) to get this information.
5012 * For now, assume our usual NFS defaults.
5014 if (slp
->ns_sotype
== SOCK_DGRAM
) {
5015 maxsize
= NFS_MAXDGRAMDATA
;
5016 prefsize
= NFS_PREFDGRAMDATA
;
5018 maxsize
= prefsize
= slp
->ns_sobufsize
? slp
->ns_sobufsize
/ 2 : NFSRV_MAXDATA
;
5021 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
5022 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5023 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
5024 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
5025 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5026 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
5027 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5028 nfsm_chain_add_64(error
, &nmrep
, 0xffffffffffffffffULL
);
5029 nfsm_chain_add_32(error
, &nmrep
, 0);
5030 nfsm_chain_add_32(error
, &nmrep
, 1);
5031 /* XXX link/symlink support should be taken from volume capabilities */
5032 nfsm_chain_add_32(error
, &nmrep
,
5033 NFSV3FSINFO_LINK
| NFSV3FSINFO_SYMLINK
|
5034 NFSV3FSINFO_HOMOGENEOUS
| NFSV3FSINFO_CANSETTIME
);
5037 nfsm_chain_build_done(error
, &nmrep
);
5039 nfsm_chain_cleanup(&nmrep
);
5046 * nfs pathconf service
5050 struct nfsrv_descript
*nd
,
5051 struct nfsrv_sock
*slp
,
5055 int error
, attrerr
, linkmax
= 0, namemax
= 0;
5056 int chownres
= 0, notrunc
= 0, case_sensitive
= 0, case_preserving
= 0;
5058 struct vnode_attr attr
;
5059 struct nfs_filehandle nfh
;
5060 struct nfs_export
*nx
;
5061 struct nfs_export_options
*nxo
;
5062 struct nfsm_chain
*nmreq
, nmrep
;
5066 nmreq
= &nd
->nd_nmreq
;
5067 nfsm_chain_null(&nmrep
);
5070 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
5072 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
5075 /* update export stats */
5076 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
5078 /* update active user stats */
5079 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
5081 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
5084 error
= VNOP_PATHCONF(vp
, _PC_LINK_MAX
, &linkmax
, ctx
);
5086 error
= VNOP_PATHCONF(vp
, _PC_NAME_MAX
, &namemax
, ctx
);
5089 error
= VNOP_PATHCONF(vp
, _PC_CHOWN_RESTRICTED
, &chownres
, ctx
);
5092 error
= VNOP_PATHCONF(vp
, _PC_NO_TRUNC
, ¬runc
, ctx
);
5095 error
= VNOP_PATHCONF(vp
, _PC_CASE_SENSITIVE
, &case_sensitive
, ctx
);
5098 error
= VNOP_PATHCONF(vp
, _PC_CASE_PRESERVING
, &case_preserving
, ctx
);
5101 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
5102 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
5109 /* assemble reply */
5110 nd
->nd_repstat
= error
;
5111 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3PATHCONF
);
5113 *mrepp
= nmrep
.nmc_mhead
;
5114 nfsmout_on_status(nd
, error
);
5115 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
5116 nfsmout_if(nd
->nd_repstat
);
5118 nfsm_chain_add_32(error
, &nmrep
, linkmax
);
5119 nfsm_chain_add_32(error
, &nmrep
, namemax
);
5120 nfsm_chain_add_32(error
, &nmrep
, notrunc
);
5121 nfsm_chain_add_32(error
, &nmrep
, chownres
);
5122 nfsm_chain_add_32(error
, &nmrep
, !case_sensitive
);
5123 nfsm_chain_add_32(error
, &nmrep
, case_preserving
);
5126 nfsm_chain_build_done(error
, &nmrep
);
5128 nfsm_chain_cleanup(&nmrep
);
5135 * Null operation, used by clients to ping server
5140 struct nfsrv_descript
*nd
,
5141 struct nfsrv_sock
*slp
,
5142 __unused vfs_context_t ctx
,
5145 int error
= NFSERR_RETVOID
;
5146 struct nfsm_chain nmrep
;
5149 * RPCSEC_GSS context setup ?
5151 if (nd
->nd_gss_context
) {
5152 return nfs_gss_svc_ctx_init(nd
, slp
, mrepp
);
5155 nfsm_chain_null(&nmrep
);
5157 /* assemble reply */
5158 nd
->nd_repstat
= error
;
5159 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
5161 *mrepp
= nmrep
.nmc_mhead
;
5163 nfsm_chain_build_done(error
, &nmrep
);
5165 nfsm_chain_cleanup(&nmrep
);
5172 * No operation, used for obsolete procedures
5177 struct nfsrv_descript
*nd
,
5178 struct nfsrv_sock
*slp
,
5179 __unused vfs_context_t ctx
,
5183 struct nfsm_chain nmrep
;
5185 nfsm_chain_null(&nmrep
);
5187 if (nd
->nd_repstat
) {
5188 error
= nd
->nd_repstat
;
5190 error
= EPROCUNAVAIL
;
5193 /* assemble reply */
5194 nd
->nd_repstat
= error
;
5195 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
5197 *mrepp
= nmrep
.nmc_mhead
;
5199 nfsm_chain_build_done(error
, &nmrep
);
5201 nfsm_chain_cleanup(&nmrep
);
5207 const nfsrv_proc_t nfsrv_procs
[NFS_NPROCS
] = {
5234 * Perform access checking for vnodes obtained from file handles that would
5235 * refer to files already opened by a Unix client. You cannot just use
5236 * vnode_authorize() for two reasons.
5237 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5238 * 2 - The owner is to be given access irrespective of mode bits so that
5239 * processes that chmod after opening a file don't break. I don't like
5240 * this because it opens a security hole, but since the nfs server opens
5241 * a security hole the size of a barn door anyhow, what the heck.
5243 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5244 * will return EPERM instead of EACCESS. EPERM is always an error.
5251 kauth_action_t action
,
5253 struct nfs_export_options
*nxo
,
5256 struct vnode_attr vattr
;
5259 if (action
& KAUTH_VNODE_WRITE_RIGHTS
) {
5261 * Disallow write attempts on read-only exports;
5262 * unless the file is a socket or a block or character
5263 * device resident on the file system.
5265 if (nxo
->nxo_flags
& NX_READONLY
) {
5266 switch (vnode_vtype(vp
)) {
5267 case VREG
: case VDIR
: case VLNK
: case VCPLX
:
5274 error
= vnode_authorize(vp
, dvp
, action
, ctx
);
5276 * Allow certain operations for the owner (reads and writes
5277 * on files that are already open). Picking up from FreeBSD.
5279 if (override
&& (error
== EACCES
)) {
5281 VATTR_WANTED(&vattr
, va_uid
);
5282 if ((vnode_getattr(vp
, &vattr
, ctx
) == 0) &&
5283 (kauth_cred_getuid(vfs_context_ucred(ctx
)) == vattr
.va_uid
)) {
5290 #endif /* CONFIG_NFS_SERVER */