2 * Copyright (c) 2000-2019 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 <sys/param.h>
69 #include <sys/systm.h>
71 #include <sys/kauth.h>
72 #include <sys/unistd.h>
73 #include <sys/malloc.h>
74 #include <sys/vnode.h>
75 #include <sys/mount_internal.h>
76 #include <sys/socket.h>
77 #include <sys/socketvar.h>
78 #include <sys/kpi_mbuf.h>
79 #include <sys/dirent.h>
81 #include <sys/kernel.h>
83 #include <sys/vnode_internal.h>
84 #include <sys/uio_internal.h>
85 #include <libkern/OSAtomic.h>
86 #include <sys/fsevents.h>
87 #include <kern/thread_call.h>
91 #include <sys/vmparam.h>
93 #include <sys/fcntl.h>
95 #include <netinet/in.h>
97 #include <nfs/nfsproto.h>
98 #include <nfs/rpcv2.h>
100 #include <nfs/xdr_subs.h>
101 #include <nfs/nfsm_subs.h>
102 #include <nfs/nfsrvcache.h>
103 #include <nfs/nfs_gss.h>
106 #include <security/mac.h>
107 #include <security/mac_framework.h>
116 int nfsd_thread_count
= 0;
117 int nfsd_thread_max
= 0;
118 lck_grp_t
*nfsd_lck_grp
;
119 lck_mtx_t
*nfsd_mutex
;
120 struct nfsd_head nfsd_head
, nfsd_queue
;
122 lck_grp_t
*nfsrv_slp_rwlock_group
;
123 lck_grp_t
*nfsrv_slp_mutex_group
;
124 struct nfsrv_sockhead nfsrv_socklist
, nfsrv_sockwg
,
125 nfsrv_sockwait
, nfsrv_sockwork
;
126 struct nfsrv_sock
*nfsrv_udpsock
= NULL
;
127 struct nfsrv_sock
*nfsrv_udp6sock
= NULL
;
130 struct nfsrv_expfs_list nfsrv_exports
;
131 struct nfsrv_export_hashhead
*nfsrv_export_hashtbl
= NULL
;
132 int nfsrv_export_hash_size
= NFSRVEXPHASHSZ
;
133 u_long nfsrv_export_hash
;
134 lck_grp_t
*nfsrv_export_rwlock_group
;
135 lck_rw_t nfsrv_export_rwlock
;
138 /* NFS server file modification event generator */
139 struct nfsrv_fmod_hashhead
*nfsrv_fmod_hashtbl
;
140 u_long nfsrv_fmod_hash
;
141 lck_grp_t
*nfsrv_fmod_grp
;
142 lck_mtx_t
*nfsrv_fmod_mutex
;
143 static int nfsrv_fmod_timer_on
= 0;
144 int nfsrv_fsevents_enabled
= 1;
147 /* NFS server timers */
149 thread_call_t nfsrv_fmod_timer_call
;
151 thread_call_t nfsrv_idlesock_timer_call
;
152 thread_call_t nfsrv_wg_timer_call
;
153 int nfsrv_wg_timer_on
;
155 /* globals for the active user list */
156 uint32_t nfsrv_user_stat_enabled
= 1;
157 uint32_t nfsrv_user_stat_node_count
= 0;
158 uint32_t nfsrv_user_stat_max_idle_sec
= NFSRV_USER_STAT_DEF_IDLE_SEC
;
159 uint32_t nfsrv_user_stat_max_nodes
= NFSRV_USER_STAT_DEF_MAX_NODES
;
160 lck_grp_t
*nfsrv_active_user_mutex_group
;
162 int nfsrv_wg_delay
= NFSRV_WGATHERDELAY
* 1000;
163 int nfsrv_wg_delay_v3
= 0;
167 int nfsrv_authorize(vnode_t
, vnode_t
, kauth_action_t
, vfs_context_t
, struct nfs_export_options
*, int);
168 int nfsrv_wg_coalesce(struct nfsrv_descript
*, struct nfsrv_descript
*);
169 void nfsrv_modified(vnode_t
, vfs_context_t
);
171 extern void IOSleep(int);
172 extern int safe_getpath(struct vnode
*dvp
, char *leafname
, char *path
, int _len
, int *truncated_path
);
175 * Initialize the data structures for the server.
178 #define NFSRV_NOT_INITIALIZED 0
179 #define NFSRV_INITIALIZING 1
180 #define NFSRV_INITIALIZED 2
181 static volatile UInt32 nfsrv_initted
= NFSRV_NOT_INITIALIZED
;
184 nfsrv_is_initialized(void)
186 return nfsrv_initted
== NFSRV_INITIALIZED
;
192 /* make sure we init only once */
193 if (!OSCompareAndSwap(NFSRV_NOT_INITIALIZED
, NFSRV_INITIALIZING
, &nfsrv_initted
)) {
194 /* wait until initialization is complete */
195 while (!nfsrv_is_initialized()) {
201 if (sizeof(struct nfsrv_sock
) > NFS_SVCALLOC
) {
202 printf("struct nfsrv_sock bloated (> %dbytes)\n", NFS_SVCALLOC
);
205 /* init nfsd mutex */
206 nfsd_lck_grp
= lck_grp_alloc_init("nfsd", LCK_GRP_ATTR_NULL
);
207 nfsd_mutex
= lck_mtx_alloc_init(nfsd_lck_grp
, LCK_ATTR_NULL
);
209 /* init slp rwlock */
210 nfsrv_slp_rwlock_group
= lck_grp_alloc_init("nfsrv-slp-rwlock", LCK_GRP_ATTR_NULL
);
211 nfsrv_slp_mutex_group
= lck_grp_alloc_init("nfsrv-slp-mutex", LCK_GRP_ATTR_NULL
);
213 /* init export data structures */
214 LIST_INIT(&nfsrv_exports
);
215 nfsrv_export_rwlock_group
= lck_grp_alloc_init("nfsrv-export-rwlock", LCK_GRP_ATTR_NULL
);
216 lck_rw_init(&nfsrv_export_rwlock
, nfsrv_export_rwlock_group
, LCK_ATTR_NULL
);
218 /* init active user list mutex structures */
219 nfsrv_active_user_mutex_group
= lck_grp_alloc_init("nfs-active-user-mutex", LCK_GRP_ATTR_NULL
);
221 /* init nfs server request cache mutex */
222 nfsrv_reqcache_lck_grp
= lck_grp_alloc_init("nfsrv_reqcache", LCK_GRP_ATTR_NULL
);
223 nfsrv_reqcache_mutex
= lck_mtx_alloc_init(nfsrv_reqcache_lck_grp
, LCK_ATTR_NULL
);
226 /* init NFS server file modified event generation */
227 nfsrv_fmod_hashtbl
= hashinit(NFSRVFMODHASHSZ
, M_TEMP
, &nfsrv_fmod_hash
);
228 nfsrv_fmod_grp
= lck_grp_alloc_init("nfsrv_fmod", LCK_GRP_ATTR_NULL
);
229 nfsrv_fmod_mutex
= lck_mtx_alloc_init(nfsrv_fmod_grp
, LCK_ATTR_NULL
);
232 /* initialize NFS server timer callouts */
234 nfsrv_fmod_timer_call
= thread_call_allocate(nfsrv_fmod_timer
, NULL
);
236 nfsrv_idlesock_timer_call
= thread_call_allocate(nfsrv_idlesock_timer
, NULL
);
237 nfsrv_wg_timer_call
= thread_call_allocate(nfsrv_wg_timer
, NULL
);
239 /* Init server data structures */
240 TAILQ_INIT(&nfsrv_socklist
);
241 TAILQ_INIT(&nfsrv_sockwait
);
242 TAILQ_INIT(&nfsrv_sockwork
);
243 TAILQ_INIT(&nfsrv_sockwg
);
244 TAILQ_INIT(&nfsd_head
);
245 TAILQ_INIT(&nfsd_queue
);
246 nfsrv_udpsock
= NULL
;
247 nfsrv_udp6sock
= NULL
;
249 /* Setup the up-call handling */
252 /* initialization complete */
253 nfsrv_initted
= NFSRV_INITIALIZED
;
259 * NFS version 2 and 3 server request processing functions
261 * These functions take the following parameters:
263 * struct nfsrv_descript *nd - the NFS request descriptor
264 * struct nfsrv_sock *slp - the NFS socket the request came in on
265 * vfs_context_t ctx - VFS context
266 * mbuf_t *mrepp - pointer to hold the reply mbuf list
268 * These routines generally have 3 phases:
270 * 1 - break down and validate the RPC request in the mbuf chain
271 * provided in nd->nd_nmreq.
272 * 2 - perform the vnode operations for the request
273 * (many are very similar to syscalls in vfs_syscalls.c and
274 * should therefore be kept in sync with those implementations)
275 * 3 - build the RPC reply in an mbuf chain (nmrep) and return the mbuf chain
280 * nfs v3 access service
284 struct nfsrv_descript
*nd
,
285 struct nfsrv_sock
*slp
,
289 struct nfsm_chain
*nmreq
, nmrep
;
292 struct vnode_attr vattr
;
293 struct nfs_filehandle nfh
;
295 kauth_action_t testaction
;
296 struct nfs_export
*nx
;
297 struct nfs_export_options
*nxo
;
302 nmreq
= &nd
->nd_nmreq
;
303 nfsm_chain_null(&nmrep
);
307 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
308 nfsm_chain_get_32(error
, nmreq
, nfsmode
);
310 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
313 /* update export stats */
314 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
316 /* update active user stats */
317 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
319 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
323 * Each NFS mode bit is tested separately.
325 * XXX this code is nominally correct, but returns a pessimistic
326 * rather than optimistic result. It will be necessary to add
327 * an NFS-specific interface to the vnode_authorize code to
328 * obtain good performance in the optimistic mode.
330 if (nfsmode
& NFS_ACCESS_READ
) {
331 testaction
= vnode_isdir(vp
) ? KAUTH_VNODE_LIST_DIRECTORY
: KAUTH_VNODE_READ_DATA
;
332 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
333 nfsmode
&= ~NFS_ACCESS_READ
;
336 if ((nfsmode
& NFS_ACCESS_LOOKUP
) &&
338 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_SEARCH
, ctx
, nxo
, 0))) {
339 nfsmode
&= ~NFS_ACCESS_LOOKUP
;
341 if (nfsmode
& NFS_ACCESS_MODIFY
) {
342 if (vnode_isdir(vp
)) {
344 KAUTH_VNODE_ADD_FILE
|
345 KAUTH_VNODE_ADD_SUBDIRECTORY
|
346 KAUTH_VNODE_DELETE_CHILD
;
349 KAUTH_VNODE_WRITE_DATA
;
351 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
352 nfsmode
&= ~NFS_ACCESS_MODIFY
;
355 if (nfsmode
& NFS_ACCESS_EXTEND
) {
356 if (vnode_isdir(vp
)) {
358 KAUTH_VNODE_ADD_FILE
|
359 KAUTH_VNODE_ADD_SUBDIRECTORY
;
362 KAUTH_VNODE_WRITE_DATA
|
363 KAUTH_VNODE_APPEND_DATA
;
365 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0)) {
366 nfsmode
&= ~NFS_ACCESS_EXTEND
;
371 * Note concerning NFS_ACCESS_DELETE:
372 * For hard links, the answer may be wrong if the vnode
373 * has multiple parents with different permissions.
374 * Also, some clients (e.g. MacOSX 10.3) may incorrectly
375 * interpret the missing/cleared DELETE bit.
376 * So we'll just leave the DELETE bit alone. At worst,
377 * we're telling the client it might be able to do
378 * something it really can't.
381 if ((nfsmode
& NFS_ACCESS_EXECUTE
) &&
383 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 0))) {
384 nfsmode
&= ~NFS_ACCESS_EXECUTE
;
387 /* get postop attributes */
388 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
389 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
393 nd
->nd_repstat
= error
;
394 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(NFS_VER3
) + NFSX_UNSIGNED
);
396 *mrepp
= nmrep
.nmc_mhead
;
397 nfsmout_on_status(nd
, error
);
398 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
399 if (!nd
->nd_repstat
) {
400 nfsm_chain_add_32(error
, &nmrep
, nfsmode
);
403 nfsm_chain_build_done(error
, &nmrep
);
408 nfsm_chain_cleanup(&nmrep
);
415 * nfs getattr service
419 struct nfsrv_descript
*nd
,
420 struct nfsrv_sock
*slp
,
424 struct nfsm_chain
*nmreq
, nmrep
;
425 struct vnode_attr vattr
;
428 struct nfs_filehandle nfh
;
429 struct nfs_export
*nx
;
430 struct nfs_export_options
*nxo
;
433 nmreq
= &nd
->nd_nmreq
;
434 nfsm_chain_null(&nmrep
);
438 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
440 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
443 /* update export stats */
444 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
446 /* update active user stats */
447 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
449 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
453 if (mac_vnode_check_open(ctx
, vp
, FREAD
)) {
459 nfsm_srv_vattr_init(&vattr
, nd
->nd_vers
);
460 error
= vnode_getattr(vp
, &vattr
, ctx
);
463 /* XXXab: Comment in the VFS code makes it sound like
464 * some arguments can be filtered out, but not
465 * what it actually means. Hopefully not like
466 * they gonna set mtime to 0 or something. For
467 * now trust there are no shenanigans here.
469 error
= mac_vnode_check_getattr(ctx
, NOCRED
, vp
, &vattr
);
478 nd
->nd_repstat
= error
;
479 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_FATTR(nd
->nd_vers
));
481 *mrepp
= nmrep
.nmc_mhead
;
482 nfsmout_if(nd
->nd_repstat
);
483 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &vattr
);
485 nfsm_chain_build_done(error
, &nmrep
);
490 nfsm_chain_cleanup(&nmrep
);
497 * nfs setattr service
501 struct nfsrv_descript
*nd
,
502 struct nfsrv_sock
*slp
,
506 struct nfsm_chain
*nmreq
, nmrep
;
507 struct vnode_attr preattr
, postattr
;
508 struct vnode_attr vattr
, *vap
= &vattr
;
510 struct nfs_export
*nx
;
511 struct nfs_export_options
*nxo
;
512 int error
, preattrerr
, postattrerr
, gcheck
;
513 struct nfs_filehandle nfh
;
514 struct timespec guard
= { .tv_sec
= 0, .tv_nsec
= 0 };
515 kauth_action_t action
;
519 preattrerr
= postattrerr
= ENOENT
;
521 nmreq
= &nd
->nd_nmreq
;
522 nfsm_chain_null(&nmrep
);
526 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
530 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
531 if (nd
->nd_vers
== NFS_VER3
) {
532 nfsm_chain_get_32(error
, nmreq
, gcheck
);
534 nfsm_chain_get_time(error
, nmreq
, nd
->nd_vers
, guard
.tv_sec
, guard
.tv_nsec
);
540 * Save the original credential UID in case they are
541 * mapped and we need to map the IDs in the attributes.
543 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
546 * Now that we have all the fields, lets do it.
548 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
551 /* update export stats */
552 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
554 /* update active user stats */
555 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
557 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
560 if (nd
->nd_vers
== NFS_VER3
) {
561 nfsm_srv_pre_vattr_init(&preattr
);
562 error
= preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
563 if (!error
&& gcheck
&& VATTR_IS_SUPPORTED(&preattr
, va_change_time
) &&
564 (preattr
.va_change_time
.tv_sec
!= guard
.tv_sec
||
565 preattr
.va_change_time
.tv_nsec
!= guard
.tv_nsec
)) {
566 error
= NFSERR_NOT_SYNC
;
568 if (!preattrerr
&& !VATTR_ALL_SUPPORTED(&preattr
)) {
575 * If the credentials were mapped, we should
576 * map the same values in the attributes.
578 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
580 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
581 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
) {
582 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
586 /* Authorize the attribute changes. */
587 error
= vnode_authattr(vp
, vap
, &action
, ctx
);
589 error
= nfsrv_authorize(vp
, NULL
, action
, ctx
, nxo
, 0);
593 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
| FWRITE
)) {
599 if (VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
)) {
600 error
= mac_vnode_check_setowner(ctx
, vp
,
601 VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: -1,
602 VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: -1);
605 if (!error
&& VATTR_IS_ACTIVE(vap
, va_mode
)) {
606 error
= mac_vnode_check_setmode(ctx
, vp
, (mode_t
)vap
->va_mode
);
609 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
610 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
611 error
= mac_vnode_check_truncate(ctx
, NOCRED
, vp
);
613 /* set utimes case */
614 if (!error
&& (VATTR_IS_ACTIVE(vap
, va_access_time
) || VATTR_IS_ACTIVE(vap
, va_modify_time
))) {
615 struct timespec current_time
;
616 nanotime(¤t_time
);
618 error
= mac_vnode_check_setutimes(ctx
, vp
,
619 VATTR_IS_ACTIVE(vap
, va_access_time
) ? vap
->va_access_time
: current_time
,
620 VATTR_IS_ACTIVE(vap
, va_modify_time
) ? vap
->va_modify_time
: current_time
);
624 /* set the new attributes */
626 error
= vnode_setattr(vp
, vap
, ctx
);
629 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
630 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
631 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
643 nd
->nd_repstat
= error
;
644 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCORFATTR(nd
->nd_vers
));
646 *mrepp
= nmrep
.nmc_mhead
;
647 nfsmout_on_status(nd
, error
);
648 if (nd
->nd_vers
== NFS_VER3
) {
649 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
650 preattrerr
, &preattr
, postattrerr
, &postattr
);
652 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
655 nfsm_chain_build_done(error
, &nmrep
);
657 nfsm_chain_cleanup(&nmrep
);
668 struct nfsrv_descript
*nd
,
669 struct nfsrv_sock
*slp
,
674 vnode_t vp
, dirp
= NULL
;
675 struct nfs_filehandle dnfh
, nfh
;
676 struct nfs_export
*nx
= NULL
;
677 struct nfs_export_options
*nxo
;
678 int error
, attrerr
, dirattrerr
, isdotdot
;
681 struct vnode_attr va
, dirattr
, *vap
= &va
;
682 struct nfsm_chain
*nmreq
, nmrep
;
685 attrerr
= dirattrerr
= ENOENT
;
686 nmreq
= &nd
->nd_nmreq
;
687 nfsm_chain_null(&nmrep
);
688 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
690 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
691 nfsm_chain_get_32(error
, nmreq
, len
);
692 nfsm_name_len_check(error
, nd
, len
);
695 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
697 ni
.ni_op
= OP_LOOKUP
;
699 ni
.ni_cnd
.cn_flags
= LOCKLEAF
;
700 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
701 isdotdot
= ((len
== 2) && (ni
.ni_cnd
.cn_pnbuf
[0] == '.') && (ni
.ni_cnd
.cn_pnbuf
[1] == '.'));
703 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
705 /* update export stats */
706 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
708 /* update active user stats */
709 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
711 if (!error
&& mac_vnode_check_open(ctx
, ni
.ni_vp
, FREAD
)) {
726 if (nd
->nd_vers
== NFS_VER3
) {
727 nfsm_srv_vattr_init(&dirattr
, NFS_VER3
);
728 dirattrerr
= vnode_getattr(dirp
, &dirattr
, ctx
);
737 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, (isdotdot
? &dnfh
: NULL
), vp
, ctx
, &nfh
);
739 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
740 attrerr
= vnode_getattr(vp
, vap
, ctx
);
746 nd
->nd_repstat
= error
;
747 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
748 NFSX_POSTOPORFATTR(nd
->nd_vers
) + NFSX_POSTOPATTR(nd
->nd_vers
));
750 *mrepp
= nmrep
.nmc_mhead
;
751 if (nd
->nd_repstat
) {
752 if (nd
->nd_vers
== NFS_VER3
) {
753 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
757 nfsm_chain_add_fh(error
, &nmrep
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
758 if (nd
->nd_vers
== NFS_VER3
) {
759 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
760 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
762 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
765 nfsm_chain_build_done(error
, &nmrep
);
767 nfsm_chain_cleanup(&nmrep
);
774 * nfs readlink service
778 struct nfsrv_descript
*nd
,
779 struct nfsrv_sock
*slp
,
783 int error
, mpcnt
, tlen
, len
, attrerr
;
785 struct vnode_attr vattr
;
786 struct nfs_filehandle nfh
;
787 struct nfs_export
*nx
;
788 struct nfs_export_options
*nxo
;
789 struct nfsm_chain
*nmreq
, nmrep
;
792 char uio_buf
[UIO_SIZEOF(4)];
793 char *uio_bufp
= &uio_buf
[0];
794 int uio_buflen
= UIO_SIZEOF(4);
798 nmreq
= &nd
->nd_nmreq
;
799 nfsm_chain_null(&nmrep
);
802 len
= NFS_MAXPATHLEN
;
804 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
807 /* get mbuf list to hold symlink path */
808 error
= nfsm_mbuf_get_list(len
, &mpath
, &mpcnt
);
811 uio_buflen
= UIO_SIZEOF(mpcnt
);
812 MALLOC(uio_bufp
, char*, uio_buflen
, M_TEMP
, M_WAITOK
);
818 auio
= uio_createwithbuffer(mpcnt
, 0, UIO_SYSSPACE
, UIO_READ
, uio_bufp
, uio_buflen
);
824 for (mp
= mpath
; mp
; mp
= mbuf_next(mp
)) {
825 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(mp
)), mbuf_len(mp
));
828 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
831 /* update export stats */
832 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
834 /* update active user stats */
835 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
837 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
840 if (vnode_vtype(vp
) != VLNK
) {
841 if (nd
->nd_vers
== NFS_VER3
) {
849 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 0);
852 if (mac_vnode_check_open(ctx
, vp
, FREAD
)) {
857 error
= mac_vnode_check_readlink(ctx
, vp
);
861 error
= VNOP_READLINK(vp
, auio
, ctx
);
864 if (nd
->nd_vers
== NFS_VER3
) {
865 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
866 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
878 nd
->nd_repstat
= error
;
879 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_UNSIGNED
);
881 *mrepp
= nmrep
.nmc_mhead
;
882 nfsmout_on_status(nd
, error
);
883 if (nd
->nd_vers
== NFS_VER3
) {
884 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
886 if (error
|| nd
->nd_repstat
) {
887 nfsm_chain_build_done(error
, &nmrep
);
890 if (auio
&& (uio_resid(auio
) > 0)) {
891 len
-= uio_resid(auio
);
892 tlen
= nfsm_rndup(len
);
893 nfsm_adj(mpath
, NFS_MAXPATHLEN
- tlen
, tlen
- len
);
895 nfsm_chain_add_32(error
, &nmrep
, len
);
896 nfsm_chain_build_done(error
, &nmrep
);
898 error
= mbuf_setnext(nmrep
.nmc_mcur
, mpath
);
909 if (uio_bufp
!= &uio_buf
[0]) {
910 FREE(uio_bufp
, M_TEMP
);
913 nfsm_chain_cleanup(&nmrep
);
924 struct nfsrv_descript
*nd
,
925 struct nfsrv_sock
*slp
,
929 int error
, attrerr
, mreadcnt
;
930 uint32_t reqlen
, maxlen
, count
, len
, tlen
, left
;
933 struct nfs_filehandle nfh
;
934 struct nfs_export
*nx
;
935 struct nfs_export_options
*nxo
;
937 char *uio_bufp
= NULL
;
938 struct vnode_attr vattr
, *vap
= &vattr
;
941 char uio_buf
[UIO_SIZEOF(0)];
942 struct nfsm_chain
*nmreq
, nmrep
;
946 nmreq
= &nd
->nd_nmreq
;
947 nfsm_chain_null(&nmrep
);
951 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
953 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
955 if (nd
->nd_vers
== NFS_VER3
) {
956 nfsm_chain_get_64(error
, nmreq
, off
);
958 nfsm_chain_get_32(error
, nmreq
, off
);
960 nfsm_chain_get_32(error
, nmreq
, reqlen
);
961 maxlen
= NFSRV_NDMAXDATA(nd
);
962 if (reqlen
> maxlen
) {
966 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
969 /* update export stats */
970 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
972 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
975 if (vnode_vtype(vp
) != VREG
) {
976 if (nd
->nd_vers
== NFS_VER3
) {
979 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
984 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 1))) {
985 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 1);
990 error
= mac_vnode_check_open(ctx
, vp
, FREAD
);
994 /* XXXab: Do we need to do this?! */
995 error
= mac_vnode_check_read(ctx
, vfs_context_ucred(ctx
), vp
);
999 /* mac_vnode_check_exec() can't be done here. */
1004 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
1005 attrerr
= vnode_getattr(vp
, vap
, ctx
);
1011 if ((u_quad_t
)off
>= vap
->va_data_size
) {
1013 } else if (((u_quad_t
)off
+ reqlen
) > vap
->va_data_size
) {
1014 count
= nfsm_rndup(vap
->va_data_size
- off
);
1021 /* get mbuf list to hold read data */
1022 error
= nfsm_mbuf_get_list(count
, &mread
, &mreadcnt
);
1024 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mreadcnt
), M_TEMP
, M_WAITOK
);
1026 auio
= uio_createwithbuffer(mreadcnt
, off
, UIO_SYSSPACE
,
1027 UIO_READ
, uio_bufp
, UIO_SIZEOF(mreadcnt
));
1029 if (!uio_bufp
|| !auio
) {
1033 for (m
= mread
; m
; m
= mbuf_next(m
)) {
1034 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mbuf_len(m
));
1036 error
= VNOP_READ(vp
, auio
, IO_NODELOCKED
, ctx
);
1038 auio
= uio_createwithbuffer(0, 0, UIO_SYSSPACE
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
1046 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
1047 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
1048 attrerr
= vnode_getattr(vp
, vap
, ctx
);
1049 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
1050 error
= attrerr
; /* NFSv2 must have attributes to return */
1058 /* trim off any data not actually read */
1059 len
-= uio_resid(auio
);
1060 tlen
= nfsm_rndup(len
);
1061 if (count
!= tlen
|| tlen
!= len
) {
1062 nfsm_adj(mread
, count
- tlen
, tlen
- len
);
1066 /* assemble reply */
1067 nd
->nd_repstat
= error
;
1068 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPORFATTR(nd
->nd_vers
) + 3 * NFSX_UNSIGNED
);
1070 *mrepp
= nmrep
.nmc_mhead
;
1071 nfsmout_on_status(nd
, error
);
1072 if (nd
->nd_vers
== NFS_VER3
) {
1073 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
1075 if (error
|| nd
->nd_repstat
) {
1076 nfsm_chain_build_done(error
, &nmrep
);
1079 if (nd
->nd_vers
== NFS_VER3
) {
1080 nfsm_chain_add_32(error
, &nmrep
, len
);
1081 nfsm_chain_add_32(error
, &nmrep
, (len
< reqlen
) ? TRUE
: FALSE
);
1083 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
1085 nfsm_chain_add_32(error
, &nmrep
, len
);
1086 nfsm_chain_build_done(error
, &nmrep
);
1088 error
= mbuf_setnext(nmrep
.nmc_mcur
, mread
);
1093 /* update export stats */
1094 NFSStatAdd64(&nx
->nx_stats
.bytes_read
, len
);
1096 /* update active user stats */
1097 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, len
, 0);
1105 if (uio_bufp
!= NULL
) {
1106 FREE(uio_bufp
, M_TEMP
);
1109 nfsm_chain_cleanup(&nmrep
);
1117 * NFS File modification reporting
1119 * When the contents of a file are changed, a "content modified"
1120 * fsevent needs to be issued. Normally this would be done at
1121 * file close time. This is difficult for NFS because the protocol
1122 * has no "close" operation. The client sends a stream of write
1123 * requests that just stop. So we keep a hash table full of
1124 * vnodes that have been written to recently, and issue a
1125 * "content modified" fsevent only if there are no writes to
1126 * a vnode for nfsrv_fmod_pendtime milliseconds.
1128 int nfsrv_fmod_pending
; /* count of vnodes being written to */
1129 int nfsrv_fmod_pendtime
= 1000; /* msec to wait */
1130 int nfsrv_fmod_min_interval
= 100; /* msec min interval between callbacks */
1133 * This function is called via the kernel's callout
1134 * mechanism. Calls are made only when there are
1135 * vnodes pending a fsevent creation, and no more
1136 * frequently than every nfsrv_fmod_min_interval ms.
1139 nfsrv_fmod_timer(__unused
void *param0
, __unused
void *param1
)
1141 struct nfsrv_fmod_hashhead
*headp
, firehead
;
1142 struct nfsrv_fmod
*fp
, *nfp
, *pfp
;
1143 uint64_t timenow
, next_deadline
;
1144 int interval
= 0, i
, fmod_fire
;
1146 LIST_INIT(&firehead
);
1147 lck_mtx_lock(nfsrv_fmod_mutex
);
1149 clock_get_uptime(&timenow
);
1150 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000,
1154 * Scan all the hash chains
1157 for (i
= 0; i
< NFSRVFMODHASHSZ
; i
++) {
1159 * For each hash chain, look for an entry
1160 * that has exceeded the deadline.
1162 headp
= &nfsrv_fmod_hashtbl
[i
];
1163 LIST_FOREACH(fp
, headp
, fm_link
) {
1164 if (timenow
>= fp
->fm_deadline
) {
1167 if (fp
->fm_deadline
< next_deadline
) {
1168 next_deadline
= fp
->fm_deadline
;
1173 * If we have an entry that's exceeded the
1174 * deadline, then the same is true for all
1175 * following entries in the chain, since they're
1176 * sorted in time order.
1180 /* move each entry to the fire list */
1181 nfp
= LIST_NEXT(fp
, fm_link
);
1182 LIST_REMOVE(fp
, fm_link
);
1185 LIST_INSERT_AFTER(pfp
, fp
, fm_link
);
1187 LIST_INSERT_HEAD(&firehead
, fp
, fm_link
);
1195 lck_mtx_unlock(nfsrv_fmod_mutex
);
1197 * Fire off the content modified fsevent for each
1198 * entry and free it.
1200 LIST_FOREACH_SAFE(fp
, &firehead
, fm_link
, nfp
) {
1201 if (nfsrv_fsevents_enabled
) {
1202 fp
->fm_context
.vc_thread
= current_thread();
1203 add_fsevent(FSE_CONTENT_MODIFIED
, &fp
->fm_context
,
1204 FSE_ARG_VNODE
, fp
->fm_vp
,
1207 vnode_put(fp
->fm_vp
);
1208 kauth_cred_unref(&fp
->fm_context
.vc_ucred
);
1209 LIST_REMOVE(fp
, fm_link
);
1212 lck_mtx_lock(nfsrv_fmod_mutex
);
1213 nfsrv_fmod_pending
-= fmod_fire
;
1218 * If there are still pending entries, set up another
1219 * callout to handle them later. Set the timeout deadline
1220 * so that the callout happens when the oldest pending
1221 * entry is ready to send its fsevent.
1223 if (nfsrv_fmod_pending
> 0) {
1224 interval
= (next_deadline
- timenow
) / (1000 * 1000);
1225 if (interval
< nfsrv_fmod_min_interval
) {
1226 interval
= nfsrv_fmod_min_interval
;
1230 nfsrv_fmod_timer_on
= interval
> 0;
1231 if (nfsrv_fmod_timer_on
) {
1232 nfs_interval_timer_start(nfsrv_fmod_timer_call
, interval
);
1235 lck_mtx_unlock(nfsrv_fmod_mutex
);
1239 * When a vnode has been written to, enter it in the hash
1240 * table of vnodes pending creation of an fsevent. If the
1241 * callout timer isn't already running, schedule a callback
1242 * for nfsrv_fmod_pendtime msec from now.
1245 nfsrv_modified(vnode_t vp
, vfs_context_t ctx
)
1248 struct nfsrv_fmod
*fp
;
1249 struct nfsrv_fmod_hashhead
*head
;
1251 lck_mtx_lock(nfsrv_fmod_mutex
);
1254 * Compute the time in the future when the
1255 * content modified fsevent is to be issued.
1257 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000, &deadline
);
1260 * Check if there's already a file content change fsevent
1261 * pending for this vnode. If there is, update its
1262 * timestamp and make sure it's at the front of the hash chain.
1264 head
= &nfsrv_fmod_hashtbl
[NFSRVFMODHASH(vp
)];
1265 LIST_FOREACH(fp
, head
, fm_link
) {
1266 if (vp
== fp
->fm_vp
) {
1267 fp
->fm_deadline
= deadline
;
1268 if (fp
!= LIST_FIRST(head
)) {
1269 LIST_REMOVE(fp
, fm_link
);
1270 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1272 lck_mtx_unlock(nfsrv_fmod_mutex
);
1278 * First content change fsevent for this vnode.
1279 * Allocate a new file mod entry and add it
1280 * on the front of the hash chain.
1282 if (vnode_get(vp
) != 0) {
1285 MALLOC(fp
, struct nfsrv_fmod
*, sizeof(*fp
), M_TEMP
, M_WAITOK
);
1291 kauth_cred_ref(vfs_context_ucred(ctx
));
1292 fp
->fm_context
= *ctx
;
1293 fp
->fm_deadline
= deadline
;
1294 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1297 * If added to an empty hash table, then set the
1298 * callout timer to go off after nfsrv_fmod_pendtime.
1300 nfsrv_fmod_pending
++;
1301 if (!nfsrv_fmod_timer_on
) {
1302 nfsrv_fmod_timer_on
= 1;
1303 nfs_interval_timer_start(nfsrv_fmod_timer_call
,
1304 nfsrv_fmod_pendtime
);
1307 lck_mtx_unlock(nfsrv_fmod_mutex
);
1310 #endif /* CONFIG_FSE */
1317 struct nfsrv_descript
*nd
,
1318 struct nfsrv_sock
*slp
,
1322 struct vnode_attr preattr
, postattr
;
1323 int error
, preattrerr
, postattrerr
;
1324 int ioflags
, len
, retlen
;
1326 int stable
= NFS_WRITE_FILESYNC
;
1329 struct nfs_filehandle nfh
;
1330 struct nfs_export
*nx
;
1331 struct nfs_export_options
*nxo
;
1333 char *uio_bufp
= NULL
;
1336 struct nfsm_chain
*nmreq
, nmrep
;
1338 if (nd
->nd_nmreq
.nmc_mhead
== NULL
) {
1344 preattrerr
= postattrerr
= ENOENT
;
1345 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1346 nmreq
= &nd
->nd_nmreq
;
1347 nfsm_chain_null(&nmrep
);
1351 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
1353 if (nd
->nd_vers
== NFS_VER3
) {
1354 nfsm_chain_get_64(error
, nmreq
, off
);
1355 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1356 nfsm_chain_get_32(error
, nmreq
, stable
);
1358 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1359 nfsm_chain_get_32(error
, nmreq
, off
);
1360 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1362 stable
= NFS_WRITE_UNSTABLE
;
1365 nfsm_chain_get_32(error
, nmreq
, len
);
1370 * For NFS Version 2, it is not obvious what a write of zero length
1371 * should do, but I might as well be consistent with Version 3,
1372 * which is to return ok so long as there are no permission problems.
1376 error
= nfsm_chain_trim_data(nmreq
, len
, &mlen
);
1381 if ((len
> NFSRV_MAXDATA
) || (len
< 0) || (mlen
< len
)) {
1385 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
1388 /* update export stats */
1389 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1391 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1394 if (nd
->nd_vers
== NFS_VER3
) {
1395 nfsm_srv_pre_vattr_init(&preattr
);
1396 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1398 if (vnode_vtype(vp
) != VREG
) {
1399 if (nd
->nd_vers
== NFS_VER3
) {
1402 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1406 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1412 error
= mac_vnode_check_open(ctx
, vp
, FWRITE
);
1416 /* XXXab: Do we need to do this?! */
1417 error
= mac_vnode_check_write(ctx
, vfs_context_ucred(ctx
), vp
);
1427 for (mcount
= 0, m
= nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
)) {
1428 if (mbuf_len(m
) > 0) {
1432 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mcount
), M_TEMP
, M_WAITOK
);
1434 auio
= uio_createwithbuffer(mcount
, off
, UIO_SYSSPACE
, UIO_WRITE
, uio_bufp
, UIO_SIZEOF(mcount
));
1436 if (!uio_bufp
|| !auio
) {
1440 for (m
= nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
)) {
1441 if ((mlen
= mbuf_len(m
)) > 0) {
1442 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mlen
);
1446 * XXX The IO_METASYNC flag indicates that all metadata (and not just
1447 * enough to ensure data integrity) mus be written to stable storage
1448 * synchronously. (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
1450 if (stable
== NFS_WRITE_UNSTABLE
) {
1451 ioflags
= IO_NODELOCKED
;
1452 } else if (stable
== NFS_WRITE_DATASYNC
) {
1453 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1455 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1458 error
= VNOP_WRITE(vp
, auio
, ioflags
, ctx
);
1459 OSAddAtomic64(1, &nfsstats
.srvvop_writes
);
1461 /* update export stats */
1462 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, len
);
1464 /* update active user stats */
1465 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, len
);
1468 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
)) {
1469 nfsrv_modified(vp
, ctx
);
1473 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1474 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1475 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
1476 error
= postattrerr
; /* NFSv2 must have attributes to return */
1482 /* assemble reply */
1483 nd
->nd_repstat
= error
;
1484 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1485 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1486 NFSX_WRITEVERF(nd
->nd_vers
));
1488 *mrepp
= nmrep
.nmc_mhead
;
1489 nfsmout_on_status(nd
, error
);
1490 if (nd
->nd_vers
== NFS_VER3
) {
1491 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1492 preattrerr
, &preattr
, postattrerr
, &postattr
);
1493 nfsmout_if(error
|| nd
->nd_repstat
);
1494 nfsm_chain_add_32(error
, &nmrep
, retlen
);
1495 /* If nfsrv_async is set, then pretend the write was FILESYNC. */
1496 if ((stable
== NFS_WRITE_UNSTABLE
) && !nfsrv_async
) {
1497 nfsm_chain_add_32(error
, &nmrep
, stable
);
1499 nfsm_chain_add_32(error
, &nmrep
, NFS_WRITE_FILESYNC
);
1501 /* write verifier */
1502 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1503 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1505 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1508 nfsm_chain_build_done(error
, &nmrep
);
1512 if (uio_bufp
!= NULL
) {
1513 FREE(uio_bufp
, M_TEMP
);
1516 nfsm_chain_cleanup(&nmrep
);
1523 * NFS write service with write gathering support. Called when
1524 * nfsrv_wg_delay > 0.
1525 * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
1526 * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
1530 #define NWDELAYHASH(sock, f) \
1531 (&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ])
1532 /* These macros compare nfsrv_descript structures. */
1533 #define NFSW_CONTIG(o, n) \
1534 (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh))
1536 * XXX The following is an incorrect comparison; it fails to take into account
1537 * XXX scoping of MAC labels, but we currently lack KPI for credential
1540 #define NFSW_SAMECRED(o, n) \
1541 (!bcmp((caddr_t)(o)->nd_cr, (caddr_t)(n)->nd_cr, \
1542 sizeof (struct ucred)))
1546 struct nfsrv_descript
**ndp
,
1547 struct nfsrv_sock
*slp
,
1551 struct nfsrv_descript
*nd
, *wp
, *owp
, *swp
;
1552 struct nfs_export
*nx
;
1553 struct nfs_export_options
*nxo
;
1554 struct nfsrv_wg_delayhash
*wpp
;
1556 struct vnode_attr preattr
, postattr
;
1557 int error
, mlen
, i
, ioflags
, tlen
;
1558 int preattrerr
, postattrerr
;
1562 char *uio_bufp
= NULL
;
1565 struct nfsm_chain
*nmreq
, nmrep
;
1568 preattrerr
= postattrerr
= ENOENT
;
1569 nfsm_chain_null(&nmrep
);
1576 nmreq
= &nd
->nd_nmreq
;
1577 LIST_INIT(&nd
->nd_coalesce
);
1579 nd
->nd_stable
= NFS_WRITE_FILESYNC
;
1581 cur_usec
= (u_quad_t
)now
.tv_sec
* 1000000 + (u_quad_t
)now
.tv_usec
;
1582 nd
->nd_time
= cur_usec
+
1583 ((nd
->nd_vers
== NFS_VER3
) ? nfsrv_wg_delay_v3
: nfsrv_wg_delay
);
1585 /* Now, get the write header... */
1586 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nd
->nd_fh
.nfh_fhp
, nd
->nd_fh
.nfh_len
);
1587 /* XXX shouldn't we be checking for invalid FHs before doing any more work? */
1589 if (nd
->nd_vers
== NFS_VER3
) {
1590 nfsm_chain_get_64(error
, nmreq
, nd
->nd_off
);
1591 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1592 nfsm_chain_get_32(error
, nmreq
, nd
->nd_stable
);
1594 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1595 nfsm_chain_get_32(error
, nmreq
, nd
->nd_off
);
1596 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1598 nd
->nd_stable
= NFS_WRITE_UNSTABLE
;
1601 nfsm_chain_get_32(error
, nmreq
, nd
->nd_len
);
1603 nd
->nd_eoff
= nd
->nd_off
+ nd
->nd_len
;
1605 if (nd
->nd_len
> 0) {
1606 error
= nfsm_chain_trim_data(nmreq
, nd
->nd_len
, &mlen
);
1612 if ((nd
->nd_len
> NFSRV_MAXDATA
) || (nd
->nd_len
< 0) || (mlen
< nd
->nd_len
)) {
1615 nd
->nd_repstat
= error
;
1616 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1618 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1619 if (nd
->nd_vers
== NFS_VER3
) {
1620 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1621 preattrerr
, &preattr
, postattrerr
, &postattr
);
1624 nfsm_chain_build_done(error
, &nmrep
);
1629 * Add this entry to the hash and time queues.
1631 lck_mtx_lock(&slp
->ns_wgmutex
);
1633 wp
= slp
->ns_tq
.lh_first
;
1634 while (wp
&& wp
->nd_time
< nd
->nd_time
) {
1636 wp
= wp
->nd_tq
.le_next
;
1639 LIST_INSERT_AFTER(owp
, nd
, nd_tq
);
1641 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1644 wpp
= NWDELAYHASH(slp
, nd
->nd_fh
.nfh_fid
);
1647 while (wp
&& !nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1649 wp
= wp
->nd_hash
.le_next
;
1651 while (wp
&& (wp
->nd_off
< nd
->nd_off
) &&
1652 nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1654 wp
= wp
->nd_hash
.le_next
;
1657 LIST_INSERT_AFTER(owp
, nd
, nd_hash
);
1659 * Search the hash list for overlapping entries and
1662 for (; nd
&& NFSW_CONTIG(owp
, nd
); nd
= wp
) {
1663 wp
= nd
->nd_hash
.le_next
;
1664 if (NFSW_SAMECRED(owp
, nd
)) {
1665 nfsrv_wg_coalesce(owp
, nd
);
1669 LIST_INSERT_HEAD(wpp
, nd
, nd_hash
);
1673 lck_mtx_lock(&slp
->ns_wgmutex
);
1677 * Now, do VNOP_WRITE()s for any one(s) that need to be done now
1678 * and generate the associated reply mbuf list(s).
1682 cur_usec
= (u_quad_t
)now
.tv_sec
* 1000000 + (u_quad_t
)now
.tv_usec
;
1683 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= owp
) {
1684 owp
= nd
->nd_tq
.le_next
;
1685 if (nd
->nd_time
> cur_usec
) {
1691 LIST_REMOVE(nd
, nd_tq
);
1692 LIST_REMOVE(nd
, nd_hash
);
1693 nmreq
= &nd
->nd_nmreq
;
1694 preattrerr
= postattrerr
= ENOENT
;
1696 /* save the incoming uid before mapping, */
1697 /* for updating active user stats later */
1698 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1700 error
= nfsrv_fhtovp(&nd
->nd_fh
, nd
, &vp
, &nx
, &nxo
);
1702 /* update per-export stats */
1703 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1705 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1711 if (nd
->nd_vers
== NFS_VER3
) {
1712 nfsm_srv_pre_vattr_init(&preattr
);
1713 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1715 if (vnode_vtype(vp
) != VREG
) {
1716 if (nd
->nd_vers
== NFS_VER3
) {
1719 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1726 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1729 if (nd
->nd_stable
== NFS_WRITE_UNSTABLE
) {
1730 ioflags
= IO_NODELOCKED
;
1731 } else if (nd
->nd_stable
== NFS_WRITE_DATASYNC
) {
1732 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1734 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1737 if (!error
&& ((nd
->nd_eoff
- nd
->nd_off
) > 0)) {
1738 for (i
= 0, m
= nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
)) {
1739 if (mbuf_len(m
) > 0) {
1744 MALLOC(uio_bufp
, char *, UIO_SIZEOF(i
), M_TEMP
, M_WAITOK
);
1746 auio
= uio_createwithbuffer(i
, nd
->nd_off
, UIO_SYSSPACE
,
1747 UIO_WRITE
, uio_bufp
, UIO_SIZEOF(i
));
1749 if (!uio_bufp
|| !auio
) {
1753 for (m
= nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
)) {
1754 if ((tlen
= mbuf_len(m
)) > 0) {
1755 uio_addiov(auio
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), tlen
);
1758 error
= VNOP_WRITE(vp
, auio
, ioflags
, ctx
);
1759 OSAddAtomic64(1, &nfsstats
.srvvop_writes
);
1761 /* update export stats */
1762 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, nd
->nd_len
);
1763 /* update active user stats */
1764 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, nd
->nd_len
);
1767 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
)) {
1768 nfsrv_modified(vp
, ctx
);
1773 FREE(uio_bufp
, M_TEMP
);
1778 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1779 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1784 * Loop around generating replies for all write rpcs that have
1785 * now been completed.
1790 nd
->nd_repstat
= error
;
1791 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1792 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1793 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1794 preattrerr
, &preattr
, postattrerr
, &postattr
);
1797 nd
->nd_repstat
= error
;
1798 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1799 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1800 NFSX_WRITEVERF(nd
->nd_vers
));
1801 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1802 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1803 preattrerr
, &preattr
, postattrerr
, &postattr
);
1804 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_len
);
1805 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_stable
);
1806 /* write verifier */
1807 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1808 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1809 } else if (!error
) {
1810 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1813 nfsm_chain_build_done(error
, &nmrep
);
1815 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1818 * Done. Put it at the head of the timer queue so that
1819 * the final phase can return the reply.
1823 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1825 nd
= swp
->nd_coalesce
.lh_first
;
1827 LIST_REMOVE(nd
, nd_tq
);
1831 LIST_INSERT_HEAD(&slp
->ns_tq
, swp
, nd_tq
);
1836 * Search for a reply to return.
1838 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= nd
->nd_tq
.le_next
) {
1840 LIST_REMOVE(nd
, nd_tq
);
1841 *mrepp
= nd
->nd_mrep
;
1846 slp
->ns_wgtime
= slp
->ns_tq
.lh_first
? slp
->ns_tq
.lh_first
->nd_time
: 0;
1847 lck_mtx_unlock(&slp
->ns_wgmutex
);
1850 * If we've just created a write pending gather,
1851 * start the timer to check on it soon to make sure
1852 * the write will be completed.
1854 * Add/Remove the socket in the nfsrv_sockwg queue as needed.
1856 lck_mtx_lock(nfsd_mutex
);
1857 if (slp
->ns_wgtime
) {
1858 if (slp
->ns_wgq
.tqe_next
== SLPNOLIST
) {
1859 TAILQ_INSERT_HEAD(&nfsrv_sockwg
, slp
, ns_wgq
);
1861 if (!nfsrv_wg_timer_on
) {
1862 nfsrv_wg_timer_on
= 1;
1863 nfs_interval_timer_start(nfsrv_wg_timer_call
,
1864 NFSRV_WGATHERDELAY
);
1866 } else if (slp
->ns_wgq
.tqe_next
!= SLPNOLIST
) {
1867 TAILQ_REMOVE(&nfsrv_sockwg
, slp
, ns_wgq
);
1868 slp
->ns_wgq
.tqe_next
= SLPNOLIST
;
1870 lck_mtx_unlock(nfsd_mutex
);
1876 * Coalesce the write request nd into owp. To do this we must:
1877 * - remove nd from the queues
1878 * - merge nd->nd_nmreq into owp->nd_nmreq
1879 * - update the nd_eoff and nd_stable for owp
1880 * - put nd on owp's nd_coalesce list
1883 nfsrv_wg_coalesce(struct nfsrv_descript
*owp
, struct nfsrv_descript
*nd
)
1887 struct nfsrv_descript
*p
;
1889 LIST_REMOVE(nd
, nd_hash
);
1890 LIST_REMOVE(nd
, nd_tq
);
1891 if (owp
->nd_eoff
< nd
->nd_eoff
) {
1892 overlap
= owp
->nd_eoff
- nd
->nd_off
;
1897 mbuf_adj(nd
->nd_nmreq
.nmc_mhead
, overlap
);
1899 mp
= owp
->nd_nmreq
.nmc_mhead
;
1900 while ((mpnext
= mbuf_next(mp
))) {
1903 error
= mbuf_setnext(mp
, nd
->nd_nmreq
.nmc_mhead
);
1907 owp
->nd_eoff
= nd
->nd_eoff
;
1909 mbuf_freem(nd
->nd_nmreq
.nmc_mhead
);
1911 nd
->nd_nmreq
.nmc_mhead
= NULL
;
1912 nd
->nd_nmreq
.nmc_mcur
= NULL
;
1913 if (nd
->nd_stable
== NFS_WRITE_FILESYNC
) {
1914 owp
->nd_stable
= NFS_WRITE_FILESYNC
;
1915 } else if ((nd
->nd_stable
== NFS_WRITE_DATASYNC
) &&
1916 (owp
->nd_stable
== NFS_WRITE_UNSTABLE
)) {
1917 owp
->nd_stable
= NFS_WRITE_DATASYNC
;
1919 LIST_INSERT_HEAD(&owp
->nd_coalesce
, nd
, nd_tq
);
1922 * If nd had anything else coalesced into it, transfer them
1923 * to owp, otherwise their replies will never get sent.
1925 while ((p
= nd
->nd_coalesce
.lh_first
)) {
1926 LIST_REMOVE(p
, nd_tq
);
1927 LIST_INSERT_HEAD(&owp
->nd_coalesce
, p
, nd_tq
);
1933 * Scan the write gathering queues for writes that need to be
1937 nfsrv_wg_timer(__unused
void *param0
, __unused
void *param1
)
1940 uint64_t cur_usec
, next_usec
;
1942 struct nfsrv_sock
*slp
;
1943 int writes_pending
= 0;
1946 cur_usec
= (uint64_t)now
.tv_sec
* 1000000 + (uint64_t)now
.tv_usec
;
1947 next_usec
= cur_usec
+ (NFSRV_WGATHERDELAY
* 1000);
1949 lck_mtx_lock(nfsd_mutex
);
1950 TAILQ_FOREACH(slp
, &nfsrv_sockwg
, ns_wgq
) {
1951 if (slp
->ns_wgtime
) {
1953 if (slp
->ns_wgtime
<= cur_usec
) {
1954 lck_rw_lock_exclusive(&slp
->ns_rwlock
);
1955 slp
->ns_flag
|= SLP_DOWRITES
;
1956 lck_rw_done(&slp
->ns_rwlock
);
1957 nfsrv_wakenfsd(slp
);
1960 if (slp
->ns_wgtime
< next_usec
) {
1961 next_usec
= slp
->ns_wgtime
;
1966 if (writes_pending
== 0) {
1967 nfsrv_wg_timer_on
= 0;
1968 lck_mtx_unlock(nfsd_mutex
);
1971 lck_mtx_unlock(nfsd_mutex
);
1974 * Return the number of msec to wait again
1976 interval
= (next_usec
- cur_usec
) / 1000;
1980 nfs_interval_timer_start(nfsrv_wg_timer_call
, interval
);
1984 * Sort the group list in increasing numerical order.
1985 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1986 * that used to be here.)
1989 nfsrv_group_sort(gid_t
*list
, int num
)
1994 /* Insertion sort. */
1995 for (i
= 1; i
< num
; i
++) {
1997 /* find correct slot for value v, moving others up */
1998 for (j
= i
; --j
>= 0 && v
< list
[j
];) {
1999 list
[j
+ 1] = list
[j
];
2006 * nfs create service
2007 * now does a truncate to 0 length via. setattr if it already exists
2011 struct nfsrv_descript
*nd
,
2012 struct nfsrv_sock
*slp
,
2016 struct vnode_attr dpreattr
, dpostattr
, postattr
;
2017 struct vnode_attr va
, *vap
= &va
;
2018 struct nameidata ni
;
2019 int error
, rdev
, dpreattrerr
, dpostattrerr
, postattrerr
;
2020 int how
, exclusive_flag
;
2021 uint32_t len
= 0, cnflags
;
2022 vnode_t vp
, dvp
, dirp
;
2023 struct nfs_filehandle nfh
;
2024 struct nfs_export
*nx
= NULL
;
2025 struct nfs_export_options
*nxo
;
2027 u_char cverf
[NFSX_V3CREATEVERF
];
2029 struct nfsm_chain
*nmreq
, nmrep
;
2032 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
2033 nmreq
= &nd
->nd_nmreq
;
2034 nfsm_chain_null(&nmrep
);
2035 vp
= dvp
= dirp
= NULL
;
2037 ni
.ni_cnd
.cn_nameiop
= 0;
2040 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2042 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2043 nfsm_chain_get_32(error
, nmreq
, len
);
2044 nfsm_name_len_check(error
, nd
, len
);
2047 ni
.ni_cnd
.cn_nameiop
= CREATE
;
2051 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2052 ni
.ni_cnd
.cn_ndp
= &ni
;
2054 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2056 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2058 /* update export stats */
2059 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2061 /* update active user stats */
2062 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2066 if (nd
->nd_vers
== NFS_VER3
) {
2067 nfsm_srv_pre_vattr_init(&dpreattr
);
2068 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2076 ni
.ni_cnd
.cn_nameiop
= 0;
2084 if (nd
->nd_vers
== NFS_VER3
) {
2085 nfsm_chain_get_32(error
, nmreq
, how
);
2088 case NFS_CREATE_GUARDED
:
2093 case NFS_CREATE_UNCHECKED
:
2094 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2096 case NFS_CREATE_EXCLUSIVE
:
2097 nfsm_chain_get_opaque(error
, nmreq
, NFSX_V3CREATEVERF
, cverf
);
2100 VATTR_SET(vap
, va_mode
, 0);
2105 VATTR_SET(vap
, va_type
, VREG
);
2109 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2111 v_type
= vap
->va_type
;
2112 if (v_type
== VNON
) {
2115 VATTR_SET(vap
, va_type
, v_type
);
2121 rdev
= vap
->va_data_size
;
2122 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2132 * If it doesn't exist, create it
2133 * otherwise just truncate to 0 length
2134 * should I set the mode too ??
2137 kauth_acl_t xacl
= NULL
;
2139 /* authorize before creating */
2140 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
2142 /* construct ACL and handle inheritance */
2144 error
= kauth_acl_inherit(dvp
,
2150 if (!error
&& xacl
!= NULL
) {
2151 VATTR_SET(vap
, va_acl
, xacl
);
2154 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2155 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
2157 * Server policy is to alway use the mapped rpc credential for
2158 * file system object creation. This has the nice side effect of
2159 * enforcing BSD creation semantics
2161 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2162 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2164 /* validate new-file security information */
2166 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2170 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
2176 if (vap
->va_type
== VREG
|| vap
->va_type
== VSOCK
) {
2178 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2181 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
2183 * If some of the requested attributes weren't handled by the VNOP,
2184 * use our fallback code.
2186 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
2190 kauth_acl_free(xacl
);
2194 if (exclusive_flag
) {
2197 bcopy(cverf
, (caddr_t
)&vap
->va_access_time
,
2199 VATTR_SET_ACTIVE(vap
, va_access_time
);
2200 // skip authorization, as this is an
2201 // NFS internal implementation detail.
2202 error
= vnode_setattr(vp
, vap
, ctx
);
2206 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_CREATE_FILE
, vp
)) {
2207 add_fsevent(FSE_CREATE_FILE
, ctx
,
2213 } else if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
||
2214 vap
->va_type
== VFIFO
) {
2215 if (vap
->va_type
== VCHR
&& rdev
== (int)0xffffffff) {
2216 VATTR_SET(vap
, va_type
, VFIFO
);
2218 if (vap
->va_type
!= VFIFO
) {
2219 error
= suser(nd
->nd_cr
, NULL
);
2222 VATTR_SET(vap
, va_rdev
, (dev_t
)rdev
);
2224 error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2227 kauth_acl_free(xacl
);
2237 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2239 ni
.ni_op
= OP_LOOKUP
;
2241 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2242 ni
.ni_cnd
.cn_context
= ctx
;
2243 ni
.ni_startdir
= dvp
;
2245 ni
.ni_rootdir
= rootvnode
;
2246 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
2247 while ((error
= lookup(&ni
)) == ERECYCLE
) {
2248 ni
.ni_cnd
.cn_flags
= cnflags
;
2249 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
2250 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
2253 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
) {
2263 * nameidone has to happen before we vnode_put(dvp)
2264 * since it may need to release the fs_nodelock on the dvp
2267 ni
.ni_cnd
.cn_nameiop
= 0;
2272 * nameidone has to happen before we vnode_put(dvp)
2273 * since it may need to release the fs_nodelock on the dvp
2276 ni
.ni_cnd
.cn_nameiop
= 0;
2281 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2282 /* NOTE: File has not been open for NFS case, so NOCRED for filecred */
2283 error
= mac_vnode_check_truncate(ctx
, NOCRED
, vp
);
2289 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2290 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
,
2293 tempsize
= vap
->va_data_size
;
2295 VATTR_SET(vap
, va_data_size
, tempsize
);
2296 error
= vnode_setattr(vp
, vap
, ctx
);
2301 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
2303 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
2304 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2305 if (nd
->nd_vers
== NFS_VER2
) {
2306 error
= postattrerr
;
2314 if (nd
->nd_vers
== NFS_VER3
) {
2315 if (exclusive_flag
&& !error
&&
2316 bcmp(cverf
, &postattr
.va_access_time
, NFSX_V3CREATEVERF
)) {
2319 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2320 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2326 /* assemble reply */
2327 nd
->nd_repstat
= error
;
2328 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
2329 NFSX_FATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
2331 *mrepp
= nmrep
.nmc_mhead
;
2332 nfsmout_on_status(nd
, error
);
2333 if (nd
->nd_vers
== NFS_VER3
) {
2334 if (!nd
->nd_repstat
) {
2335 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2336 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2338 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2339 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2341 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2343 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
2347 nfsm_chain_build_done(error
, &nmrep
);
2348 if (ni
.ni_cnd
.cn_nameiop
) {
2350 * nameidone has to happen before we vnode_put(dvp)
2351 * since it may need to release the fs_nodelock on the dvp
2364 nfsm_chain_cleanup(&nmrep
);
2371 * nfs v3 mknod service
2375 struct nfsrv_descript
*nd
,
2376 struct nfsrv_sock
*slp
,
2380 struct vnode_attr dpreattr
, dpostattr
, postattr
;
2381 struct vnode_attr va
, *vap
= &va
;
2382 struct nameidata ni
;
2383 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
2384 uint32_t len
= 0, cnflags
;
2385 u_int32_t major
= 0, minor
= 0;
2388 vnode_t vp
, dvp
, dirp
;
2389 struct nfs_filehandle nfh
;
2390 struct nfs_export
*nx
= NULL
;
2391 struct nfs_export_options
*nxo
;
2393 kauth_acl_t xacl
= NULL
;
2394 struct nfsm_chain
*nmreq
, nmrep
;
2397 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
2398 nmreq
= &nd
->nd_nmreq
;
2399 nfsm_chain_null(&nmrep
);
2400 vp
= dvp
= dirp
= NULL
;
2401 ni
.ni_cnd
.cn_nameiop
= 0;
2403 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2405 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2406 nfsm_chain_get_32(error
, nmreq
, len
);
2407 nfsm_name_len_check(error
, nd
, len
);
2410 ni
.ni_cnd
.cn_nameiop
= CREATE
;
2414 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2415 ni
.ni_cnd
.cn_ndp
= &ni
;
2416 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2418 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2420 /* update export stats */
2421 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2423 /* update active user stats */
2424 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2428 nfsm_srv_pre_vattr_init(&dpreattr
);
2429 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2432 ni
.ni_cnd
.cn_nameiop
= 0;
2439 nfsm_chain_get_32(error
, nmreq
, nvtype
);
2441 vtyp
= nfstov_type(nvtype
, NFS_VER3
);
2442 if (!error
&& (vtyp
!= VCHR
) && (vtyp
!= VBLK
) && (vtyp
!= VSOCK
) && (vtyp
!= VFIFO
)) {
2443 error
= NFSERR_BADTYPE
;
2448 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2449 if ((vtyp
== VCHR
) || (vtyp
== VBLK
)) {
2450 nfsm_chain_get_32(error
, nmreq
, major
);
2451 nfsm_chain_get_32(error
, nmreq
, minor
);
2453 VATTR_SET(vap
, va_rdev
, makedev(major
, minor
));
2458 * If it doesn't exist, create it.
2464 VATTR_SET(vap
, va_type
, vtyp
);
2466 /* authorize before creating */
2467 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
2469 /* construct ACL and handle inheritance */
2471 error
= kauth_acl_inherit(dvp
,
2477 if (!error
&& xacl
!= NULL
) {
2478 VATTR_SET(vap
, va_acl
, xacl
);
2481 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2482 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
2484 * Server policy is to alway use the mapped rpc credential for
2485 * file system object creation. This has the nice side effect of
2486 * enforcing BSD creation semantics
2488 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2489 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2491 /* validate new-file security information */
2493 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2496 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
2505 if (vtyp
== VSOCK
) {
2506 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2508 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
2510 * If some of the requested attributes weren't handled by the VNOP,
2511 * use our fallback code.
2513 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
2516 if (vtyp
!= VFIFO
&& (error
= suser(nd
->nd_cr
, (u_short
*)0))) {
2519 if ((error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
))) {
2527 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2529 ni
.ni_op
= OP_LOOKUP
;
2531 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2532 ni
.ni_cnd
.cn_context
= vfs_context_current();
2533 ni
.ni_startdir
= dvp
;
2535 ni
.ni_rootdir
= rootvnode
;
2536 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
2537 while ((error
= lookup(&ni
)) == ERECYCLE
) {
2538 ni
.ni_cnd
.cn_flags
= cnflags
;
2539 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
2540 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
2544 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
) {
2551 kauth_acl_free(xacl
);
2555 * nameidone has to happen before we vnode_put(dvp)
2556 * since it may need to release the fs_nodelock on the dvp
2559 ni
.ni_cnd
.cn_nameiop
= 0;
2565 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
2567 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
2568 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2576 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2577 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2582 /* assemble reply */
2583 nd
->nd_repstat
= error
;
2584 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(NFS_VER3
, &nfh
) +
2585 NFSX_POSTOPATTR(NFS_VER3
) + NFSX_WCCDATA(NFS_VER3
));
2587 *mrepp
= nmrep
.nmc_mhead
;
2588 nfsmout_on_status(nd
, error
);
2589 if (!nd
->nd_repstat
) {
2590 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2591 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2593 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2594 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2596 nfsm_chain_build_done(error
, &nmrep
);
2597 if (ni
.ni_cnd
.cn_nameiop
) {
2599 * nameidone has to happen before we vnode_put(dvp)
2600 * since it may need to release the fs_nodelock on the dvp
2619 nfsm_chain_cleanup(&nmrep
);
2626 * nfs remove service
2630 struct nfsrv_descript
*nd
,
2631 struct nfsrv_sock
*slp
,
2635 struct nameidata ni
;
2636 int error
, dpreattrerr
, dpostattrerr
;
2639 vnode_t vp
, dvp
, dirp
= NULL
;
2640 struct vnode_attr dpreattr
, dpostattr
;
2641 struct nfs_filehandle nfh
;
2642 struct nfs_export
*nx
= NULL
;
2643 struct nfs_export_options
*nxo
;
2644 struct nfsm_chain
*nmreq
, nmrep
;
2647 dpreattrerr
= dpostattrerr
= ENOENT
;
2648 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2649 dvp
= vp
= dirp
= NULL
;
2650 nmreq
= &nd
->nd_nmreq
;
2651 nfsm_chain_null(&nmrep
);
2653 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2654 nfsm_chain_get_32(error
, nmreq
, len
);
2655 nfsm_name_len_check(error
, nd
, len
);
2658 ni
.ni_cnd
.cn_nameiop
= DELETE
;
2660 ni
.ni_op
= OP_UNLINK
;
2662 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2663 ni
.ni_cnd
.cn_ndp
= &ni
;
2664 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2666 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2668 /* update export stats */
2669 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2671 /* update active user stats */
2672 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2676 if (nd
->nd_vers
== NFS_VER3
) {
2677 nfsm_srv_pre_vattr_init(&dpreattr
);
2678 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2689 if (vnode_vtype(vp
) == VDIR
) {
2690 error
= EPERM
; /* POSIX */
2691 } else if (vnode_isvroot(vp
)) {
2693 * The root of a mounted filesystem cannot be deleted.
2697 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
2701 error
= vn_authorize_unlink(dvp
, vp
, &ni
.ni_cnd
, ctx
, NULL
);
2713 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
2715 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
2716 get_fse_info(vp
, &finfo
, ctx
);
2718 release_pathbuff(path
);
2723 error
= VNOP_REMOVE(dvp
, vp
, &ni
.ni_cnd
, 0, ctx
);
2728 add_fsevent(FSE_DELETE
, ctx
,
2729 FSE_ARG_STRING
, plen
, path
,
2730 FSE_ARG_FINFO
, &finfo
,
2733 release_pathbuff(path
);
2739 * nameidone has to happen before we vnode_put(dvp)
2740 * since it may need to release the fs_nodelock on the dvp
2750 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
2751 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2755 /* assemble reply */
2756 nd
->nd_repstat
= error
;
2757 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
2759 *mrepp
= nmrep
.nmc_mhead
;
2760 nfsmout_on_status(nd
, error
);
2761 if (nd
->nd_vers
== NFS_VER3
) {
2762 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2763 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2766 nfsm_chain_build_done(error
, &nmrep
);
2768 nfsm_chain_cleanup(&nmrep
);
2775 * nfs rename service
2779 struct nfsrv_descript
*nd
,
2780 struct nfsrv_sock
*slp
,
2784 kauth_cred_t saved_cred
= NULL
;
2787 uint32_t fromlen
, tolen
;
2788 int fdpreattrerr
, fdpostattrerr
;
2789 int tdpreattrerr
, tdpostattrerr
;
2790 char *frompath
= NULL
, *topath
= NULL
;
2791 struct nameidata fromni
, toni
;
2792 vnode_t fvp
, tvp
, tdvp
, fdvp
, fdirp
, tdirp
;
2793 struct vnode_attr fdpreattr
, fdpostattr
;
2794 struct vnode_attr tdpreattr
, tdpostattr
;
2795 struct nfs_filehandle fnfh
, tnfh
;
2796 struct nfs_export
*fnx
, *tnx
;
2797 struct nfs_export_options
*fnxo
, *tnxo
;
2798 enum vtype fvtype
, tvtype
;
2799 int holding_mntlock
;
2801 struct nfsm_chain
*nmreq
, nmrep
;
2802 char *from_name
, *to_name
;
2804 int from_len
= 0, to_len
= 0;
2805 fse_info from_finfo
, to_finfo
;
2807 u_char didstats
= 0;
2811 fdpreattrerr
= fdpostattrerr
= ENOENT
;
2812 tdpreattrerr
= tdpostattrerr
= ENOENT
;
2813 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2814 fromlen
= tolen
= 0;
2815 frompath
= topath
= NULL
;
2816 fdirp
= tdirp
= NULL
;
2817 nmreq
= &nd
->nd_nmreq
;
2818 nfsm_chain_null(&nmrep
);
2821 * these need to be set before calling any code
2822 * that they may take us out through the error path.
2824 holding_mntlock
= 0;
2829 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, fnfh
.nfh_fhp
, fnfh
.nfh_len
);
2830 nfsm_chain_get_32(error
, nmreq
, fromlen
);
2831 nfsm_name_len_check(error
, nd
, fromlen
);
2833 error
= nfsm_chain_get_path_namei(nmreq
, fromlen
, &fromni
);
2835 frompath
= fromni
.ni_cnd
.cn_pnbuf
;
2837 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, tnfh
.nfh_fhp
, tnfh
.nfh_len
);
2838 nfsm_chain_get_32(error
, nmreq
, tolen
);
2839 nfsm_name_len_check(error
, nd
, tolen
);
2841 error
= nfsm_chain_get_path_namei(nmreq
, tolen
, &toni
);
2843 topath
= toni
.ni_cnd
.cn_pnbuf
;
2846 * Remember our original uid so that we can reset cr_uid before
2847 * the second nfsrv_namei() call, in case it is remapped.
2849 saved_cred
= nd
->nd_cr
;
2850 kauth_cred_ref(saved_cred
);
2852 fromni
.ni_cnd
.cn_nameiop
= DELETE
;
2854 fromni
.ni_op
= OP_UNLINK
;
2856 fromni
.ni_cnd
.cn_flags
= WANTPARENT
;
2858 fromni
.ni_cnd
.cn_pnbuf
= frompath
;
2860 fromni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2861 fromni
.ni_cnd
.cn_flags
|= HASBUF
;
2862 fromni
.ni_cnd
.cn_ndp
= &fromni
;
2864 error
= nfsrv_namei(nd
, ctx
, &fromni
, &fnfh
, &fdirp
, &fnx
, &fnxo
);
2868 fdvp
= fromni
.ni_dvp
;
2872 if (nd
->nd_vers
== NFS_VER3
) {
2873 nfsm_srv_pre_vattr_init(&fdpreattr
);
2874 fdpreattrerr
= vnode_getattr(fdirp
, &fdpreattr
, ctx
);
2880 fvtype
= vnode_vtype(fvp
);
2882 /* reset credential if it was remapped */
2883 if (nd
->nd_cr
!= saved_cred
) {
2884 kauth_cred_ref(saved_cred
);
2885 kauth_cred_unref(&nd
->nd_cr
);
2886 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
2889 toni
.ni_cnd
.cn_nameiop
= RENAME
;
2891 toni
.ni_op
= OP_RENAME
;
2893 toni
.ni_cnd
.cn_flags
= WANTPARENT
;
2895 toni
.ni_cnd
.cn_pnbuf
= topath
;
2897 toni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2898 toni
.ni_cnd
.cn_flags
|= HASBUF
;
2899 toni
.ni_cnd
.cn_ndp
= &toni
;
2901 if (fvtype
== VDIR
) {
2902 toni
.ni_cnd
.cn_flags
|= WILLBEDIR
;
2906 error
= nfsrv_namei(nd
, ctx
, &toni
, &tnfh
, &tdirp
, &tnx
, &tnxo
);
2909 * Translate error code for rename("dir1", "dir2/.").
2911 if (error
== EISDIR
&& fvtype
== VDIR
) {
2912 if (nd
->nd_vers
== NFS_VER3
) {
2924 /* update export stats once only */
2926 /* update export stats */
2927 NFSStatAdd64(&tnx
->nx_stats
.ops
, 1);
2929 /* update active user stats */
2930 nfsrv_update_user_stat(tnx
, nd
, saved_uid
, 1, 0, 0);
2936 if (nd
->nd_vers
== NFS_VER3
) {
2937 nfsm_srv_pre_vattr_init(&tdpreattr
);
2938 tdpreattrerr
= vnode_getattr(tdirp
, &tdpreattr
, ctx
);
2946 tvtype
= vnode_vtype(tvp
);
2948 if (fvtype
== VDIR
&& tvtype
!= VDIR
) {
2949 if (nd
->nd_vers
== NFS_VER3
) {
2955 } else if (fvtype
!= VDIR
&& tvtype
== VDIR
) {
2956 if (nd
->nd_vers
== NFS_VER3
) {
2963 if (tvtype
== VDIR
&& vnode_mountedhere(tvp
)) {
2964 if (nd
->nd_vers
== NFS_VER3
) {
2973 if (nd
->nd_vers
== NFS_VER3
) {
2984 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2985 * the node is moving between directories and we need rights to remove from the
2986 * old and add to the new.
2988 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2990 * Note that we do not inherit when renaming. XXX this needs to be revisited to
2991 * implement the deferred-inherit bit.
2997 if ((tvp
!= NULL
) && vnode_isdir(tvp
)) {
3001 } else if (tdvp
!= fdvp
) {
3005 /* moving out of fdvp, must have delete rights */
3006 if ((error
= nfsrv_authorize(fvp
, fdvp
, KAUTH_VNODE_DELETE
, ctx
, fnxo
, 0)) != 0) {
3009 /* moving into tdvp or tvp, must have rights to add */
3010 if ((error
= nfsrv_authorize(((tvp
!= NULL
) && vnode_isdir(tvp
)) ? tvp
: tdvp
,
3012 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
3013 ctx
, tnxo
, 0)) != 0) {
3017 /* node staying in same directory, must be allowed to add new name */
3018 if ((error
= nfsrv_authorize(fdvp
, NULL
,
3019 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
3020 ctx
, fnxo
, 0)) != 0) {
3024 /* overwriting tvp */
3025 if ((tvp
!= NULL
) && !vnode_isdir(tvp
) &&
3026 ((error
= nfsrv_authorize(tvp
, tdvp
, KAUTH_VNODE_DELETE
, ctx
, tnxo
, 0)) != 0)) {
3031 ((error
= vn_authorize_rename(fdvp
, fvp
, &fromni
.ni_cnd
, tdvp
, tvp
, &toni
.ni_cnd
, ctx
, NULL
)) != 0)) {
3037 /* XXX more checks? */
3040 /* authorization denied */
3046 if ((vnode_mount(fvp
) != vnode_mount(tdvp
)) ||
3047 (tvp
&& (vnode_mount(fvp
) != vnode_mount(tvp
)))) {
3048 if (nd
->nd_vers
== NFS_VER3
) {
3056 * The following edge case is caught here:
3057 * (to cannot be a descendent of from)
3070 if (tdvp
->v_parent
== fvp
) {
3071 if (nd
->nd_vers
== NFS_VER3
) {
3078 if (fvtype
== VDIR
&& vnode_mountedhere(fvp
)) {
3079 if (nd
->nd_vers
== NFS_VER3
) {
3087 * If source is the same as the destination (that is the
3088 * same vnode) then there is nothing to do...
3089 * EXCEPT if the underlying file system supports case
3090 * insensitivity and is case preserving. In this case
3091 * the file system needs to handle the special case of
3092 * getting the same vnode as target (fvp) and source (tvp).
3094 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
3095 * and _PC_CASE_PRESERVING can have this exception, and they need to
3096 * handle the special case of getting the same vnode as target and
3097 * source. NOTE: Then the target is unlocked going into vnop_rename,
3098 * so not to cause locking problems. There is a single reference on tvp.
3100 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
3101 * that correct behaviour then is just to remove the source (link)
3103 if ((fvp
== tvp
) && (fdvp
== tdvp
)) {
3104 if (fromni
.ni_cnd
.cn_namelen
== toni
.ni_cnd
.cn_namelen
&&
3105 !bcmp(fromni
.ni_cnd
.cn_nameptr
, toni
.ni_cnd
.cn_nameptr
,
3106 fromni
.ni_cnd
.cn_namelen
)) {
3111 if (holding_mntlock
&& vnode_mount(fvp
) != locked_mp
) {
3113 * we're holding a reference and lock
3114 * on locked_mp, but it no longer matches
3115 * what we want to do... so drop our hold
3117 mount_unlock_renames(locked_mp
);
3118 mount_drop(locked_mp
, 0);
3119 holding_mntlock
= 0;
3121 if (tdvp
!= fdvp
&& fvtype
== VDIR
) {
3123 * serialize renames that re-shape
3124 * the tree... if holding_mntlock is
3125 * set, then we're ready to go...
3127 * first need to drop the iocounts
3128 * we picked up, second take the
3129 * lock to serialize the access,
3130 * then finally start the lookup
3131 * process over with the lock held
3133 if (!holding_mntlock
) {
3135 * need to grab a reference on
3136 * the mount point before we
3137 * drop all the iocounts... once
3138 * the iocounts are gone, the mount
3141 locked_mp
= vnode_mount(fvp
);
3142 mount_ref(locked_mp
, 0);
3144 /* make a copy of to path to pass to nfsrv_namei() again */
3145 MALLOC_ZONE(topath
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
3147 bcopy(toni
.ni_cnd
.cn_pnbuf
, topath
, tolen
+ 1);
3151 * nameidone has to happen before we vnode_put(tdvp)
3152 * since it may need to release the fs_nodelock on the tdvp
3161 /* make a copy of from path to pass to nfsrv_namei() again */
3162 MALLOC_ZONE(frompath
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
3164 bcopy(fromni
.ni_cnd
.cn_pnbuf
, frompath
, fromlen
+ 1);
3168 * nameidone has to happen before we vnode_put(fdvp)
3169 * since it may need to release the fs_nodelock on the fdvp
3184 mount_lock_renames(locked_mp
);
3185 holding_mntlock
= 1;
3190 fdpreattrerr
= tdpreattrerr
= ENOENT
;
3192 if (!topath
|| !frompath
) {
3193 /* we couldn't allocate a path, so bail */
3198 /* reset credential if it was remapped */
3199 if (nd
->nd_cr
!= saved_cred
) {
3200 kauth_cred_ref(saved_cred
);
3201 kauth_cred_unref(&nd
->nd_cr
);
3202 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
3209 * when we dropped the iocounts to take
3210 * the lock, we allowed the identity of
3211 * the various vnodes to change... if they did,
3212 * we may no longer be dealing with a rename
3213 * that reshapes the tree... once we're holding
3214 * the iocounts, the vnodes can't change type
3215 * so we're free to drop the lock at this point
3218 if (holding_mntlock
) {
3219 mount_unlock_renames(locked_mp
);
3220 mount_drop(locked_mp
, 0);
3221 holding_mntlock
= 0;
3225 // save these off so we can later verify that fvp is the same
3227 oname
= fvp
->v_name
;
3228 oparent
= fvp
->v_parent
;
3231 * If generating an fsevent, then
3232 * stash any pre-rename info we may need.
3235 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_RENAME
, fvp
)) {
3236 int from_truncated
= 0, to_truncated
= 0;
3238 get_fse_info(fvp
, &from_finfo
, ctx
);
3240 get_fse_info(tvp
, &to_finfo
, ctx
);
3243 from_name
= get_pathbuff();
3245 from_len
= safe_getpath(fdvp
, fromni
.ni_cnd
.cn_nameptr
, from_name
, MAXPATHLEN
, &from_truncated
);
3248 to_name
= from_name
? get_pathbuff() : NULL
;
3250 to_len
= safe_getpath(tdvp
, toni
.ni_cnd
.cn_nameptr
, to_name
, MAXPATHLEN
, &to_truncated
);
3253 if (from_truncated
|| to_truncated
) {
3254 from_finfo
.mode
|= FSE_TRUNCATED_PATH
;
3260 #else /* CONFIG_FSE */
3263 #endif /* CONFIG_FSE */
3265 error
= VNOP_RENAME(fromni
.ni_dvp
, fromni
.ni_vp
, &fromni
.ni_cnd
,
3266 toni
.ni_dvp
, toni
.ni_vp
, &toni
.ni_cnd
, ctx
);
3268 * fix up name & parent pointers. note that we first
3269 * check that fvp has the same name/parent pointers it
3270 * had before the rename call... this is a 'weak' check
3273 if (oname
== fvp
->v_name
&& oparent
== fvp
->v_parent
) {
3275 update_flags
= VNODE_UPDATE_NAME
;
3277 update_flags
|= VNODE_UPDATE_PARENT
;
3279 vnode_update_identity(fvp
, tdvp
, toni
.ni_cnd
.cn_nameptr
,
3280 toni
.ni_cnd
.cn_namelen
, toni
.ni_cnd
.cn_hash
, update_flags
);
3284 * If the rename is OK and we've got the paths
3285 * then add an fsevent.
3288 if (nfsrv_fsevents_enabled
&& !error
&& from_name
&& to_name
) {
3290 add_fsevent(FSE_RENAME
, ctx
,
3291 FSE_ARG_STRING
, from_len
, from_name
,
3292 FSE_ARG_FINFO
, &from_finfo
,
3293 FSE_ARG_STRING
, to_len
, to_name
,
3294 FSE_ARG_FINFO
, &to_finfo
,
3297 add_fsevent(FSE_RENAME
, ctx
,
3298 FSE_ARG_STRING
, from_len
, from_name
,
3299 FSE_ARG_FINFO
, &from_finfo
,
3300 FSE_ARG_STRING
, to_len
, to_name
,
3305 release_pathbuff(from_name
);
3308 release_pathbuff(to_name
);
3310 #endif /* CONFIG_FSE */
3311 from_name
= to_name
= NULL
;
3314 if (holding_mntlock
) {
3315 mount_unlock_renames(locked_mp
);
3316 mount_drop(locked_mp
, 0);
3317 holding_mntlock
= 0;
3321 * nameidone has to happen before we vnode_put(tdvp)
3322 * since it may need to release the fs_nodelock on the tdvp
3334 * nameidone has to happen before we vnode_put(fdvp)
3335 * since it may need to release the fs_nodelock on the fdvp
3347 nfsm_srv_vattr_init(&fdpostattr
, nd
->nd_vers
);
3348 fdpostattrerr
= vnode_getattr(fdirp
, &fdpostattr
, ctx
);
3353 nfsm_srv_vattr_init(&tdpostattr
, nd
->nd_vers
);
3354 tdpostattrerr
= vnode_getattr(tdirp
, &tdpostattr
, ctx
);
3360 /* assemble reply */
3361 nd
->nd_repstat
= error
;
3362 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 2 * NFSX_WCCDATA(nd
->nd_vers
));
3364 *mrepp
= nmrep
.nmc_mhead
;
3365 nfsmout_on_status(nd
, error
);
3366 if (nd
->nd_vers
== NFS_VER3
) {
3367 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3368 fdpreattrerr
, &fdpreattr
, fdpostattrerr
, &fdpostattr
);
3369 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3370 tdpreattrerr
, &tdpreattr
, tdpostattrerr
, &tdpostattr
);
3373 nfsm_chain_build_done(error
, &nmrep
);
3374 if (holding_mntlock
) {
3375 mount_unlock_renames(locked_mp
);
3376 mount_drop(locked_mp
, 0);
3380 * nameidone has to happen before we vnode_put(tdvp)
3381 * since it may need to release the fs_nodelock on the tdvp
3392 * nameidone has to happen before we vnode_put(fdvp)
3393 * since it may need to release the fs_nodelock on the fdvp
3409 FREE_ZONE(frompath
, MAXPATHLEN
, M_NAMEI
);
3412 FREE_ZONE(topath
, MAXPATHLEN
, M_NAMEI
);
3415 kauth_cred_unref(&saved_cred
);
3418 nfsm_chain_cleanup(&nmrep
);
3429 struct nfsrv_descript
*nd
,
3430 struct nfsrv_sock
*slp
,
3434 struct nameidata ni
;
3435 int error
, dpreattrerr
, dpostattrerr
, attrerr
;
3437 vnode_t vp
, xp
, dvp
, dirp
;
3438 struct vnode_attr dpreattr
, dpostattr
, attr
;
3439 struct nfs_filehandle nfh
, dnfh
;
3440 struct nfs_export
*nx
;
3441 struct nfs_export_options
*nxo
;
3442 struct nfsm_chain
*nmreq
, nmrep
;
3445 dpreattrerr
= dpostattrerr
= attrerr
= ENOENT
;
3446 vp
= xp
= dvp
= dirp
= NULL
;
3447 nmreq
= &nd
->nd_nmreq
;
3448 nfsm_chain_null(&nmrep
);
3450 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3451 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
3452 nfsm_chain_get_32(error
, nmreq
, len
);
3453 nfsm_name_len_check(error
, nd
, len
);
3455 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
3458 /* update export stats */
3459 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3461 /* update active user stats */
3462 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
3464 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
3467 /* we're not allowed to link to directories... */
3468 if (vnode_vtype(vp
) == VDIR
) {
3469 error
= EPERM
; /* POSIX */
3473 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3474 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LINKTARGET
, ctx
, nxo
, 0)) != 0) {
3478 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3482 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3483 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3485 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
3488 if (nd
->nd_vers
== NFS_VER3
) {
3489 nfsm_srv_pre_vattr_init(&dpreattr
);
3490 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3504 } else if (vnode_mount(vp
) != vnode_mount(dvp
)) {
3507 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3512 error
= mac_vnode_check_link(ctx
, dvp
, vp
, &ni
.ni_cnd
);
3519 error
= VNOP_LINK(vp
, dvp
, &ni
.ni_cnd
, ctx
);
3523 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CREATE_FILE
, dvp
)) {
3524 char *target_path
= NULL
;
3525 int plen
, truncated
= 0;
3528 /* build the path to the new link file */
3529 target_path
= get_pathbuff();
3531 plen
= safe_getpath(dvp
, ni
.ni_cnd
.cn_nameptr
, target_path
, MAXPATHLEN
, &truncated
);
3533 if (get_fse_info(vp
, &finfo
, ctx
) == 0) {
3535 finfo
.mode
|= FSE_TRUNCATED_PATH
;
3537 add_fsevent(FSE_CREATE_FILE
, ctx
,
3538 FSE_ARG_STRING
, plen
, target_path
,
3539 FSE_ARG_FINFO
, &finfo
,
3543 release_pathbuff(target_path
);
3549 * nameidone has to happen before we vnode_put(dvp)
3550 * since it may need to release the fs_nodelock on the dvp
3559 if (nd
->nd_vers
== NFS_VER3
) {
3560 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
3561 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
3564 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3565 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3573 /* assemble reply */
3574 nd
->nd_repstat
= error
;
3575 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3577 *mrepp
= nmrep
.nmc_mhead
;
3578 nfsmout_on_status(nd
, error
);
3579 if (nd
->nd_vers
== NFS_VER3
) {
3580 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
3581 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3582 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3585 nfsm_chain_build_done(error
, &nmrep
);
3590 nfsm_chain_cleanup(&nmrep
);
3597 * nfs symbolic link service
3601 struct nfsrv_descript
*nd
,
3602 struct nfsrv_sock
*slp
,
3606 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3607 struct vnode_attr va
, *vap
= &va
;
3608 struct nameidata ni
;
3609 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3610 uint32_t len
= 0, linkdatalen
, cnflags
;
3613 vnode_t vp
, dvp
, dirp
;
3614 struct nfs_filehandle nfh
;
3615 struct nfs_export
*nx
= NULL
;
3616 struct nfs_export_options
*nxo
;
3618 char uio_buf
[UIO_SIZEOF(1)];
3619 struct nfsm_chain
*nmreq
, nmrep
;
3622 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3623 nmreq
= &nd
->nd_nmreq
;
3624 nfsm_chain_null(&nmrep
);
3628 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3630 ni
.ni_cnd
.cn_nameiop
= 0;
3633 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3634 nfsm_chain_get_32(error
, nmreq
, len
);
3635 nfsm_name_len_check(error
, nd
, len
);
3638 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3642 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3644 ni
.ni_cnd
.cn_ndp
= &ni
;
3645 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3647 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3649 /* update export stats */
3650 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3652 /* update active user stats */
3653 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3657 if (nd
->nd_vers
== NFS_VER3
) {
3658 nfsm_srv_pre_vattr_init(&dpreattr
);
3659 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3666 ni
.ni_cnd
.cn_nameiop
= 0;
3673 if (nd
->nd_vers
== NFS_VER3
) {
3674 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3676 nfsm_chain_get_32(error
, nmreq
, linkdatalen
);
3677 if (!error
&& (((nd
->nd_vers
== NFS_VER2
) && (linkdatalen
> NFS_MAXPATHLEN
)) ||
3678 ((nd
->nd_vers
== NFS_VER3
) && (linkdatalen
> MAXPATHLEN
)))) {
3679 error
= NFSERR_NAMETOL
;
3682 MALLOC(linkdata
, caddr_t
, linkdatalen
+ 1, M_TEMP
, M_WAITOK
);
3684 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
3685 &uio_buf
[0], sizeof(uio_buf
));
3687 if (!linkdata
|| !auio
) {
3691 uio_addiov(auio
, CAST_USER_ADDR_T(linkdata
), linkdatalen
);
3692 error
= nfsm_chain_get_uio(nmreq
, linkdatalen
, auio
);
3693 if (!error
&& (nd
->nd_vers
== NFS_VER2
)) {
3694 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3697 *(linkdata
+ linkdatalen
) = '\0';
3703 VATTR_SET(vap
, va_type
, VLNK
);
3704 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3705 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3707 * Server policy is to alway use the mapped rpc credential for
3708 * file system object creation. This has the nice side effect of
3709 * enforcing BSD creation semantics
3711 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3712 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3714 /* authorize before creating */
3715 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3717 /* validate given attributes */
3719 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3722 error
= vn_authorize_create(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
3729 error
= VNOP_SYMLINK(dvp
, &vp
, &ni
.ni_cnd
, vap
, linkdata
, ctx
);
3732 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
3734 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
3736 ni
.ni_op
= OP_LOOKUP
;
3738 ni
.ni_cnd
.cn_flags
&= ~(LOCKPARENT
| FOLLOW
);
3739 ni
.ni_cnd
.cn_flags
|= (NOFOLLOW
| LOCKLEAF
);
3740 ni
.ni_cnd
.cn_context
= ctx
;
3741 ni
.ni_startdir
= dvp
;
3743 ni
.ni_rootdir
= rootvnode
;
3744 cnflags
= ni
.ni_cnd
.cn_flags
; /* store in case we have to restore */
3745 while ((error
= lookup(&ni
)) == ERECYCLE
) {
3746 ni
.ni_cnd
.cn_flags
= cnflags
;
3747 ni
.ni_cnd
.cn_nameptr
= ni
.ni_cnd
.cn_pnbuf
;
3748 ni
.ni_usedvp
= ni
.ni_dvp
= ni
.ni_startdir
= dvp
;
3755 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
3757 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
3758 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
3764 if (nfsrv_fsevents_enabled
&& !error
&& vp
) {
3765 add_fsevent(FSE_CREATE_FILE
, ctx
,
3772 * nameidone has to happen before we vnode_put(dvp)
3773 * since it may need to release the fs_nodelock on the dvp
3776 ni
.ni_cnd
.cn_nameiop
= 0;
3783 FREE(linkdata
, M_TEMP
);
3787 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3788 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3794 /* assemble reply */
3795 nd
->nd_repstat
= error
;
3796 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
3797 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3799 *mrepp
= nmrep
.nmc_mhead
;
3800 nfsmout_on_status(nd
, error
);
3801 if (nd
->nd_vers
== NFS_VER3
) {
3802 if (!nd
->nd_repstat
) {
3803 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3804 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
3806 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3807 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3810 nfsm_chain_build_done(error
, &nmrep
);
3811 if (ni
.ni_cnd
.cn_nameiop
) {
3813 * nameidone has to happen before we vnode_put(dvp)
3814 * since it may need to release the fs_nodelock on the dvp
3827 FREE(linkdata
, M_TEMP
);
3830 nfsm_chain_cleanup(&nmrep
);
3842 struct nfsrv_descript
*nd
,
3843 struct nfsrv_sock
*slp
,
3847 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3848 struct vnode_attr va
, *vap
= &va
;
3849 struct nameidata ni
;
3850 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3852 vnode_t vp
, dvp
, dirp
;
3853 struct nfs_filehandle nfh
;
3854 struct nfs_export
*nx
= NULL
;
3855 struct nfs_export_options
*nxo
;
3857 kauth_acl_t xacl
= NULL
;
3858 struct nfsm_chain
*nmreq
, nmrep
;
3861 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3862 nmreq
= &nd
->nd_nmreq
;
3863 nfsm_chain_null(&nmrep
);
3865 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3867 ni
.ni_cnd
.cn_nameiop
= 0;
3868 vp
= dvp
= dirp
= NULL
;
3870 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3871 nfsm_chain_get_32(error
, nmreq
, len
);
3872 nfsm_name_len_check(error
, nd
, len
);
3875 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3879 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| WILLBEDIR
;
3880 ni
.ni_cnd
.cn_ndp
= &ni
;
3881 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3883 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3885 /* update export stats */
3886 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3888 /* update active user stats */
3889 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3893 if (nd
->nd_vers
== NFS_VER3
) {
3894 nfsm_srv_pre_vattr_init(&dpreattr
);
3895 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3902 ni
.ni_cnd
.cn_nameiop
= 0;
3909 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3911 VATTR_SET(vap
, va_type
, VDIR
);
3915 * nameidone has to happen before we vnode_put(dvp)
3916 * since it may need to release the fs_nodelock on the dvp
3925 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_SUBDIRECTORY
, ctx
, nxo
, 0);
3927 /* construct ACL and handle inheritance */
3929 error
= kauth_acl_inherit(dvp
,
3935 if (!error
&& xacl
!= NULL
) {
3936 VATTR_SET(vap
, va_acl
, xacl
);
3940 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3941 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3943 * We don't support the S_ISGID bit for directories. Solaris and other
3944 * SRV4 derived systems might set this to get BSD semantics, which we enforce
3947 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
3948 vap
->va_mode
&= ~S_ISGID
;
3951 * Server policy is to alway use the mapped rpc credential for
3952 * file system object creation. This has the nice side effect of
3953 * enforcing BSD creation semantics
3955 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3956 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3958 /* validate new-file security information */
3960 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3963 * vnode_authattr_new can return errors other than EPERM, but that's not going to
3964 * sit well with our clients so we map all errors to EPERM.
3971 error
= vn_authorize_mkdir(dvp
, &ni
.ni_cnd
, vap
, ctx
, NULL
);
3978 error
= VNOP_MKDIR(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
3982 if (nfsrv_fsevents_enabled
&& !error
) {
3983 add_fsevent(FSE_CREATE_DIR
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
3987 if (!error
&& !VATTR_ALL_SUPPORTED(vap
)) {
3989 * If some of the requested attributes weren't handled by the VNOP,
3990 * use our fallback code.
3992 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
3996 kauth_acl_free(xacl
);
4000 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
4002 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
4003 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
4004 if (nd
->nd_vers
== NFS_VER2
) {
4005 error
= postattrerr
;
4012 * nameidone has to happen before we vnode_put(dvp)
4013 * since it may need to release the fs_nodelock on the dvp
4018 ni
.ni_cnd
.cn_nameiop
= 0;
4021 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
4022 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
4028 /* assemble reply */
4029 nd
->nd_repstat
= error
;
4030 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
4031 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
4033 *mrepp
= nmrep
.nmc_mhead
;
4034 nfsmout_on_status(nd
, error
);
4035 if (nd
->nd_vers
== NFS_VER3
) {
4036 if (!nd
->nd_repstat
) {
4037 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4038 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
4040 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4041 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
4043 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4045 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
4049 nfsm_chain_build_done(error
, &nmrep
);
4050 if (ni
.ni_cnd
.cn_nameiop
) {
4052 * nameidone has to happen before we vnode_put(dvp)
4053 * since it may need to release the fs_nodelock on the dvp
4065 nfsm_chain_cleanup(&nmrep
);
4076 struct nfsrv_descript
*nd
,
4077 struct nfsrv_sock
*slp
,
4081 int error
, dpreattrerr
, dpostattrerr
;
4084 vnode_t vp
, dvp
, dirp
;
4085 struct vnode_attr dpreattr
, dpostattr
;
4086 struct nfs_filehandle nfh
;
4087 struct nfs_export
*nx
= NULL
;
4088 struct nfs_export_options
*nxo
;
4089 struct nameidata ni
;
4090 struct nfsm_chain
*nmreq
, nmrep
;
4093 dpreattrerr
= dpostattrerr
= ENOENT
;
4094 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
4095 nmreq
= &nd
->nd_nmreq
;
4096 nfsm_chain_null(&nmrep
);
4098 vp
= dvp
= dirp
= NULL
;
4100 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4101 nfsm_chain_get_32(error
, nmreq
, len
);
4102 nfsm_name_len_check(error
, nd
, len
);
4105 ni
.ni_cnd
.cn_nameiop
= DELETE
;
4107 ni
.ni_op
= OP_UNLINK
;
4109 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
4110 ni
.ni_cnd
.cn_ndp
= &ni
;
4111 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
4113 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
4115 /* update export stats */
4116 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4118 /* update active user stats */
4119 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
4123 if (nd
->nd_vers
== NFS_VER3
) {
4124 nfsm_srv_pre_vattr_init(&dpreattr
);
4125 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
4136 if (vnode_vtype(vp
) != VDIR
) {
4141 * No rmdir "." please.
4148 * The root of a mounted filesystem cannot be deleted.
4150 if (vnode_isvroot(vp
)) {
4154 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
4157 error
= vn_authorize_rmdir(dvp
, vp
, &ni
.ni_cnd
, ctx
, NULL
);
4169 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
4171 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
4172 get_fse_info(vp
, &finfo
, ctx
);
4174 release_pathbuff(path
);
4178 #endif /* CONFIG_FSE */
4180 error
= VNOP_RMDIR(dvp
, vp
, &ni
.ni_cnd
, ctx
);
4185 add_fsevent(FSE_DELETE
, ctx
,
4186 FSE_ARG_STRING
, plen
, path
,
4187 FSE_ARG_FINFO
, &finfo
,
4190 release_pathbuff(path
);
4192 #endif /* CONFIG_FSE */
4196 * nameidone has to happen before we vnode_put(dvp)
4197 * since it may need to release the fs_nodelock on the dvp
4205 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
4206 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
4212 /* assemble reply */
4213 nd
->nd_repstat
= error
;
4214 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
4216 *mrepp
= nmrep
.nmc_mhead
;
4217 nfsmout_on_status(nd
, error
);
4218 if (nd
->nd_vers
== NFS_VER3
) {
4219 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4220 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
4223 nfsm_chain_build_done(error
, &nmrep
);
4228 nfsm_chain_cleanup(&nmrep
);
4235 * nfs readdir service
4236 * - mallocs what it thinks is enough to read
4237 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
4238 * - calls VNOP_READDIR()
4239 * - loops around building the reply
4240 * if the output generated exceeds count break out of loop
4241 * The nfsm_clget macro is used here so that the reply will be packed
4242 * tightly in mbuf clusters.
4243 * - it only knows that it has encountered eof when the VNOP_READDIR()
4245 * - as such one readdir rpc will return eof false although you are there
4246 * and then the next will return eof
4247 * - it trims out records with d_fileno == 0
4248 * this doesn't matter for Unix clients, but they might confuse clients
4250 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
4251 * than requested, but this may not apply to all filesystems. For
4252 * example, client NFS does not { although it is never remote mounted
4254 * The alternate call nfsrv_readdirplus() does lookups as well.
4255 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
4256 * are supposed to cover. For readdir, the count is the total number of
4257 * bytes included in everything from the directory's postopattr through
4258 * the EOF flag. For readdirplus, the maxcount is the same, and the
4259 * dircount includes all that except for the entry attributes and handles.
4263 struct nfsrv_descript
*nd
,
4264 struct nfsrv_sock
*slp
,
4268 struct direntry
*dp
;
4269 char *cpos
, *cend
, *rbuf
;
4271 struct vnode_attr attr
;
4272 struct nfs_filehandle nfh
;
4273 struct nfs_export
*nx
;
4274 struct nfs_export_options
*nxo
;
4276 char uio_buf
[UIO_SIZEOF(1)];
4277 int len
, nlen
, rem
, xfer
, error
, attrerr
;
4278 int siz
, count
, fullsiz
, eofflag
, nentries
;
4279 u_quad_t off
, toff
, verf
;
4281 struct nfsm_chain
*nmreq
, nmrep
;
4285 count
= nentries
= 0;
4286 nmreq
= &nd
->nd_nmreq
;
4287 nfsm_chain_null(&nmrep
);
4291 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
4293 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4294 if (nd
->nd_vers
== NFS_VER3
) {
4295 nfsm_chain_get_64(error
, nmreq
, toff
);
4296 nfsm_chain_get_64(error
, nmreq
, verf
);
4298 nfsm_chain_get_32(error
, nmreq
, toff
);
4300 nfsm_chain_get_32(error
, nmreq
, count
);
4304 siz
= ((count
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4305 xfer
= NFSRV_NDMAXDATA(nd
);
4311 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4314 /* update export stats */
4315 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4317 /* update active user stats */
4318 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4320 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4323 if (nxo
->nxo_flags
& NX_MANGLEDNAMES
|| nd
->nd_vers
== NFS_VER2
) {
4324 vnopflag
|= VNODE_READDIR_NAMEMAX
;
4327 if ((nd
->nd_vers
== NFS_VER2
) || (nxo
->nxo_flags
& NX_32BITCLIENTS
)) {
4328 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
4331 if (nd
->nd_vers
== NFS_VER3
) {
4332 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4333 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4334 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
)) {
4335 error
= NFSERR_BAD_COOKIE
;
4339 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
4343 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
)) {
4348 error
= mac_vnode_check_readdir(ctx
, vp
);
4354 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
4356 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
4357 &uio_buf
[0], sizeof(uio_buf
));
4359 if (!rbuf
|| !auio
) {
4364 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
4365 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
4367 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
4368 off
= uio_offset(auio
);
4370 if (nd
->nd_vers
== NFS_VER3
) {
4371 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4372 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4376 if (uio_resid(auio
) != 0) {
4377 siz
-= uio_resid(auio
);
4379 /* If nothing read, return empty reply with eof set */
4384 /* assemble reply */
4385 nd
->nd_repstat
= error
;
4386 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
4387 NFSX_COOKIEVERF(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
);
4389 *mrepp
= nmrep
.nmc_mhead
;
4390 nfsmout_on_status(nd
, error
);
4391 if (nd
->nd_vers
== NFS_VER3
) {
4392 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4393 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4395 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4396 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4397 nfsm_chain_build_done(error
, &nmrep
);
4403 * Check for degenerate cases of nothing useful read.
4404 * If so go try again
4408 dp
= (struct direntry
*)cpos
;
4409 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4410 cpos
+= dp
->d_reclen
;
4411 dp
= (struct direntry
*)cpos
;
4414 if ((cpos
>= cend
) || (nentries
== 0)) {
4423 /* assemble reply */
4424 nd
->nd_repstat
= error
;
4425 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
4426 NFSX_COOKIEVERF(nd
->nd_vers
) + siz
);
4428 *mrepp
= nmrep
.nmc_mhead
;
4429 nfsmout_on_status(nd
, error
);
4430 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4432 len
= 2 * NFSX_UNSIGNED
;
4433 if (nd
->nd_vers
== NFS_VER3
) {
4434 len
+= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
;
4435 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4436 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4440 /* Loop through the records and build reply */
4441 while ((cpos
< cend
) && (nentries
> 0)) {
4442 if (dp
->d_fileno
!= 0) {
4443 nlen
= dp
->d_namlen
;
4444 if ((nd
->nd_vers
== NFS_VER2
) && (nlen
> NFS_MAXNAMLEN
)) {
4445 nlen
= NFS_MAXNAMLEN
;
4447 rem
= nfsm_rndup(nlen
) - nlen
;
4448 len
+= (4 * NFSX_UNSIGNED
+ nlen
+ rem
);
4449 if (nd
->nd_vers
== NFS_VER3
) {
4450 len
+= 2 * NFSX_UNSIGNED
;
4456 /* Build the directory record xdr from the direntry. */
4457 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4458 if (nd
->nd_vers
== NFS_VER3
) {
4459 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4461 nfsm_chain_add_32(error
, &nmrep
, dp
->d_fileno
);
4463 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4464 if (nd
->nd_vers
== NFS_VER3
) {
4465 if (vnopflag
& VNODE_READDIR_SEEKOFF32
) {
4466 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4468 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4470 nfsm_chain_add_32(error
, &nmrep
, dp
->d_seekoff
);
4474 cpos
+= dp
->d_reclen
;
4475 dp
= (struct direntry
*)cpos
;
4478 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4479 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4489 nd
->nd_repstat
= error
;
4490 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
));
4492 *mrepp
= nmrep
.nmc_mhead
;
4493 nfsmout_on_status(nd
, error
);
4494 if (nd
->nd_vers
== NFS_VER3
) {
4495 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4498 nfsm_chain_build_done(error
, &nmrep
);
4500 nfsm_chain_cleanup(&nmrep
);
4508 struct nfsrv_descript
*nd
,
4509 struct nfsrv_sock
*slp
,
4513 struct direntry
*dp
;
4514 char *cpos
, *cend
, *rbuf
;
4516 struct nfs_filehandle dnfh
, nfh
;
4517 struct nfs_export
*nx
;
4518 struct nfs_export_options
*nxo
;
4520 char uio_buf
[UIO_SIZEOF(1)];
4521 struct vnode_attr attr
, va
, *vap
= &va
;
4522 int len
, nlen
, rem
, xfer
, error
, attrerr
, gotfh
, gotattr
;
4523 int siz
, dircount
, maxcount
, fullsiz
, eofflag
, dirlen
, nentries
, isdotdot
;
4524 u_quad_t off
, toff
, verf
;
4526 struct nfsm_chain
*nmreq
, nmrep
;
4531 nmreq
= &nd
->nd_nmreq
;
4532 nfsm_chain_null(&nmrep
);
4535 dircount
= maxcount
= 0;
4537 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
4539 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
4540 nfsm_chain_get_64(error
, nmreq
, toff
);
4541 nfsm_chain_get_64(error
, nmreq
, verf
);
4542 nfsm_chain_get_32(error
, nmreq
, dircount
);
4543 nfsm_chain_get_32(error
, nmreq
, maxcount
);
4547 xfer
= NFSRV_NDMAXDATA(nd
);
4548 dircount
= ((dircount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4549 if (dircount
> xfer
) {
4552 fullsiz
= siz
= dircount
;
4553 maxcount
= ((maxcount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4554 if (maxcount
> xfer
) {
4558 error
= nfsrv_fhtovp(&dnfh
, nd
, &vp
, &nx
, &nxo
);
4561 /* update export stats */
4562 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4564 /* update active user stats */
4565 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4567 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4570 if (nxo
->nxo_flags
& NX_32BITCLIENTS
) {
4571 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
4574 if (nxo
->nxo_flags
& NX_MANGLEDNAMES
) {
4575 vnopflag
|= VNODE_READDIR_NAMEMAX
;
4578 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4579 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4580 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
)) {
4581 error
= NFSERR_BAD_COOKIE
;
4584 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
4588 if (!error
&& mac_vnode_check_open(ctx
, vp
, FREAD
)) {
4593 error
= mac_vnode_check_readdir(ctx
, vp
);
4599 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
4601 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
4602 &uio_buf
[0], sizeof(uio_buf
));
4604 if (!rbuf
|| !auio
) {
4610 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
4611 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
4613 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
4614 off
= uio_offset(auio
);
4615 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4616 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4619 if (uio_resid(auio
) != 0) {
4620 siz
-= uio_resid(auio
);
4622 /* If nothing read, return empty reply with eof set */
4627 /* assemble reply */
4628 nd
->nd_repstat
= error
;
4629 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+
4630 NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
);
4632 *mrepp
= nmrep
.nmc_mhead
;
4633 nfsmout_on_status(nd
, error
);
4634 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4635 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4636 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4637 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4638 nfsm_chain_build_done(error
, &nmrep
);
4644 * Check for degenerate cases of nothing useful read.
4645 * If so go try again
4649 dp
= (struct direntry
*)cpos
;
4650 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4651 cpos
+= dp
->d_reclen
;
4652 dp
= (struct direntry
*)cpos
;
4655 if ((cpos
>= cend
) || (nentries
== 0)) {
4662 * Probe one of the directory entries to see if the filesystem
4665 if ((error
= VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
))) {
4666 if (error
== ENOTSUP
) { /* let others get passed back */
4667 error
= NFSERR_NOTSUPP
;
4673 /* assemble reply */
4674 nd
->nd_repstat
= error
;
4675 error
= nfsrv_rephead(nd
, slp
, &nmrep
, maxcount
);
4677 *mrepp
= nmrep
.nmc_mhead
;
4678 nfsmout_on_status(nd
, error
);
4679 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4681 dirlen
= len
= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
;
4682 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4683 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4686 /* Loop through the records and build reply */
4687 while ((cpos
< cend
) && (nentries
> 0)) {
4688 if (dp
->d_fileno
!= 0) {
4689 nlen
= dp
->d_namlen
;
4690 rem
= nfsm_rndup(nlen
) - nlen
;
4691 gotfh
= gotattr
= 1;
4693 /* Got to get the vnode for lookup per entry. */
4694 if (VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
)) {
4695 /* Can't get the vnode... so no fh or attrs */
4696 gotfh
= gotattr
= 0;
4698 isdotdot
= ((dp
->d_namlen
== 2) &&
4699 (dp
->d_name
[0] == '.') && (dp
->d_name
[1] == '.'));
4700 if (nfsrv_vptofh(nx
, 0, (isdotdot
? &dnfh
: NULL
), nvp
, ctx
, &nfh
)) {
4703 nfsm_srv_vattr_init(vap
, NFS_VER3
);
4704 if (vnode_getattr(nvp
, vap
, ctx
)) {
4711 * If either the dircount or maxcount will be
4712 * exceeded, get out now. Both of these lengths
4713 * are calculated conservatively, including all
4716 len
+= 8 * NFSX_UNSIGNED
+ nlen
+ rem
;
4718 len
+= NFSX_V3FATTR
;
4721 len
+= NFSX_UNSIGNED
+ nfsm_rndup(nfh
.nfh_len
);
4723 dirlen
+= 6 * NFSX_UNSIGNED
+ nlen
+ rem
;
4724 if ((len
> maxcount
) || (dirlen
> dircount
)) {
4729 /* Build the directory record xdr from the direntry. */
4730 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4731 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4732 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4733 if (vnopflag
& VNODE_READDIR_SEEKOFF32
) {
4734 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4736 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4737 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, (gotattr
? 0 : ENOENT
), vap
);
4739 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4741 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4745 cpos
+= dp
->d_reclen
;
4746 dp
= (struct direntry
*)cpos
;
4751 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4752 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4759 nd
->nd_repstat
= error
;
4760 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
);
4762 *mrepp
= nmrep
.nmc_mhead
;
4763 nfsmout_on_status(nd
, error
);
4764 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4766 nfsm_chain_build_done(error
, &nmrep
);
4771 nfsm_chain_cleanup(&nmrep
);
4778 * nfs commit service
4782 struct nfsrv_descript
*nd
,
4783 struct nfsrv_sock
*slp
,
4788 struct nfs_filehandle nfh
;
4789 struct nfs_export
*nx
;
4790 struct nfs_export_options
*nxo
;
4791 int error
, preattrerr
, postattrerr
, count
;
4792 struct vnode_attr preattr
, postattr
;
4794 struct nfsm_chain
*nmreq
, nmrep
;
4797 preattrerr
= postattrerr
= ENOENT
;
4798 nmreq
= &nd
->nd_nmreq
;
4799 nfsm_chain_null(&nmrep
);
4803 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4804 * count parameters, so those arguments are useless (someday maybe).
4807 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4808 nfsm_chain_get_64(error
, nmreq
, off
);
4809 nfsm_chain_get_32(error
, nmreq
, count
);
4812 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4815 /* update export stats */
4816 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4818 /* update active user stats */
4819 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4821 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4824 nfsm_srv_pre_vattr_init(&preattr
);
4825 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
4827 error
= VNOP_FSYNC(vp
, MNT_WAIT
, ctx
);
4829 nfsm_srv_vattr_init(&postattr
, 1);
4830 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
4837 /* assemble reply */
4838 nd
->nd_repstat
= error
;
4839 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3WCCDATA
+ NFSX_V3WRITEVERF
);
4841 *mrepp
= nmrep
.nmc_mhead
;
4842 nfsmout_on_status(nd
, error
);
4843 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4844 preattrerr
, &preattr
, postattrerr
, &postattr
);
4845 if (!nd
->nd_repstat
) {
4846 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
4847 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
4850 nfsm_chain_build_done(error
, &nmrep
);
4852 nfsm_chain_cleanup(&nmrep
);
4859 * nfs statfs service
4863 struct nfsrv_descript
*nd
,
4864 struct nfsrv_sock
*slp
,
4871 struct vnode_attr attr
;
4872 struct nfs_filehandle nfh
;
4873 struct nfs_export
*nx
;
4874 struct nfs_export_options
*nxo
;
4876 struct nfsm_chain
*nmreq
, nmrep
;
4880 nmreq
= &nd
->nd_nmreq
;
4881 nfsm_chain_null(&nmrep
);
4885 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4887 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4890 /* update export stats */
4891 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4893 /* update active user stats */
4894 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4896 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4900 VFSATTR_WANTED(&va
, f_blocks
);
4901 VFSATTR_WANTED(&va
, f_bavail
);
4902 VFSATTR_WANTED(&va
, f_files
);
4903 VFSATTR_WANTED(&va
, f_ffree
);
4904 error
= vfs_getattr(vnode_mount(vp
), &va
, ctx
);
4905 blksize
= vnode_mount(vp
)->mnt_vfsstat
.f_bsize
;
4907 if (nd
->nd_vers
== NFS_VER3
) {
4908 nfsm_srv_vattr_init(&attr
, nd
->nd_vers
);
4909 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4917 /* assemble reply */
4918 nd
->nd_repstat
= error
;
4919 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_STATFS(nd
->nd_vers
));
4921 *mrepp
= nmrep
.nmc_mhead
;
4922 nfsmout_on_status(nd
, error
);
4923 if (nd
->nd_vers
== NFS_VER3
) {
4924 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4926 nfsmout_if(nd
->nd_repstat
);
4928 if (nd
->nd_vers
== NFS_VER3
) {
4929 nfsm_chain_add_64(error
, &nmrep
, va
.f_blocks
* blksize
);
4930 nfsm_chain_add_64(error
, &nmrep
, va
.f_bfree
* blksize
);
4931 nfsm_chain_add_64(error
, &nmrep
, va
.f_bavail
* blksize
);
4932 nfsm_chain_add_64(error
, &nmrep
, va
.f_files
);
4933 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4934 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4935 nfsm_chain_add_32(error
, &nmrep
, 0); /* invarsec */
4937 nfsm_chain_add_32(error
, &nmrep
, NFS_V2MAXDATA
);
4938 nfsm_chain_add_32(error
, &nmrep
, blksize
);
4939 nfsm_chain_add_32(error
, &nmrep
, va
.f_blocks
);
4940 nfsm_chain_add_32(error
, &nmrep
, va
.f_bfree
);
4941 nfsm_chain_add_32(error
, &nmrep
, va
.f_bavail
);
4944 nfsm_chain_build_done(error
, &nmrep
);
4946 nfsm_chain_cleanup(&nmrep
);
4953 * nfs fsinfo service
4957 struct nfsrv_descript
*nd
,
4958 struct nfsrv_sock
*slp
,
4962 int error
, attrerr
, prefsize
, maxsize
;
4964 struct vnode_attr attr
;
4965 struct nfs_filehandle nfh
;
4966 struct nfs_export
*nx
;
4967 struct nfs_export_options
*nxo
;
4968 struct nfsm_chain
*nmreq
, nmrep
;
4972 nmreq
= &nd
->nd_nmreq
;
4973 nfsm_chain_null(&nmrep
);
4976 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4978 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4981 /* update export stats */
4982 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4984 /* update active user stats */
4985 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4987 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4990 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4991 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4998 /* assemble reply */
4999 nd
->nd_repstat
= error
;
5000 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3FSINFO
);
5002 *mrepp
= nmrep
.nmc_mhead
;
5003 nfsmout_on_status(nd
, error
);
5004 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
5005 nfsmout_if(nd
->nd_repstat
);
5008 * XXX There should be file system VFS OP(s) to get this information.
5009 * For now, assume our usual NFS defaults.
5011 if (slp
->ns_sotype
== SOCK_DGRAM
) {
5012 maxsize
= NFS_MAXDGRAMDATA
;
5013 prefsize
= NFS_PREFDGRAMDATA
;
5015 maxsize
= prefsize
= NFSRV_MAXDATA
;
5018 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
5019 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5020 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
5021 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
5022 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5023 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
5024 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
5025 nfsm_chain_add_64(error
, &nmrep
, 0xffffffffffffffffULL
);
5026 nfsm_chain_add_32(error
, &nmrep
, 0);
5027 nfsm_chain_add_32(error
, &nmrep
, 1);
5028 /* XXX link/symlink support should be taken from volume capabilities */
5029 nfsm_chain_add_32(error
, &nmrep
,
5030 NFSV3FSINFO_LINK
| NFSV3FSINFO_SYMLINK
|
5031 NFSV3FSINFO_HOMOGENEOUS
| NFSV3FSINFO_CANSETTIME
);
5034 nfsm_chain_build_done(error
, &nmrep
);
5036 nfsm_chain_cleanup(&nmrep
);
5043 * nfs pathconf service
5047 struct nfsrv_descript
*nd
,
5048 struct nfsrv_sock
*slp
,
5052 int error
, attrerr
, linkmax
, namemax
;
5053 int chownres
, notrunc
, case_sensitive
, case_preserving
;
5055 struct vnode_attr attr
;
5056 struct nfs_filehandle nfh
;
5057 struct nfs_export
*nx
;
5058 struct nfs_export_options
*nxo
;
5059 struct nfsm_chain
*nmreq
, nmrep
;
5063 nmreq
= &nd
->nd_nmreq
;
5064 nfsm_chain_null(&nmrep
);
5067 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
5069 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
5072 /* update export stats */
5073 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
5075 /* update active user stats */
5076 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
5078 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
5081 error
= VNOP_PATHCONF(vp
, _PC_LINK_MAX
, &linkmax
, ctx
);
5083 error
= VNOP_PATHCONF(vp
, _PC_NAME_MAX
, &namemax
, ctx
);
5086 error
= VNOP_PATHCONF(vp
, _PC_CHOWN_RESTRICTED
, &chownres
, ctx
);
5089 error
= VNOP_PATHCONF(vp
, _PC_NO_TRUNC
, ¬runc
, ctx
);
5092 error
= VNOP_PATHCONF(vp
, _PC_CASE_SENSITIVE
, &case_sensitive
, ctx
);
5095 error
= VNOP_PATHCONF(vp
, _PC_CASE_PRESERVING
, &case_preserving
, ctx
);
5098 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
5099 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
5106 /* assemble reply */
5107 nd
->nd_repstat
= error
;
5108 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3PATHCONF
);
5110 *mrepp
= nmrep
.nmc_mhead
;
5111 nfsmout_on_status(nd
, error
);
5112 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
5113 nfsmout_if(nd
->nd_repstat
);
5115 nfsm_chain_add_32(error
, &nmrep
, linkmax
);
5116 nfsm_chain_add_32(error
, &nmrep
, namemax
);
5117 nfsm_chain_add_32(error
, &nmrep
, notrunc
);
5118 nfsm_chain_add_32(error
, &nmrep
, chownres
);
5119 nfsm_chain_add_32(error
, &nmrep
, !case_sensitive
);
5120 nfsm_chain_add_32(error
, &nmrep
, case_preserving
);
5123 nfsm_chain_build_done(error
, &nmrep
);
5125 nfsm_chain_cleanup(&nmrep
);
5132 * Null operation, used by clients to ping server
5137 struct nfsrv_descript
*nd
,
5138 struct nfsrv_sock
*slp
,
5139 __unused vfs_context_t ctx
,
5142 int error
= NFSERR_RETVOID
;
5143 struct nfsm_chain nmrep
;
5146 * RPCSEC_GSS context setup ?
5148 if (nd
->nd_gss_context
) {
5149 return nfs_gss_svc_ctx_init(nd
, slp
, mrepp
);
5152 nfsm_chain_null(&nmrep
);
5154 /* assemble reply */
5155 nd
->nd_repstat
= error
;
5156 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
5158 *mrepp
= nmrep
.nmc_mhead
;
5160 nfsm_chain_build_done(error
, &nmrep
);
5162 nfsm_chain_cleanup(&nmrep
);
5169 * No operation, used for obsolete procedures
5174 struct nfsrv_descript
*nd
,
5175 struct nfsrv_sock
*slp
,
5176 __unused vfs_context_t ctx
,
5180 struct nfsm_chain nmrep
;
5182 nfsm_chain_null(&nmrep
);
5184 if (nd
->nd_repstat
) {
5185 error
= nd
->nd_repstat
;
5187 error
= EPROCUNAVAIL
;
5190 /* assemble reply */
5191 nd
->nd_repstat
= error
;
5192 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
5194 *mrepp
= nmrep
.nmc_mhead
;
5196 nfsm_chain_build_done(error
, &nmrep
);
5198 nfsm_chain_cleanup(&nmrep
);
5204 const nfsrv_proc_t nfsrv_procs
[NFS_NPROCS
] = {
5231 * Perform access checking for vnodes obtained from file handles that would
5232 * refer to files already opened by a Unix client. You cannot just use
5233 * vnode_authorize() for two reasons.
5234 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
5235 * 2 - The owner is to be given access irrespective of mode bits so that
5236 * processes that chmod after opening a file don't break. I don't like
5237 * this because it opens a security hole, but since the nfs server opens
5238 * a security hole the size of a barn door anyhow, what the heck.
5240 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
5241 * will return EPERM instead of EACCESS. EPERM is always an error.
5248 kauth_action_t action
,
5250 struct nfs_export_options
*nxo
,
5253 struct vnode_attr vattr
;
5256 if (action
& KAUTH_VNODE_WRITE_RIGHTS
) {
5258 * Disallow write attempts on read-only exports;
5259 * unless the file is a socket or a block or character
5260 * device resident on the file system.
5262 if (nxo
->nxo_flags
& NX_READONLY
) {
5263 switch (vnode_vtype(vp
)) {
5264 case VREG
: case VDIR
: case VLNK
: case VCPLX
:
5271 error
= vnode_authorize(vp
, dvp
, action
, ctx
);
5273 * Allow certain operations for the owner (reads and writes
5274 * on files that are already open). Picking up from FreeBSD.
5276 if (override
&& (error
== EACCES
)) {
5278 VATTR_WANTED(&vattr
, va_uid
);
5279 if ((vnode_getattr(vp
, &vattr
, ctx
) == 0) &&
5280 (kauth_cred_getuid(vfs_context_ucred(ctx
)) == vattr
.va_uid
)) {
5287 #endif /* NFSSERVER */