2 * Copyright (c) 2000-2007 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>
90 #include <sys/vmparam.h>
92 #include <nfs/nfsproto.h>
93 #include <nfs/rpcv2.h>
95 #include <nfs/xdr_subs.h>
96 #include <nfs/nfsm_subs.h>
97 #include <nfs/nfsrvcache.h>
98 #include <nfs/nfs_gss.h>
106 int nfsd_thread_count
= 0;
107 int nfsd_thread_max
= 0;
108 lck_grp_t
*nfsd_lck_grp
;
109 lck_mtx_t
*nfsd_mutex
;
110 struct nfsd_head nfsd_head
, nfsd_queue
;
112 lck_grp_t
*nfsrv_slp_rwlock_group
;
113 lck_grp_t
*nfsrv_slp_mutex_group
;
114 struct nfsrv_sockhead nfsrv_socklist
, nfsrv_deadsocklist
, nfsrv_sockwg
,
115 nfsrv_sockwait
, nfsrv_sockwork
;
116 struct nfsrv_sock
*nfsrv_udpsock
= NULL
;
119 struct nfsrv_expfs_list nfsrv_exports
;
120 struct nfsrv_export_hashhead
*nfsrv_export_hashtbl
;
121 u_long nfsrv_export_hash
;
122 lck_grp_t
*nfsrv_export_rwlock_group
;
123 lck_rw_t nfsrv_export_rwlock
;
125 /* NFS server file modification event generator */
126 struct nfsrv_fmod_hashhead
*nfsrv_fmod_hashtbl
;
127 u_long nfsrv_fmod_hash
;
128 lck_grp_t
*nfsrv_fmod_grp
;
129 lck_mtx_t
*nfsrv_fmod_mutex
;
130 static int nfsrv_fmod_timer_on
= 0;
132 int nfsrv_fsevents_enabled
= 1;
134 /* NFS server timers */
135 thread_call_t nfsrv_fmod_timer_call
;
136 thread_call_t nfsrv_deadsock_timer_call
;
137 thread_call_t nfsrv_wg_timer_call
;
138 int nfsrv_wg_timer_on
;
140 /* globals for the active user list */
141 uint32_t nfsrv_user_stat_enabled
= 1;
142 uint32_t nfsrv_user_stat_node_count
= 0;
143 uint32_t nfsrv_user_stat_max_idle_sec
= NFSRV_USER_STAT_DEF_IDLE_SEC
;
144 uint32_t nfsrv_user_stat_max_nodes
= NFSRV_USER_STAT_DEF_MAX_NODES
;
145 lck_grp_t
*nfsrv_active_user_mutex_group
;
147 int nfsrv_wg_delay
= NFSRV_WGATHERDELAY
* 1000;
148 int nfsrv_wg_delay_v3
= 0;
152 static int nfsrv_authorize(vnode_t
,vnode_t
,kauth_action_t
,vfs_context_t
,struct nfs_export_options
*,int);
153 static int nfsrv_wg_coalesce(struct nfsrv_descript
*, struct nfsrv_descript
*);
155 extern void IOSleep(int);
158 * Initialize the data structures for the server.
161 #define NFSRV_NOT_INITIALIZED 0
162 #define NFSRV_INITIALIZING 1
163 #define NFSRV_INITIALIZED 2
164 static volatile UInt32 nfsrv_initted
= NFSRV_NOT_INITIALIZED
;
167 nfsrv_is_initialized(void)
169 return (nfsrv_initted
== NFSRV_INITIALIZED
);
175 /* make sure we init only once */
176 if (!OSCompareAndSwap(NFSRV_NOT_INITIALIZED
, NFSRV_INITIALIZING
, &nfsrv_initted
)) {
177 /* wait until initialization is complete */
178 while (!nfsrv_is_initialized())
183 if (sizeof (struct nfsrv_sock
) > NFS_SVCALLOC
) {
184 printf("struct nfsrv_sock bloated (> %dbytes)\n",NFS_SVCALLOC
);
185 printf("Try reducing NFS_UIDHASHSIZ\n");
188 /* init nfsd mutex */
189 nfsd_lck_grp
= lck_grp_alloc_init("nfsd", LCK_GRP_ATTR_NULL
);
190 nfsd_mutex
= lck_mtx_alloc_init(nfsd_lck_grp
, LCK_ATTR_NULL
);
192 /* init slp rwlock */
193 nfsrv_slp_rwlock_group
= lck_grp_alloc_init("nfsrv-slp-rwlock", LCK_GRP_ATTR_NULL
);
194 nfsrv_slp_mutex_group
= lck_grp_alloc_init("nfsrv-slp-mutex", LCK_GRP_ATTR_NULL
);
196 /* init export data structures */
197 nfsrv_export_hashtbl
= hashinit(8, M_TEMP
, &nfsrv_export_hash
);
198 LIST_INIT(&nfsrv_exports
);
199 nfsrv_export_rwlock_group
= lck_grp_alloc_init("nfsrv-export-rwlock", LCK_GRP_ATTR_NULL
);
200 lck_rw_init(&nfsrv_export_rwlock
, nfsrv_export_rwlock_group
, LCK_ATTR_NULL
);
202 /* init active user list mutex structures */
203 nfsrv_active_user_mutex_group
= lck_grp_alloc_init("nfs-active-user-mutex", LCK_GRP_ATTR_NULL
);
205 /* init nfs server request cache mutex */
206 nfsrv_reqcache_lck_grp
= lck_grp_alloc_init("nfsrv_reqcache", LCK_GRP_ATTR_NULL
);
207 nfsrv_reqcache_mutex
= lck_mtx_alloc_init(nfsrv_reqcache_lck_grp
, LCK_ATTR_NULL
);
209 /* init NFS server file modified event generation */
210 nfsrv_fmod_hashtbl
= hashinit(NFSRVFMODHASHSZ
, M_TEMP
, &nfsrv_fmod_hash
);
211 nfsrv_fmod_grp
= lck_grp_alloc_init("nfsrv_fmod", LCK_GRP_ATTR_NULL
);
212 nfsrv_fmod_mutex
= lck_mtx_alloc_init(nfsrv_fmod_grp
, LCK_ATTR_NULL
);
214 /* initialize NFS server timer callouts */
215 nfsrv_fmod_timer_call
= thread_call_allocate(nfsrv_fmod_timer
, NULL
);
216 nfsrv_deadsock_timer_call
= thread_call_allocate(nfsrv_deadsock_timer
, NULL
);
217 nfsrv_wg_timer_call
= thread_call_allocate(nfsrv_wg_timer
, NULL
);
219 /* Init server data structures */
220 TAILQ_INIT(&nfsrv_socklist
);
221 TAILQ_INIT(&nfsrv_sockwait
);
222 TAILQ_INIT(&nfsrv_sockwork
);
223 TAILQ_INIT(&nfsrv_deadsocklist
);
224 TAILQ_INIT(&nfsrv_sockwg
);
225 TAILQ_INIT(&nfsd_head
);
226 TAILQ_INIT(&nfsd_queue
);
227 nfsrv_udpsock
= NULL
;
229 /* initialization complete */
230 nfsrv_initted
= NFSRV_INITIALIZED
;
236 * NFS version 2 and 3 server request processing functions
238 * These functions take the following parameters:
240 * struct nfsrv_descript *nd - the NFS request descriptor
241 * struct nfsrv_sock *slp - the NFS socket the request came in on
242 * vfs_context_t ctx - VFS context
243 * mbuf_t *mrepp - pointer to hold the reply mbuf list
245 * These routines generally have 3 phases:
247 * 1 - break down and validate the RPC request in the mbuf chain
248 * provided in nd->nd_nmreq.
249 * 2 - perform the vnode operations for the request
250 * (many are very similar to syscalls in vfs_syscalls.c and
251 * should therefore be kept in sync with those implementations)
252 * 3 - build the RPC reply in an mbuf chain (nmrep) and return the mbuf chain
257 * nfs v3 access service
261 struct nfsrv_descript
*nd
,
262 struct nfsrv_sock
*slp
,
266 struct nfsm_chain
*nmreq
, nmrep
;
269 struct vnode_attr vattr
;
270 struct nfs_filehandle nfh
;
272 kauth_action_t testaction
;
273 struct nfs_export
*nx
;
274 struct nfs_export_options
*nxo
;
279 nmreq
= &nd
->nd_nmreq
;
280 nfsm_chain_null(&nmrep
);
284 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
285 nfsm_chain_get_32(error
, nmreq
, nfsmode
);
287 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
290 /* update export stats */
291 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
293 /* update active user stats */
294 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
296 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
300 * Each NFS mode bit is tested separately.
302 * XXX this code is nominally correct, but returns a pessimistic
303 * rather than optimistic result. It will be necessary to add
304 * an NFS-specific interface to the vnode_authorize code to
305 * obtain good performance in the optimistic mode.
307 if (nfsmode
& NFS_ACCESS_READ
) {
308 if (vnode_isdir(vp
)) {
310 KAUTH_VNODE_LIST_DIRECTORY
|
311 KAUTH_VNODE_READ_EXTATTRIBUTES
;
314 KAUTH_VNODE_READ_DATA
|
315 KAUTH_VNODE_READ_EXTATTRIBUTES
;
317 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0))
318 nfsmode
&= ~NFS_ACCESS_READ
;
320 if ((nfsmode
& NFS_ACCESS_LOOKUP
) &&
322 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_SEARCH
, ctx
, nxo
, 0)))
323 nfsmode
&= ~NFS_ACCESS_LOOKUP
;
324 if (nfsmode
& NFS_ACCESS_MODIFY
) {
325 if (vnode_isdir(vp
)) {
327 KAUTH_VNODE_ADD_FILE
|
328 KAUTH_VNODE_ADD_SUBDIRECTORY
|
329 KAUTH_VNODE_DELETE_CHILD
;
332 KAUTH_VNODE_WRITE_DATA
;
334 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0))
335 nfsmode
&= ~NFS_ACCESS_MODIFY
;
337 if (nfsmode
& NFS_ACCESS_EXTEND
) {
338 if (vnode_isdir(vp
)) {
340 KAUTH_VNODE_ADD_FILE
|
341 KAUTH_VNODE_ADD_SUBDIRECTORY
;
344 KAUTH_VNODE_WRITE_DATA
|
345 KAUTH_VNODE_APPEND_DATA
;
347 if (nfsrv_authorize(vp
, NULL
, testaction
, ctx
, nxo
, 0))
348 nfsmode
&= ~NFS_ACCESS_EXTEND
;
352 * Note concerning NFS_ACCESS_DELETE:
353 * For hard links, the answer may be wrong if the vnode
354 * has multiple parents with different permissions.
355 * Also, some clients (e.g. MacOSX 10.3) may incorrectly
356 * interpret the missing/cleared DELETE bit.
357 * So we'll just leave the DELETE bit alone. At worst,
358 * we're telling the client it might be able to do
359 * something it really can't.
362 if ((nfsmode
& NFS_ACCESS_EXECUTE
) &&
364 nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 0)))
365 nfsmode
&= ~NFS_ACCESS_EXECUTE
;
367 /* get postop attributes */
368 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
369 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
373 nd
->nd_repstat
= error
;
374 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(NFS_VER3
) + NFSX_UNSIGNED
);
376 *mrepp
= nmrep
.nmc_mhead
;
377 nfsmout_on_status(nd
, error
);
378 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
380 nfsm_chain_add_32(error
, &nmrep
, nfsmode
);
382 nfsm_chain_build_done(error
, &nmrep
);
386 nfsm_chain_cleanup(&nmrep
);
393 * nfs getattr service
397 struct nfsrv_descript
*nd
,
398 struct nfsrv_sock
*slp
,
402 struct nfsm_chain
*nmreq
, nmrep
;
403 struct vnode_attr vattr
;
406 struct nfs_filehandle nfh
;
407 struct nfs_export
*nx
;
408 struct nfs_export_options
*nxo
;
411 nmreq
= &nd
->nd_nmreq
;
412 nfsm_chain_null(&nmrep
);
416 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
418 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
421 /* update export stats */
422 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
424 /* update active user stats */
425 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
427 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
430 nfsm_srv_vattr_init(&vattr
, nd
->nd_vers
);
431 error
= vnode_getattr(vp
, &vattr
, ctx
);
437 nd
->nd_repstat
= error
;
438 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_FATTR(nd
->nd_vers
));
440 *mrepp
= nmrep
.nmc_mhead
;
441 nfsmout_if(nd
->nd_repstat
);
442 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &vattr
);
444 nfsm_chain_build_done(error
, &nmrep
);
448 nfsm_chain_cleanup(&nmrep
);
455 * nfs setattr service
459 struct nfsrv_descript
*nd
,
460 struct nfsrv_sock
*slp
,
464 struct nfsm_chain
*nmreq
, nmrep
;
465 struct vnode_attr preattr
, postattr
;
466 struct vnode_attr vattr
, *vap
= &vattr
;
468 struct nfs_export
*nx
;
469 struct nfs_export_options
*nxo
;
470 int error
, preattrerr
, postattrerr
, gcheck
;
471 struct nfs_filehandle nfh
;
472 struct timespec guard
= { 0, 0 };
473 kauth_action_t action
;
477 preattrerr
= postattrerr
= ENOENT
;
479 nmreq
= &nd
->nd_nmreq
;
480 nfsm_chain_null(&nmrep
);
484 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
488 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
489 if (nd
->nd_vers
== NFS_VER3
) {
490 nfsm_chain_get_32(error
, nmreq
, gcheck
);
492 nfsm_chain_get_time(error
, nmreq
, nd
->nd_vers
, guard
.tv_sec
, guard
.tv_nsec
);
497 * Save the original credential UID in case they are
498 * mapped and we need to map the IDs in the attributes.
500 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
503 * Now that we have all the fields, lets do it.
505 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
508 /* update export stats */
509 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
511 /* update active user stats */
512 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
514 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
517 if (nd
->nd_vers
== NFS_VER3
) {
518 nfsm_srv_pre_vattr_init(&preattr
);
519 error
= preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
520 if (!error
&& gcheck
&& VATTR_IS_SUPPORTED(&preattr
, va_change_time
) &&
521 (preattr
.va_change_time
.tv_sec
!= guard
.tv_sec
||
522 preattr
.va_change_time
.tv_nsec
!= guard
.tv_nsec
))
523 error
= NFSERR_NOT_SYNC
;
524 if (!preattrerr
&& !VATTR_ALL_SUPPORTED(&preattr
))
530 * If the credentials were mapped, we should
531 * map the same values in the attributes.
533 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
535 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
536 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
)
537 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
540 /* Authorize the attribute changes. */
541 error
= vnode_authattr(vp
, vap
, &action
, ctx
);
543 error
= nfsrv_authorize(vp
, NULL
, action
, ctx
, nxo
, 0);
545 /* set the new attributes */
547 error
= vnode_setattr(vp
, vap
, ctx
);
549 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
550 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
551 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
561 nd
->nd_repstat
= error
;
562 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCORFATTR(nd
->nd_vers
));
564 *mrepp
= nmrep
.nmc_mhead
;
565 nfsmout_on_status(nd
, error
);
566 if (nd
->nd_vers
== NFS_VER3
)
567 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
568 preattrerr
, &preattr
, postattrerr
, &postattr
);
570 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
572 nfsm_chain_build_done(error
, &nmrep
);
574 nfsm_chain_cleanup(&nmrep
);
585 struct nfsrv_descript
*nd
,
586 struct nfsrv_sock
*slp
,
590 struct nameidata ni
, *nip
= &ni
;
591 vnode_t vp
, dirp
= NULL
;
592 struct nfs_filehandle dnfh
, nfh
;
593 struct nfs_export
*nx
= NULL
;
594 struct nfs_export_options
*nxo
;
595 int error
, attrerr
, dirattrerr
, isdotdot
;
598 struct vnode_attr va
, dirattr
, *vap
= &va
;
599 struct nfsm_chain
*nmreq
, nmrep
;
602 attrerr
= dirattrerr
= ENOENT
;
603 nmreq
= &nd
->nd_nmreq
;
604 nfsm_chain_null(&nmrep
);
605 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
607 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
608 nfsm_chain_get_32(error
, nmreq
, len
);
609 nfsm_name_len_check(error
, nd
, len
);
612 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
613 ni
.ni_cnd
.cn_flags
= LOCKLEAF
;
614 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
615 isdotdot
= ((len
== 2) && (ni
.ni_cnd
.cn_pnbuf
[0] == '.') && (ni
.ni_cnd
.cn_pnbuf
[1] == '.'));
617 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
619 /* update export stats */
620 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
622 /* update active user stats */
623 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
628 if (nd
->nd_vers
== NFS_VER3
) {
629 nfsm_srv_vattr_init(&dirattr
, NFS_VER3
);
630 dirattrerr
= vnode_getattr(dirp
, &dirattr
, ctx
);
639 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, (isdotdot
? &dnfh
: NULL
), vp
, ctx
, &nfh
);
641 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
642 attrerr
= vnode_getattr(vp
, vap
, ctx
);
648 nd
->nd_repstat
= error
;
649 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
650 NFSX_POSTOPORFATTR(nd
->nd_vers
) + NFSX_POSTOPATTR(nd
->nd_vers
));
652 *mrepp
= nmrep
.nmc_mhead
;
653 if (nd
->nd_repstat
) {
654 if (nd
->nd_vers
== NFS_VER3
)
655 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
658 nfsm_chain_add_fh(error
, &nmrep
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
659 if (nd
->nd_vers
== NFS_VER3
) {
660 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
661 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, dirattrerr
, &dirattr
);
663 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
666 nfsm_chain_build_done(error
, &nmrep
);
668 nfsm_chain_cleanup(&nmrep
);
675 * nfs readlink service
679 struct nfsrv_descript
*nd
,
680 struct nfsrv_sock
*slp
,
684 int error
, mpcnt
, tlen
, len
, attrerr
;
686 struct vnode_attr vattr
;
687 struct nfs_filehandle nfh
;
688 struct nfs_export
*nx
;
689 struct nfs_export_options
*nxo
;
690 struct nfsm_chain
*nmreq
, nmrep
;
693 char uio_buf
[ UIO_SIZEOF(4) ];
694 char *uio_bufp
= &uio_buf
[0];
695 int uio_buflen
= UIO_SIZEOF(4);
699 nmreq
= &nd
->nd_nmreq
;
700 nfsm_chain_null(&nmrep
);
703 len
= NFS_MAXPATHLEN
;
705 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
708 /* get mbuf list to hold symlink path */
709 error
= nfsm_mbuf_get_list(len
, &mpath
, &mpcnt
);
712 uio_buflen
= UIO_SIZEOF(mpcnt
);
713 MALLOC(uio_bufp
, char*, uio_buflen
, M_TEMP
, M_WAITOK
);
718 uiop
= uio_createwithbuffer(mpcnt
, 0, UIO_SYSSPACE
, UIO_READ
, uio_bufp
, uio_buflen
);
723 for (mp
= mpath
; mp
; mp
= mbuf_next(mp
))
724 uio_addiov(uiop
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(mp
)), mbuf_len(mp
));
726 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
729 /* update export stats */
730 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
732 /* update active user stats */
733 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
735 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
738 if (vnode_vtype(vp
) != VLNK
) {
739 if (nd
->nd_vers
== NFS_VER3
)
746 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 0);
748 error
= VNOP_READLINK(vp
, uiop
, ctx
);
750 if (nd
->nd_vers
== NFS_VER3
) {
751 nfsm_srv_vattr_init(&vattr
, NFS_VER3
);
752 attrerr
= vnode_getattr(vp
, &vattr
, ctx
);
764 nd
->nd_repstat
= error
;
765 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_UNSIGNED
);
767 *mrepp
= nmrep
.nmc_mhead
;
768 nfsmout_on_status(nd
, error
);
769 if (nd
->nd_vers
== NFS_VER3
)
770 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &vattr
);
771 if (error
|| nd
->nd_repstat
) {
772 nfsm_chain_build_done(error
, &nmrep
);
775 if (uiop
&& (uio_resid(uiop
) > 0)) {
776 // LP64todo - fix this
777 len
-= uio_resid(uiop
);
778 tlen
= nfsm_rndup(len
);
779 nfsm_adj(mpath
, NFS_MAXPATHLEN
-tlen
, tlen
-len
);
781 nfsm_chain_add_32(error
, &nmrep
, len
);
782 nfsm_chain_build_done(error
, &nmrep
);
784 error
= mbuf_setnext(nmrep
.nmc_mcur
, mpath
);
792 if (uio_bufp
!= &uio_buf
[0])
793 FREE(uio_bufp
, M_TEMP
);
795 nfsm_chain_cleanup(&nmrep
);
806 struct nfsrv_descript
*nd
,
807 struct nfsrv_sock
*slp
,
811 int error
, attrerr
, mreadcnt
;
812 uint32_t reqlen
, maxlen
, count
, len
, tlen
, left
;
815 struct nfs_filehandle nfh
;
816 struct nfs_export
*nx
;
817 struct nfs_export_options
*nxo
;
819 char *uio_bufp
= NULL
;
820 struct vnode_attr vattr
, *vap
= &vattr
;
823 char uio_buf
[ UIO_SIZEOF(0) ];
824 struct nfsm_chain
*nmreq
, nmrep
;
828 nmreq
= &nd
->nd_nmreq
;
829 nfsm_chain_null(&nmrep
);
833 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
835 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
837 if (nd
->nd_vers
== NFS_VER3
)
838 nfsm_chain_get_64(error
, nmreq
, off
);
840 nfsm_chain_get_32(error
, nmreq
, off
);
841 nfsm_chain_get_32(error
, nmreq
, reqlen
);
842 maxlen
= NFS_SRVMAXDATA(nd
);
846 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
849 /* update export stats */
850 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
852 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
855 if (vnode_vtype(vp
) != VREG
) {
856 if (nd
->nd_vers
== NFS_VER3
)
859 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
863 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
, nxo
, 1)))
864 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_EXECUTE
, ctx
, nxo
, 1);
866 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
867 attrerr
= vnode_getattr(vp
, vap
, ctx
);
872 if ((u_quad_t
)off
>= vap
->va_data_size
)
874 else if (((u_quad_t
)off
+ reqlen
) > vap
->va_data_size
)
875 count
= nfsm_rndup(vap
->va_data_size
- off
);
881 /* get mbuf list to hold read data */
882 error
= nfsm_mbuf_get_list(count
, &mread
, &mreadcnt
);
884 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mreadcnt
), M_TEMP
, M_WAITOK
);
886 uiop
= uio_createwithbuffer(mreadcnt
, off
, UIO_SYSSPACE
,
887 UIO_READ
, uio_bufp
, UIO_SIZEOF(mreadcnt
));
888 if (!uio_bufp
|| !uiop
) {
892 for (m
= mread
; m
; m
= mbuf_next(m
))
893 uio_addiov(uiop
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mbuf_len(m
));
894 error
= VNOP_READ(vp
, uiop
, IO_NODELOCKED
, ctx
);
896 uiop
= uio_createwithbuffer(0, 0, UIO_SYSSPACE
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
904 if (!error
|| (nd
->nd_vers
== NFS_VER3
)) {
905 nfsm_srv_vattr_init(vap
, nd
->nd_vers
);
906 attrerr
= vnode_getattr(vp
, vap
, ctx
);
907 if (!error
&& (nd
->nd_vers
== NFS_VER2
))
908 error
= attrerr
; /* NFSv2 must have attributes to return */
915 /* trim off any data not actually read */
916 // LP64todo - fix this
917 len
-= uio_resid(uiop
);
918 tlen
= nfsm_rndup(len
);
919 if (count
!= tlen
|| tlen
!= len
)
920 nfsm_adj(mread
, count
- tlen
, tlen
- len
);
924 nd
->nd_repstat
= error
;
925 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPORFATTR(nd
->nd_vers
) + 3 * NFSX_UNSIGNED
);
927 *mrepp
= nmrep
.nmc_mhead
;
928 nfsmout_on_status(nd
, error
);
929 if (nd
->nd_vers
== NFS_VER3
)
930 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, vap
);
931 if (error
|| nd
->nd_repstat
) {
932 nfsm_chain_build_done(error
, &nmrep
);
935 if (nd
->nd_vers
== NFS_VER3
) {
936 nfsm_chain_add_32(error
, &nmrep
, len
);
937 nfsm_chain_add_32(error
, &nmrep
, (len
< reqlen
) ? TRUE
: FALSE
);
939 error
= nfsm_chain_add_fattr(nd
, &nmrep
, vap
);
941 nfsm_chain_add_32(error
, &nmrep
, len
);
942 nfsm_chain_build_done(error
, &nmrep
);
944 error
= mbuf_setnext(nmrep
.nmc_mcur
, mread
);
948 /* update export stats */
949 NFSStatAdd64(&nx
->nx_stats
.bytes_read
, len
);
951 /* update active user stats */
952 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, len
, 0);
958 if (uio_bufp
!= NULL
)
959 FREE(uio_bufp
, M_TEMP
);
961 nfsm_chain_cleanup(&nmrep
);
968 * NFS File modification reporting
970 * When the contents of a file are changed, a "content modified"
971 * fsevent needs to be issued. Normally this would be done at
972 * file close time. This is difficult for NFS because the protocol
973 * has no "close" operation. The client sends a stream of write
974 * requests that just stop. So we keep a hash table full of
975 * vnodes that have been written to recently, and issue a
976 * "content modified" fsevent only if there are no writes to
977 * a vnode for nfsrv_fmod_pendtime milliseconds.
979 int nfsrv_fmod_pending
; /* count of vnodes being written to */
980 int nfsrv_fmod_pendtime
= 1000; /* msec to wait */
981 int nfsrv_fmod_min_interval
= 100; /* msec min interval between callbacks */
984 * This function is called via the kernel's callout
985 * mechanism. Calls are made only when there are
986 * vnodes pending a fsevent creation, and no more
987 * frequently than every nfsrv_fmod_min_interval ms.
990 nfsrv_fmod_timer(__unused
void *param0
, __unused
void *param1
)
992 struct nfsrv_fmod_hashhead
*head
;
993 struct nfsrv_fmod
*fp
, *nfp
;
994 uint64_t timenow
, next_deadline
;
998 lck_mtx_lock(nfsrv_fmod_mutex
);
999 clock_get_uptime(&timenow
);
1000 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000,
1004 * Scan all the hash chains
1006 for (i
= 0; i
< NFSRVFMODHASHSZ
; i
++) {
1008 * For each hash chain, look for an entry
1009 * that has exceeded the deadline.
1011 head
= &nfsrv_fmod_hashtbl
[i
];
1012 LIST_FOREACH(fp
, head
, fm_link
) {
1013 if (timenow
>= fp
->fm_deadline
)
1015 if (fp
->fm_deadline
< next_deadline
)
1016 next_deadline
= fp
->fm_deadline
;
1020 * If we have an entry that's exceeded the
1021 * deadline, then the same is true for all
1022 * following entries in the chain, since they're
1023 * sorted in time order.
1027 * Fire off the content modified fsevent for each
1028 * entry, remove it from the list, and free it.
1031 if (nfsrv_fsevents_enabled
)
1032 add_fsevent(FSE_CONTENT_MODIFIED
, &fp
->fm_context
,
1033 FSE_ARG_VNODE
, fp
->fm_vp
,
1036 vnode_put(fp
->fm_vp
);
1037 kauth_cred_unref(&fp
->fm_context
.vc_ucred
);
1038 nfp
= LIST_NEXT(fp
, fm_link
);
1039 LIST_REMOVE(fp
, fm_link
);
1041 nfsrv_fmod_pending
--;
1047 * If there are still pending entries, set up another
1048 * callout to handle them later. Set the timeout deadline
1049 * so that the callout happens when the oldest pending
1050 * entry is ready to send its fsevent.
1052 if (nfsrv_fmod_pending
> 0) {
1053 interval
= (next_deadline
- timenow
) / (1000 * 1000);
1054 if (interval
< nfsrv_fmod_min_interval
)
1055 interval
= nfsrv_fmod_min_interval
;
1058 nfsrv_fmod_timer_on
= interval
> 0;
1059 if (nfsrv_fmod_timer_on
)
1060 nfs_interval_timer_start(nfsrv_fmod_timer_call
, interval
);
1062 lck_mtx_unlock(nfsrv_fmod_mutex
);
1067 * When a vnode has been written to, enter it in the hash
1068 * table of vnodes pending creation of an fsevent. If the
1069 * callout timer isn't already running, schedule a callback
1070 * for nfsrv_fmod_pendtime msec from now.
1073 nfsrv_modified(vnode_t vp
, vfs_context_t ctx
)
1076 struct nfsrv_fmod
*fp
;
1077 struct nfsrv_fmod_hashhead
*head
;
1079 lck_mtx_lock(nfsrv_fmod_mutex
);
1082 * Compute the time in the future when the
1083 * content modified fsevent is to be issued.
1085 clock_interval_to_deadline(nfsrv_fmod_pendtime
, 1000 * 1000, &deadline
);
1088 * Check if there's already a file content change fsevent
1089 * pending for this vnode. If there is, update its
1090 * timestamp and make sure it's at the front of the hash chain.
1092 head
= &nfsrv_fmod_hashtbl
[NFSRVFMODHASH(vp
)];
1093 LIST_FOREACH(fp
, head
, fm_link
) {
1094 if (vp
== fp
->fm_vp
) {
1095 fp
->fm_deadline
= deadline
;
1096 if (fp
!= LIST_FIRST(head
)) {
1097 LIST_REMOVE(fp
, fm_link
);
1098 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1100 lck_mtx_unlock(nfsrv_fmod_mutex
);
1106 * First content change fsevent for this vnode.
1107 * Allocate a new file mod entry and add it
1108 * on the front of the hash chain.
1110 if (vnode_get(vp
) != 0)
1112 MALLOC(fp
, struct nfsrv_fmod
*, sizeof(*fp
), M_TEMP
, M_WAITOK
);
1118 kauth_cred_ref(vfs_context_ucred(ctx
));
1119 fp
->fm_context
= *ctx
;
1120 fp
->fm_deadline
= deadline
;
1121 LIST_INSERT_HEAD(head
, fp
, fm_link
);
1124 * If added to an empty hash table, then set the
1125 * callout timer to go off after nfsrv_fmod_pendtime.
1127 nfsrv_fmod_pending
++;
1128 if (!nfsrv_fmod_timer_on
) {
1129 nfsrv_fmod_timer_on
= 1;
1130 nfs_interval_timer_start(nfsrv_fmod_timer_call
,
1131 nfsrv_fmod_pendtime
);
1134 lck_mtx_unlock(nfsrv_fmod_mutex
);
1137 #endif /* CONFIG_FSE */
1144 struct nfsrv_descript
*nd
,
1145 struct nfsrv_sock
*slp
,
1149 struct vnode_attr preattr
, postattr
;
1150 int error
, preattrerr
, postattrerr
;
1151 int ioflags
, len
, retlen
;
1153 int stable
= NFS_WRITE_FILESYNC
;
1156 struct nfs_filehandle nfh
;
1157 struct nfs_export
*nx
;
1158 struct nfs_export_options
*nxo
;
1160 char *uio_bufp
= NULL
;
1163 struct nfsm_chain
*nmreq
, nmrep
;
1165 if (nd
->nd_nmreq
.nmc_mhead
== NULL
) {
1171 preattrerr
= postattrerr
= ENOENT
;
1172 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1173 nmreq
= &nd
->nd_nmreq
;
1174 nfsm_chain_null(&nmrep
);
1178 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
1180 if (nd
->nd_vers
== NFS_VER3
) {
1181 nfsm_chain_get_64(error
, nmreq
, off
);
1182 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1183 nfsm_chain_get_32(error
, nmreq
, stable
);
1185 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1186 nfsm_chain_get_32(error
, nmreq
, off
);
1187 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1189 stable
= NFS_WRITE_UNSTABLE
;
1191 nfsm_chain_get_32(error
, nmreq
, len
);
1196 * For NFS Version 2, it is not obvious what a write of zero length
1197 * should do, but I might as well be consistent with Version 3,
1198 * which is to return ok so long as there are no permission problems.
1202 error
= nfsm_chain_trim_data(nmreq
, len
, &mlen
);
1207 if ((len
> NFS_MAXDATA
) || (len
< 0) || (mlen
< len
)) {
1211 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
1214 /* update export stats */
1215 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1217 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1220 if (nd
->nd_vers
== NFS_VER3
) {
1221 nfsm_srv_pre_vattr_init(&preattr
);
1222 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1224 if (vnode_vtype(vp
) != VREG
) {
1225 if (nd
->nd_vers
== NFS_VER3
)
1228 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1231 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1235 for (mcount
=0, m
=nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
))
1236 if (mbuf_len(m
) > 0)
1238 MALLOC(uio_bufp
, char *, UIO_SIZEOF(mcount
), M_TEMP
, M_WAITOK
);
1240 uiop
= uio_createwithbuffer(mcount
, off
, UIO_SYSSPACE
, UIO_WRITE
, uio_bufp
, UIO_SIZEOF(mcount
));
1241 if (!uio_bufp
|| !uiop
)
1244 for (m
= nmreq
->nmc_mcur
; m
; m
= mbuf_next(m
))
1245 if ((mlen
= mbuf_len(m
)) > 0)
1246 uio_addiov(uiop
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), mlen
);
1248 * XXX The IO_METASYNC flag indicates that all metadata (and not just
1249 * enough to ensure data integrity) mus be written to stable storage
1250 * synchronously. (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
1252 if (stable
== NFS_WRITE_UNSTABLE
)
1253 ioflags
= IO_NODELOCKED
;
1254 else if (stable
== NFS_WRITE_DATASYNC
)
1255 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1257 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1259 error
= VNOP_WRITE(vp
, uiop
, ioflags
, ctx
);
1260 OSAddAtomic(1, (SInt32
*)&nfsstats
.srvvop_writes
);
1262 /* update export stats */
1263 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, len
);
1265 /* update active user stats */
1266 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, len
);
1269 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
))
1270 nfsrv_modified(vp
, ctx
);
1273 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1274 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1275 if (!error
&& (nd
->nd_vers
== NFS_VER2
))
1276 error
= postattrerr
; /* NFSv2 must have attributes to return */
1281 /* assemble reply */
1282 nd
->nd_repstat
= error
;
1283 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1284 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1285 NFSX_WRITEVERF(nd
->nd_vers
));
1287 *mrepp
= nmrep
.nmc_mhead
;
1288 nfsmout_on_status(nd
, error
);
1289 if (nd
->nd_vers
== NFS_VER3
) {
1290 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1291 preattrerr
, &preattr
, postattrerr
, &postattr
);
1292 nfsmout_if(error
|| nd
->nd_repstat
);
1293 nfsm_chain_add_32(error
, &nmrep
, retlen
);
1294 /* If nfsrv_async is set, then pretend the write was FILESYNC. */
1295 if ((stable
== NFS_WRITE_UNSTABLE
) && !nfsrv_async
)
1296 nfsm_chain_add_32(error
, &nmrep
, stable
);
1298 nfsm_chain_add_32(error
, &nmrep
, NFS_WRITE_FILESYNC
);
1299 /* write verifier */
1300 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1301 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1303 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1306 nfsm_chain_build_done(error
, &nmrep
);
1309 if (uio_bufp
!= NULL
)
1310 FREE(uio_bufp
, M_TEMP
);
1312 nfsm_chain_cleanup(&nmrep
);
1319 * NFS write service with write gathering support. Called when
1320 * nfsrv_wg_delay > 0.
1321 * See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
1322 * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
1326 #define NWDELAYHASH(sock, f) \
1327 (&(sock)->ns_wdelayhashtbl[(*((u_long *)(f))) % NFS_WDELAYHASHSIZ])
1328 /* These macros compare nfsrv_descript structures. */
1329 #define NFSW_CONTIG(o, n) \
1330 (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh))
1332 * XXX The following is an incorrect comparison; it fails to take into account
1333 * XXX scoping of MAC labels, but we currently lack KPI for credential
1336 #define NFSW_SAMECRED(o, n) \
1337 (!bcmp((caddr_t)(o)->nd_cr, (caddr_t)(n)->nd_cr, \
1338 sizeof (struct ucred)))
1342 struct nfsrv_descript
**ndp
,
1343 struct nfsrv_sock
*slp
,
1347 struct nfsrv_descript
*nd
, *wp
, *owp
, *swp
;
1348 struct nfs_export
*nx
;
1349 struct nfs_export_options
*nxo
;
1350 struct nfsrv_wg_delayhash
*wpp
;
1352 struct vnode_attr preattr
, postattr
;
1353 int error
, mlen
, i
, ioflags
, tlen
;
1354 int preattrerr
, postattrerr
;
1358 char *uio_bufp
= NULL
;
1361 struct nfsm_chain
*nmreq
, nmrep
;
1364 preattrerr
= postattrerr
= ENOENT
;
1365 nfsm_chain_null(&nmrep
);
1372 nmreq
= &nd
->nd_nmreq
;
1373 LIST_INIT(&nd
->nd_coalesce
);
1375 nd
->nd_stable
= NFS_WRITE_FILESYNC
;
1377 cur_usec
= (u_quad_t
)now
.tv_sec
* 1000000 + (u_quad_t
)now
.tv_usec
;
1378 nd
->nd_time
= cur_usec
+
1379 ((nd
->nd_vers
== NFS_VER3
) ? nfsrv_wg_delay_v3
: nfsrv_wg_delay
);
1381 /* Now, get the write header... */
1382 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nd
->nd_fh
.nfh_fhp
, nd
->nd_fh
.nfh_len
);
1383 /* XXX shouldn't we be checking for invalid FHs before doing any more work? */
1385 if (nd
->nd_vers
== NFS_VER3
) {
1386 nfsm_chain_get_64(error
, nmreq
, nd
->nd_off
);
1387 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1388 nfsm_chain_get_32(error
, nmreq
, nd
->nd_stable
);
1390 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1391 nfsm_chain_get_32(error
, nmreq
, nd
->nd_off
);
1392 nfsm_chain_adv(error
, nmreq
, NFSX_UNSIGNED
);
1394 nd
->nd_stable
= NFS_WRITE_UNSTABLE
;
1396 nfsm_chain_get_32(error
, nmreq
, nd
->nd_len
);
1398 nd
->nd_eoff
= nd
->nd_off
+ nd
->nd_len
;
1400 if (nd
->nd_len
> 0) {
1401 error
= nfsm_chain_trim_data(nmreq
, nd
->nd_len
, &mlen
);
1407 if ((nd
->nd_len
> NFS_MAXDATA
) || (nd
->nd_len
< 0) || (mlen
< nd
->nd_len
)) {
1410 nd
->nd_repstat
= error
;
1411 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1413 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1414 if (nd
->nd_vers
== NFS_VER3
)
1415 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1416 preattrerr
, &preattr
, postattrerr
, &postattr
);
1418 nfsm_chain_build_done(error
, &nmrep
);
1423 * Add this entry to the hash and time queues.
1425 lck_mtx_lock(&slp
->ns_wgmutex
);
1427 wp
= slp
->ns_tq
.lh_first
;
1428 while (wp
&& wp
->nd_time
< nd
->nd_time
) {
1430 wp
= wp
->nd_tq
.le_next
;
1433 LIST_INSERT_AFTER(owp
, nd
, nd_tq
);
1435 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1438 wpp
= NWDELAYHASH(slp
, nd
->nd_fh
.nfh_fid
);
1441 while (wp
&& !nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1443 wp
= wp
->nd_hash
.le_next
;
1445 while (wp
&& (wp
->nd_off
< nd
->nd_off
) &&
1446 nfsrv_fhmatch(&nd
->nd_fh
, &wp
->nd_fh
)) {
1448 wp
= wp
->nd_hash
.le_next
;
1451 LIST_INSERT_AFTER(owp
, nd
, nd_hash
);
1453 * Search the hash list for overlapping entries and
1456 for(; nd
&& NFSW_CONTIG(owp
, nd
); nd
= wp
) {
1457 wp
= nd
->nd_hash
.le_next
;
1458 if (NFSW_SAMECRED(owp
, nd
))
1459 nfsrv_wg_coalesce(owp
, nd
);
1462 LIST_INSERT_HEAD(wpp
, nd
, nd_hash
);
1466 lck_mtx_lock(&slp
->ns_wgmutex
);
1470 * Now, do VNOP_WRITE()s for any one(s) that need to be done now
1471 * and generate the associated reply mbuf list(s).
1475 cur_usec
= (u_quad_t
)now
.tv_sec
* 1000000 + (u_quad_t
)now
.tv_usec
;
1476 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= owp
) {
1477 owp
= nd
->nd_tq
.le_next
;
1478 if (nd
->nd_time
> cur_usec
)
1482 LIST_REMOVE(nd
, nd_tq
);
1483 LIST_REMOVE(nd
, nd_hash
);
1484 nmreq
= &nd
->nd_nmreq
;
1485 preattrerr
= postattrerr
= ENOENT
;
1487 /* save the incoming uid before mapping, */
1488 /* for updating active user stats later */
1489 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1491 error
= nfsrv_fhtovp(&nd
->nd_fh
, nd
, &vp
, &nx
, &nxo
);
1493 /* update per-export stats */
1494 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1496 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
1501 if (nd
->nd_vers
== NFS_VER3
) {
1502 nfsm_srv_pre_vattr_init(&preattr
);
1503 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
1505 if (vnode_vtype(vp
) != VREG
) {
1506 if (nd
->nd_vers
== NFS_VER3
)
1509 error
= (vnode_vtype(vp
) == VDIR
) ? EISDIR
: EACCES
;
1514 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
, ctx
, nxo
, 1);
1516 if (nd
->nd_stable
== NFS_WRITE_UNSTABLE
)
1517 ioflags
= IO_NODELOCKED
;
1518 else if (nd
->nd_stable
== NFS_WRITE_DATASYNC
)
1519 ioflags
= (IO_SYNC
| IO_NODELOCKED
);
1521 ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
1523 if (!error
&& ((nd
->nd_eoff
- nd
->nd_off
) > 0)) {
1524 for (i
=0, m
=nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
))
1525 if (mbuf_len(m
) > 0)
1528 MALLOC(uio_bufp
, char *, UIO_SIZEOF(i
), M_TEMP
, M_WAITOK
);
1530 uiop
= uio_createwithbuffer(i
, nd
->nd_off
, UIO_SYSSPACE
,
1531 UIO_WRITE
, uio_bufp
, UIO_SIZEOF(i
));
1532 if (!uio_bufp
|| !uiop
)
1535 for (m
= nmreq
->nmc_mhead
; m
; m
= mbuf_next(m
))
1536 if ((tlen
= mbuf_len(m
)) > 0)
1537 uio_addiov(uiop
, CAST_USER_ADDR_T((caddr_t
)mbuf_data(m
)), tlen
);
1538 error
= VNOP_WRITE(vp
, uiop
, ioflags
, ctx
);
1539 OSAddAtomic(1, (SInt32
*)&nfsstats
.srvvop_writes
);
1541 /* update export stats */
1542 NFSStatAdd64(&nx
->nx_stats
.bytes_written
, nd
->nd_len
);
1543 /* update active user stats */
1544 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, nd
->nd_len
);
1547 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CONTENT_MODIFIED
, vp
))
1548 nfsrv_modified(vp
, ctx
);
1552 FREE(uio_bufp
, M_TEMP
);
1557 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
1558 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
1563 * Loop around generating replies for all write rpcs that have
1564 * now been completed.
1569 nd
->nd_repstat
= error
;
1570 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
1571 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1572 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1573 preattrerr
, &preattr
, postattrerr
, &postattr
);
1576 nd
->nd_repstat
= error
;
1577 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_PREOPATTR(nd
->nd_vers
) +
1578 NFSX_POSTOPORFATTR(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
+
1579 NFSX_WRITEVERF(nd
->nd_vers
));
1580 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
1581 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
1582 preattrerr
, &preattr
, postattrerr
, &postattr
);
1583 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_len
);
1584 nfsm_chain_add_32(error
, &nmrep
, nd
->nd_stable
);
1585 /* write verifier */
1586 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
1587 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
1588 } else if (!error
) {
1589 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
1592 nfsm_chain_build_done(error
, &nmrep
);
1594 nd
->nd_mrep
= nmrep
.nmc_mhead
;
1597 * Done. Put it at the head of the timer queue so that
1598 * the final phase can return the reply.
1602 LIST_INSERT_HEAD(&slp
->ns_tq
, nd
, nd_tq
);
1604 nd
= swp
->nd_coalesce
.lh_first
;
1606 LIST_REMOVE(nd
, nd_tq
);
1610 LIST_INSERT_HEAD(&slp
->ns_tq
, swp
, nd_tq
);
1615 * Search for a reply to return.
1617 for (nd
= slp
->ns_tq
.lh_first
; nd
; nd
= nd
->nd_tq
.le_next
)
1619 LIST_REMOVE(nd
, nd_tq
);
1620 *mrepp
= nd
->nd_mrep
;
1624 slp
->ns_wgtime
= slp
->ns_tq
.lh_first
? slp
->ns_tq
.lh_first
->nd_time
: 0;
1625 lck_mtx_unlock(&slp
->ns_wgmutex
);
1628 * If we've just created a write pending gather,
1629 * start the timer to check on it soon to make sure
1630 * the write will be completed.
1632 * Add/Remove the socket in the nfsrv_sockwg queue as needed.
1634 lck_mtx_lock(nfsd_mutex
);
1635 if (slp
->ns_wgtime
) {
1636 if (slp
->ns_wgq
.tqe_next
== SLPNOLIST
) {
1637 TAILQ_INSERT_HEAD(&nfsrv_sockwg
, slp
, ns_wgq
);
1639 if (!nfsrv_wg_timer_on
) {
1640 nfsrv_wg_timer_on
= 1;
1641 nfs_interval_timer_start(nfsrv_wg_timer_call
,
1642 NFSRV_WGATHERDELAY
);
1644 } else if (slp
->ns_wgq
.tqe_next
!= SLPNOLIST
) {
1645 TAILQ_REMOVE(&nfsrv_sockwg
, slp
, ns_wgq
);
1646 slp
->ns_wgq
.tqe_next
= SLPNOLIST
;
1648 lck_mtx_unlock(nfsd_mutex
);
1654 * Coalesce the write request nd into owp. To do this we must:
1655 * - remove nd from the queues
1656 * - merge nd->nd_nmreq into owp->nd_nmreq
1657 * - update the nd_eoff and nd_stable for owp
1658 * - put nd on owp's nd_coalesce list
1661 nfsrv_wg_coalesce(struct nfsrv_descript
*owp
, struct nfsrv_descript
*nd
)
1665 struct nfsrv_descript
*p
;
1667 LIST_REMOVE(nd
, nd_hash
);
1668 LIST_REMOVE(nd
, nd_tq
);
1669 if (owp
->nd_eoff
< nd
->nd_eoff
) {
1670 overlap
= owp
->nd_eoff
- nd
->nd_off
;
1674 mbuf_adj(nd
->nd_nmreq
.nmc_mhead
, overlap
);
1675 mp
= owp
->nd_nmreq
.nmc_mhead
;
1676 while ((mpnext
= mbuf_next(mp
)))
1678 error
= mbuf_setnext(mp
, nd
->nd_nmreq
.nmc_mhead
);
1681 owp
->nd_eoff
= nd
->nd_eoff
;
1683 mbuf_freem(nd
->nd_nmreq
.nmc_mhead
);
1685 nd
->nd_nmreq
.nmc_mhead
= NULL
;
1686 nd
->nd_nmreq
.nmc_mcur
= NULL
;
1687 if (nd
->nd_stable
== NFS_WRITE_FILESYNC
)
1688 owp
->nd_stable
= NFS_WRITE_FILESYNC
;
1689 else if ((nd
->nd_stable
== NFS_WRITE_DATASYNC
) &&
1690 (owp
->nd_stable
== NFS_WRITE_UNSTABLE
))
1691 owp
->nd_stable
= NFS_WRITE_DATASYNC
;
1692 LIST_INSERT_HEAD(&owp
->nd_coalesce
, nd
, nd_tq
);
1695 * If nd had anything else coalesced into it, transfer them
1696 * to owp, otherwise their replies will never get sent.
1698 while ((p
= nd
->nd_coalesce
.lh_first
)) {
1699 LIST_REMOVE(p
, nd_tq
);
1700 LIST_INSERT_HEAD(&owp
->nd_coalesce
, p
, nd_tq
);
1706 * Scan the write gathering queues for writes that need to be
1710 nfsrv_wg_timer(__unused
void *param0
, __unused
void *param1
)
1713 uint64_t cur_usec
, next_usec
;
1715 struct nfsrv_sock
*slp
;
1716 int writes_pending
= 0;
1719 cur_usec
= (uint64_t)now
.tv_sec
* 1000000 + (uint64_t)now
.tv_usec
;
1720 next_usec
= cur_usec
+ (NFSRV_WGATHERDELAY
* 1000);
1722 lck_mtx_lock(nfsd_mutex
);
1723 TAILQ_FOREACH(slp
, &nfsrv_sockwg
, ns_wgq
) {
1724 if (slp
->ns_wgtime
) {
1726 if (slp
->ns_wgtime
<= cur_usec
) {
1727 lck_rw_lock_exclusive(&slp
->ns_rwlock
);
1728 slp
->ns_flag
|= SLP_DOWRITES
;
1729 lck_rw_done(&slp
->ns_rwlock
);
1730 nfsrv_wakenfsd(slp
);
1733 if (slp
->ns_wgtime
< next_usec
)
1734 next_usec
= slp
->ns_wgtime
;
1738 if (writes_pending
== 0) {
1739 nfsrv_wg_timer_on
= 0;
1740 lck_mtx_unlock(nfsd_mutex
);
1743 lck_mtx_unlock(nfsd_mutex
);
1746 * Return the number of msec to wait again
1748 interval
= (next_usec
- cur_usec
) / 1000;
1751 nfs_interval_timer_start(nfsrv_wg_timer_call
, interval
);
1755 * Sort the group list in increasing numerical order.
1756 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1757 * that used to be here.)
1760 nfsrv_group_sort(gid_t
*list
, int num
)
1765 /* Insertion sort. */
1766 for (i
= 1; i
< num
; i
++) {
1768 /* find correct slot for value v, moving others up */
1769 for (j
= i
; --j
>= 0 && v
< list
[j
];)
1770 list
[j
+ 1] = list
[j
];
1776 * nfs create service
1777 * now does a truncate to 0 length via. setattr if it already exists
1781 struct nfsrv_descript
*nd
,
1782 struct nfsrv_sock
*slp
,
1786 struct vnode_attr dpreattr
, dpostattr
, postattr
;
1787 struct vnode_attr va
, *vap
= &va
;
1788 struct nameidata ni
;
1789 int error
, rdev
, dpreattrerr
, dpostattrerr
, postattrerr
;
1790 int how
, exclusive_flag
;
1792 vnode_t vp
, dvp
, dirp
;
1793 struct nfs_filehandle nfh
;
1794 struct nfs_export
*nx
= NULL
;
1795 struct nfs_export_options
*nxo
;
1797 u_char cverf
[NFSX_V3CREATEVERF
];
1799 struct nfsm_chain
*nmreq
, nmrep
;
1802 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
1803 nmreq
= &nd
->nd_nmreq
;
1804 nfsm_chain_null(&nmrep
);
1805 vp
= dvp
= dirp
= NULL
;
1807 ni
.ni_cnd
.cn_nameiop
= 0;
1811 * Save the original credential UID in case they are
1812 * mapped and we need to map the IDs in the attributes.
1814 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
1816 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
1817 nfsm_chain_get_32(error
, nmreq
, len
);
1818 nfsm_name_len_check(error
, nd
, len
);
1821 ni
.ni_cnd
.cn_nameiop
= CREATE
;
1822 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
1823 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
1825 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
1827 /* update export stats */
1828 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
1830 /* update active user stats */
1831 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
1835 if (nd
->nd_vers
== NFS_VER3
) {
1836 nfsm_srv_pre_vattr_init(&dpreattr
);
1837 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
1845 ni
.ni_cnd
.cn_nameiop
= 0;
1853 if (nd
->nd_vers
== NFS_VER3
) {
1854 nfsm_chain_get_32(error
, nmreq
, how
);
1857 case NFS_CREATE_GUARDED
:
1862 case NFS_CREATE_UNCHECKED
:
1863 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
1865 case NFS_CREATE_EXCLUSIVE
:
1866 nfsm_chain_get_opaque(error
, nmreq
, NFSX_V3CREATEVERF
, cverf
);
1869 VATTR_SET(vap
, va_mode
, 0);
1872 VATTR_SET(vap
, va_type
, VREG
);
1876 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
1878 v_type
= vap
->va_type
;
1881 VATTR_SET(vap
, va_type
, v_type
);
1887 rdev
= vap
->va_data_size
;
1888 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
1897 * If it doesn't exist, create it
1898 * otherwise just truncate to 0 length
1899 * should I set the mode too ??
1902 kauth_acl_t xacl
= NULL
;
1905 * If the credentials were mapped, we should
1906 * map the same values in the attributes.
1908 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
1910 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
1911 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
)
1912 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
1915 /* authorize before creating */
1916 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
1918 /* construct ACL and handle inheritance */
1920 error
= kauth_acl_inherit(dvp
,
1926 if (!error
&& xacl
!= NULL
)
1927 VATTR_SET(vap
, va_acl
, xacl
);
1929 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
1930 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
1932 /* validate new-file security information */
1934 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
1935 if (error
&& (VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
))) {
1937 * Most NFS servers just ignore the UID/GID attributes, so we
1938 * try ignoring them if that'll help the request succeed.
1940 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
1941 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
1942 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
1946 if (vap
->va_type
== VREG
|| vap
->va_type
== VSOCK
) {
1949 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
1951 if (!error
&& !VATTR_ALL_SUPPORTED(vap
))
1953 * If some of the requested attributes weren't handled by the VNOP,
1954 * use our fallback code.
1956 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
1959 kauth_acl_free(xacl
);
1962 if (exclusive_flag
) {
1965 bcopy(cverf
, (caddr_t
)&vap
->va_access_time
,
1967 VATTR_SET_ACTIVE(vap
, va_access_time
);
1968 // skip authorization, as this is an
1969 // NFS internal implementation detail.
1970 error
= vnode_setattr(vp
, vap
, ctx
);
1974 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_CREATE_FILE
, vp
)) {
1975 add_fsevent(FSE_CREATE_FILE
, ctx
,
1982 } else if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
||
1983 vap
->va_type
== VFIFO
) {
1984 if (vap
->va_type
== VCHR
&& rdev
== (int)0xffffffff)
1985 VATTR_SET(vap
, va_type
, VFIFO
);
1986 if (vap
->va_type
!= VFIFO
) {
1987 error
= suser(nd
->nd_cr
, NULL
);
1990 VATTR_SET(vap
, va_rdev
, (dev_t
)rdev
);
1992 error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
1995 kauth_acl_free(xacl
);
2004 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2005 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2006 ni
.ni_cnd
.cn_context
= ctx
;
2007 ni
.ni_startdir
= dvp
;
2009 error
= lookup(&ni
);
2011 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
)
2020 * nameidone has to happen before we vnode_put(dvp)
2021 * since it may need to release the fs_nodelock on the dvp
2024 ni
.ni_cnd
.cn_nameiop
= 0;
2029 * nameidone has to happen before we vnode_put(dvp)
2030 * since it may need to release the fs_nodelock on the dvp
2033 ni
.ni_cnd
.cn_nameiop
= 0;
2037 if (!error
&& VATTR_IS_ACTIVE(vap
, va_data_size
)) {
2038 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_DATA
,
2041 tempsize
= vap
->va_data_size
;
2043 VATTR_SET(vap
, va_data_size
, tempsize
);
2044 error
= vnode_setattr(vp
, vap
, ctx
);
2049 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
2051 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
2052 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2053 if (nd
->nd_vers
== NFS_VER2
)
2054 error
= postattrerr
;
2060 if (nd
->nd_vers
== NFS_VER3
) {
2061 if (exclusive_flag
&& !error
&&
2062 bcmp(cverf
, &postattr
.va_access_time
, NFSX_V3CREATEVERF
))
2064 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2065 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2071 /* assemble reply */
2072 nd
->nd_repstat
= error
;
2073 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
2074 NFSX_FATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
2076 *mrepp
= nmrep
.nmc_mhead
;
2077 nfsmout_on_status(nd
, error
);
2078 if (nd
->nd_vers
== NFS_VER3
) {
2079 if (!nd
->nd_repstat
) {
2080 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2081 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2083 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2084 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2086 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2088 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
2091 nfsm_chain_build_done(error
, &nmrep
);
2092 if (ni
.ni_cnd
.cn_nameiop
) {
2094 * nameidone has to happen before we vnode_put(dvp)
2095 * since it may need to release the fs_nodelock on the dvp
2106 nfsm_chain_cleanup(&nmrep
);
2113 * nfs v3 mknod service
2117 struct nfsrv_descript
*nd
,
2118 struct nfsrv_sock
*slp
,
2122 struct vnode_attr dpreattr
, dpostattr
, postattr
;
2123 struct vnode_attr va
, *vap
= &va
;
2124 struct nameidata ni
;
2125 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
2127 u_long major
, minor
;
2129 vnode_t vp
, dvp
, dirp
;
2130 struct nfs_filehandle nfh
;
2131 struct nfs_export
*nx
= NULL
;
2132 struct nfs_export_options
*nxo
;
2134 kauth_acl_t xacl
= NULL
;
2135 struct nfsm_chain
*nmreq
, nmrep
;
2138 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
2139 nmreq
= &nd
->nd_nmreq
;
2140 nfsm_chain_null(&nmrep
);
2141 vp
= dvp
= dirp
= NULL
;
2142 ni
.ni_cnd
.cn_nameiop
= 0;
2145 * Save the original credential UID in case they are
2146 * mapped and we need to map the IDs in the attributes.
2148 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2150 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2151 nfsm_chain_get_32(error
, nmreq
, len
);
2152 nfsm_name_len_check(error
, nd
, len
);
2155 ni
.ni_cnd
.cn_nameiop
= CREATE
;
2156 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2157 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2159 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2161 /* update export stats */
2162 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2164 /* update active user stats */
2165 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2169 nfsm_srv_pre_vattr_init(&dpreattr
);
2170 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2173 ni
.ni_cnd
.cn_nameiop
= 0;
2180 nfsm_chain_get_32(error
, nmreq
, vtyp
);
2182 vtyp
= nfstov_type(vtyp
, NFS_VER3
);
2183 if (!error
&& (vtyp
!= VCHR
) && (vtyp
!= VBLK
) && (vtyp
!= VSOCK
) && (vtyp
!= VFIFO
)) {
2184 error
= NFSERR_BADTYPE
;
2189 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
2190 if ((vtyp
== VCHR
) || (vtyp
== VBLK
)) {
2191 nfsm_chain_get_32(error
, nmreq
, major
);
2192 nfsm_chain_get_32(error
, nmreq
, minor
);
2194 VATTR_SET(vap
, va_rdev
, makedev(major
, minor
));
2199 * If it doesn't exist, create it.
2205 VATTR_SET(vap
, va_type
, vtyp
);
2208 * If the credentials were mapped, we should
2209 * map the same values in the attributes.
2211 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
2213 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
2214 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
)
2215 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
2218 /* authorize before creating */
2219 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
2221 /* construct ACL and handle inheritance */
2223 error
= kauth_acl_inherit(dvp
,
2229 if (!error
&& xacl
!= NULL
)
2230 VATTR_SET(vap
, va_acl
, xacl
);
2232 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
2233 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
2235 /* validate new-file security information */
2237 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2238 if (error
&& (VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
))) {
2240 * Most NFS servers just ignore the UID/GID attributes, so we
2241 * try ignoring them if that'll help the request succeed.
2243 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
2244 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
2245 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
2251 if (vtyp
== VSOCK
) {
2252 error
= VNOP_CREATE(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
2254 if (!error
&& !VATTR_ALL_SUPPORTED(vap
))
2256 * If some of the requested attributes weren't handled by the VNOP,
2257 * use our fallback code.
2259 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
2261 if (vtyp
!= VFIFO
&& (error
= suser(nd
->nd_cr
, (u_short
*)0)))
2263 if ((error
= VNOP_MKNOD(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
)))
2270 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
2271 ni
.ni_cnd
.cn_flags
&= ~LOCKPARENT
;
2272 ni
.ni_cnd
.cn_context
= vfs_context_current();
2273 ni
.ni_startdir
= dvp
;
2275 error
= lookup(&ni
);
2278 if (ni
.ni_cnd
.cn_flags
& ISSYMLINK
)
2284 kauth_acl_free(xacl
);
2287 * nameidone has to happen before we vnode_put(dvp)
2288 * since it may need to release the fs_nodelock on the dvp
2291 ni
.ni_cnd
.cn_nameiop
= 0;
2297 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
2299 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
2300 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
2308 nfsm_srv_vattr_init(&dpostattr
, NFS_VER3
);
2309 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2314 /* assemble reply */
2315 nd
->nd_repstat
= error
;
2316 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(NFS_VER3
, &nfh
) +
2317 NFSX_POSTOPATTR(NFS_VER3
) + NFSX_WCCDATA(NFS_VER3
));
2319 *mrepp
= nmrep
.nmc_mhead
;
2320 nfsmout_on_status(nd
, error
);
2321 if (!nd
->nd_repstat
) {
2322 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2323 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
2325 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2326 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2328 nfsm_chain_build_done(error
, &nmrep
);
2329 if (ni
.ni_cnd
.cn_nameiop
) {
2331 * nameidone has to happen before we vnode_put(dvp)
2332 * since it may need to release the fs_nodelock on the dvp
2347 nfsm_chain_cleanup(&nmrep
);
2354 * nfs remove service
2358 struct nfsrv_descript
*nd
,
2359 struct nfsrv_sock
*slp
,
2363 struct nameidata ni
;
2364 int error
, dpreattrerr
, dpostattrerr
;
2367 vnode_t vp
, dvp
, dirp
= NULL
;
2368 struct vnode_attr dpreattr
, dpostattr
;
2369 struct nfs_filehandle nfh
;
2370 struct nfs_export
*nx
= NULL
;
2371 struct nfs_export_options
*nxo
;
2372 struct nfsm_chain
*nmreq
, nmrep
;
2375 dpreattrerr
= dpostattrerr
= ENOENT
;
2376 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2377 dvp
= vp
= dirp
= NULL
;
2378 nmreq
= &nd
->nd_nmreq
;
2379 nfsm_chain_null(&nmrep
);
2381 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
2382 nfsm_chain_get_32(error
, nmreq
, len
);
2383 nfsm_name_len_check(error
, nd
, len
);
2386 ni
.ni_cnd
.cn_nameiop
= DELETE
;
2387 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
2388 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
2390 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
2392 /* update export stats */
2393 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
2395 /* update active user stats */
2396 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
2400 if (nd
->nd_vers
== NFS_VER3
) {
2401 nfsm_srv_pre_vattr_init(&dpreattr
);
2402 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
2413 if (vnode_vtype(vp
) == VDIR
)
2414 error
= EPERM
; /* POSIX */
2415 else if (vnode_isvroot(vp
))
2417 * The root of a mounted filesystem cannot be deleted.
2421 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
2429 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
2431 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
2432 get_fse_info(vp
, &finfo
, ctx
);
2434 release_pathbuff(path
);
2439 error
= VNOP_REMOVE(dvp
, vp
, &ni
.ni_cnd
, 0, ctx
);
2444 add_fsevent(FSE_DELETE
, ctx
,
2445 FSE_ARG_STRING
, plen
, path
,
2446 FSE_ARG_FINFO
, &finfo
,
2448 release_pathbuff(path
);
2454 * nameidone has to happen before we vnode_put(dvp)
2455 * since it may need to release the fs_nodelock on the dvp
2465 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
2466 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
2470 /* assemble reply */
2471 nd
->nd_repstat
= error
;
2472 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
2474 *mrepp
= nmrep
.nmc_mhead
;
2475 nfsmout_on_status(nd
, error
);
2476 if (nd
->nd_vers
== NFS_VER3
)
2477 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
2478 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
2480 nfsm_chain_build_done(error
, &nmrep
);
2482 nfsm_chain_cleanup(&nmrep
);
2489 * nfs rename service
2493 struct nfsrv_descript
*nd
,
2494 struct nfsrv_sock
*slp
,
2498 kauth_cred_t saved_cred
= NULL
;
2501 uint32_t fromlen
, tolen
;
2502 int fdpreattrerr
, fdpostattrerr
;
2503 int tdpreattrerr
, tdpostattrerr
;
2504 char *frompath
= NULL
, *topath
= NULL
;
2505 struct nameidata fromni
, toni
;
2506 vnode_t fvp
, tvp
, tdvp
, fdvp
, fdirp
, tdirp
;
2507 struct vnode_attr fdpreattr
, fdpostattr
;
2508 struct vnode_attr tdpreattr
, tdpostattr
;
2509 struct nfs_filehandle fnfh
, tnfh
;
2510 struct nfs_export
*fnx
, *tnx
;
2511 struct nfs_export_options
*fnxo
, *tnxo
;
2512 enum vtype fvtype
, tvtype
;
2513 int holding_mntlock
;
2515 struct nfsm_chain
*nmreq
, nmrep
;
2516 char *from_name
, *to_name
;
2518 int from_len
, to_len
;
2519 fse_info from_finfo
, to_finfo
;
2521 u_char didstats
= 0;
2525 fdpreattrerr
= fdpostattrerr
= ENOENT
;
2526 tdpreattrerr
= tdpostattrerr
= ENOENT
;
2527 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
2528 frompath
= topath
= NULL
;
2529 fdirp
= tdirp
= NULL
;
2530 nmreq
= &nd
->nd_nmreq
;
2531 nfsm_chain_null(&nmrep
);
2534 * these need to be set before calling any code
2535 * that they may take us out through the error path.
2537 holding_mntlock
= 0;
2542 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, fnfh
.nfh_fhp
, fnfh
.nfh_len
);
2543 nfsm_chain_get_32(error
, nmreq
, fromlen
);
2544 nfsm_name_len_check(error
, nd
, fromlen
);
2546 error
= nfsm_chain_get_path_namei(nmreq
, fromlen
, &fromni
);
2548 frompath
= fromni
.ni_cnd
.cn_pnbuf
;
2550 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, tnfh
.nfh_fhp
, tnfh
.nfh_len
);
2551 nfsm_chain_get_32(error
, nmreq
, tolen
);
2552 nfsm_name_len_check(error
, nd
, tolen
);
2554 error
= nfsm_chain_get_path_namei(nmreq
, tolen
, &toni
);
2556 topath
= toni
.ni_cnd
.cn_pnbuf
;
2559 * Remember our original uid so that we can reset cr_uid before
2560 * the second nfsrv_namei() call, in case it is remapped.
2562 saved_cred
= nd
->nd_cr
;
2563 kauth_cred_ref(saved_cred
);
2565 fromni
.ni_cnd
.cn_nameiop
= DELETE
;
2566 fromni
.ni_cnd
.cn_flags
= WANTPARENT
;
2568 fromni
.ni_cnd
.cn_pnbuf
= frompath
;
2570 fromni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2571 fromni
.ni_cnd
.cn_flags
|= HASBUF
;
2573 error
= nfsrv_namei(nd
, ctx
, &fromni
, &fnfh
, &fdirp
, &fnx
, &fnxo
);
2576 fdvp
= fromni
.ni_dvp
;
2580 if (nd
->nd_vers
== NFS_VER3
) {
2581 nfsm_srv_pre_vattr_init(&fdpreattr
);
2582 fdpreattrerr
= vnode_getattr(fdirp
, &fdpreattr
, ctx
);
2588 fvtype
= vnode_vtype(fvp
);
2590 /* reset credential if it was remapped */
2591 if (nd
->nd_cr
!= saved_cred
) {
2592 kauth_cred_ref(saved_cred
);
2593 kauth_cred_unref(&nd
->nd_cr
);
2594 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
2597 toni
.ni_cnd
.cn_nameiop
= RENAME
;
2598 toni
.ni_cnd
.cn_flags
= WANTPARENT
;
2600 toni
.ni_cnd
.cn_pnbuf
= topath
;
2602 toni
.ni_cnd
.cn_pnlen
= MAXPATHLEN
;
2603 toni
.ni_cnd
.cn_flags
|= HASBUF
;
2606 toni
.ni_cnd
.cn_flags
|= WILLBEDIR
;
2609 error
= nfsrv_namei(nd
, ctx
, &toni
, &tnfh
, &tdirp
, &tnx
, &tnxo
);
2612 * Translate error code for rename("dir1", "dir2/.").
2614 if (error
== EISDIR
&& fvtype
== VDIR
) {
2615 if (nd
->nd_vers
== NFS_VER3
)
2626 /* update export stats once only */
2628 /* update export stats */
2629 NFSStatAdd64(&tnx
->nx_stats
.ops
, 1);
2631 /* update active user stats */
2632 nfsrv_update_user_stat(tnx
, nd
, saved_uid
, 1, 0, 0);
2638 if (nd
->nd_vers
== NFS_VER3
) {
2639 nfsm_srv_pre_vattr_init(&tdpreattr
);
2640 tdpreattrerr
= vnode_getattr(tdirp
, &tdpreattr
, ctx
);
2648 tvtype
= vnode_vtype(tvp
);
2650 if (fvtype
== VDIR
&& tvtype
!= VDIR
) {
2651 if (nd
->nd_vers
== NFS_VER3
)
2656 } else if (fvtype
!= VDIR
&& tvtype
== VDIR
) {
2657 if (nd
->nd_vers
== NFS_VER3
)
2663 if (tvtype
== VDIR
&& vnode_mountedhere(tvp
)) {
2664 if (nd
->nd_vers
== NFS_VER3
)
2672 if (nd
->nd_vers
== NFS_VER3
)
2682 * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp,
2683 * the node is moving between directories and we need rights to remove from the
2684 * old and add to the new.
2686 * If tvp already exists and is not a directory, we need to be allowed to delete it.
2688 * Note that we do not inherit when renaming. XXX this needs to be revisited to
2689 * implement the deferred-inherit bit.
2695 if ((tvp
!= NULL
) && vnode_isdir(tvp
)) {
2698 } else if (tdvp
!= fdvp
) {
2702 /* moving out of fdvp, must have delete rights */
2703 if ((error
= nfsrv_authorize(fvp
, fdvp
, KAUTH_VNODE_DELETE
, ctx
, fnxo
, 0)) != 0)
2705 /* moving into tdvp or tvp, must have rights to add */
2706 if ((error
= nfsrv_authorize(((tvp
!= NULL
) && vnode_isdir(tvp
)) ? tvp
: tdvp
,
2708 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
2709 ctx
, tnxo
, 0)) != 0)
2712 /* node staying in same directory, must be allowed to add new name */
2713 if ((error
= nfsrv_authorize(fdvp
, NULL
,
2714 vnode_isdir(fvp
) ? KAUTH_VNODE_ADD_SUBDIRECTORY
: KAUTH_VNODE_ADD_FILE
,
2715 ctx
, fnxo
, 0)) != 0)
2718 /* overwriting tvp */
2719 if ((tvp
!= NULL
) && !vnode_isdir(tvp
) &&
2720 ((error
= nfsrv_authorize(tvp
, tdvp
, KAUTH_VNODE_DELETE
, ctx
, tnxo
, 0)) != 0))
2723 /* XXX more checks? */
2726 /* authorization denied */
2731 if ((vnode_mount(fvp
) != vnode_mount(tdvp
)) ||
2732 (tvp
&& (vnode_mount(fvp
) != vnode_mount(tvp
)))) {
2733 if (nd
->nd_vers
== NFS_VER3
)
2740 * The following edge case is caught here:
2741 * (to cannot be a descendent of from)
2754 if (tdvp
->v_parent
== fvp
) {
2755 if (nd
->nd_vers
== NFS_VER3
)
2761 if (fvtype
== VDIR
&& vnode_mountedhere(fvp
)) {
2762 if (nd
->nd_vers
== NFS_VER3
)
2769 * If source is the same as the destination (that is the
2770 * same vnode) then there is nothing to do...
2771 * EXCEPT if the underlying file system supports case
2772 * insensitivity and is case preserving. In this case
2773 * the file system needs to handle the special case of
2774 * getting the same vnode as target (fvp) and source (tvp).
2776 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
2777 * and _PC_CASE_PRESERVING can have this exception, and they need to
2778 * handle the special case of getting the same vnode as target and
2779 * source. NOTE: Then the target is unlocked going into vnop_rename,
2780 * so not to cause locking problems. There is a single reference on tvp.
2782 * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE
2783 * that correct behaviour then is just to remove the source (link)
2785 if ((fvp
== tvp
) && (fdvp
== tdvp
)) {
2786 if (fromni
.ni_cnd
.cn_namelen
== toni
.ni_cnd
.cn_namelen
&&
2787 !bcmp(fromni
.ni_cnd
.cn_nameptr
, toni
.ni_cnd
.cn_nameptr
,
2788 fromni
.ni_cnd
.cn_namelen
)) {
2793 if (holding_mntlock
&& vnode_mount(fvp
) != locked_mp
) {
2795 * we're holding a reference and lock
2796 * on locked_mp, but it no longer matches
2797 * what we want to do... so drop our hold
2799 mount_unlock_renames(locked_mp
);
2800 mount_drop(locked_mp
, 0);
2801 holding_mntlock
= 0;
2803 if (tdvp
!= fdvp
&& fvtype
== VDIR
) {
2805 * serialize renames that re-shape
2806 * the tree... if holding_mntlock is
2807 * set, then we're ready to go...
2809 * first need to drop the iocounts
2810 * we picked up, second take the
2811 * lock to serialize the access,
2812 * then finally start the lookup
2813 * process over with the lock held
2815 if (!holding_mntlock
) {
2817 * need to grab a reference on
2818 * the mount point before we
2819 * drop all the iocounts... once
2820 * the iocounts are gone, the mount
2823 locked_mp
= vnode_mount(fvp
);
2824 mount_ref(locked_mp
, 0);
2826 /* make a copy of to path to pass to nfsrv_namei() again */
2827 MALLOC_ZONE(topath
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2829 bcopy(toni
.ni_cnd
.cn_pnbuf
, topath
, tolen
+ 1);
2832 * nameidone has to happen before we vnode_put(tdvp)
2833 * since it may need to release the fs_nodelock on the tdvp
2841 /* make a copy of from path to pass to nfsrv_namei() again */
2842 MALLOC_ZONE(frompath
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
2844 bcopy(fromni
.ni_cnd
.cn_pnbuf
, frompath
, fromlen
+ 1);
2847 * nameidone has to happen before we vnode_put(fdvp)
2848 * since it may need to release the fs_nodelock on the fdvp
2863 mount_lock_renames(locked_mp
);
2864 holding_mntlock
= 1;
2869 fdpreattrerr
= tdpreattrerr
= ENOENT
;
2871 if (!topath
|| !frompath
) {
2872 /* we couldn't allocate a path, so bail */
2877 /* reset credential if it was remapped */
2878 if (nd
->nd_cr
!= saved_cred
) {
2879 kauth_cred_ref(saved_cred
);
2880 kauth_cred_unref(&nd
->nd_cr
);
2881 ctx
->vc_ucred
= nd
->nd_cr
= saved_cred
;
2888 * when we dropped the iocounts to take
2889 * the lock, we allowed the identity of
2890 * the various vnodes to change... if they did,
2891 * we may no longer be dealing with a rename
2892 * that reshapes the tree... once we're holding
2893 * the iocounts, the vnodes can't change type
2894 * so we're free to drop the lock at this point
2897 if (holding_mntlock
) {
2898 mount_unlock_renames(locked_mp
);
2899 mount_drop(locked_mp
, 0);
2900 holding_mntlock
= 0;
2904 // save these off so we can later verify that fvp is the same
2906 oname
= fvp
->v_name
;
2907 oparent
= fvp
->v_parent
;
2910 * If generating an fsevent, then
2911 * stash any pre-rename info we may need.
2914 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_RENAME
, fvp
)) {
2915 get_fse_info(fvp
, &from_finfo
, ctx
);
2917 get_fse_info(tvp
, &to_finfo
, ctx
);
2919 from_name
= get_pathbuff();
2920 from_len
= MAXPATHLEN
;
2921 if (from_name
&& vn_getpath(fdvp
, from_name
, &from_len
)) {
2922 release_pathbuff(from_name
);
2924 } else if ((from_len
+ 1 + fromni
.ni_cnd
.cn_namelen
+ 1) < MAXPATHLEN
) {
2925 // if the path is not just "/", then append a "/"
2927 from_name
[from_len
-1] = '/';
2931 strlcpy(&from_name
[from_len
], fromni
.ni_cnd
.cn_nameptr
, MAXPATHLEN
-from_len
);
2932 from_len
+= fromni
.ni_cnd
.cn_namelen
+ 1;
2933 from_name
[from_len
] = '\0';
2936 to_name
= from_name
? get_pathbuff() : NULL
;
2937 to_len
= MAXPATHLEN
;
2941 release_pathbuff(from_name
);
2944 } else if (vn_getpath(tdvp
, to_name
, &to_len
)) {
2945 release_pathbuff(from_name
);
2946 release_pathbuff(to_name
);
2947 from_name
= to_name
= NULL
;
2948 } else if ((to_len
+ 1 + toni
.ni_cnd
.cn_namelen
+ 1) < MAXPATHLEN
) {
2949 // if the path is not just "/", then append a "/"
2951 to_name
[to_len
-1] = '/';
2955 strlcpy(&to_name
[to_len
], toni
.ni_cnd
.cn_nameptr
, MAXPATHLEN
-to_len
);
2956 to_len
+= toni
.ni_cnd
.cn_namelen
+ 1;
2957 to_name
[to_len
] = '\0';
2963 #else /* CONFIG_FSE */
2966 #endif /* CONFIG_FSE */
2968 error
= VNOP_RENAME(fromni
.ni_dvp
, fromni
.ni_vp
, &fromni
.ni_cnd
,
2969 toni
.ni_dvp
, toni
.ni_vp
, &toni
.ni_cnd
, ctx
);
2971 * fix up name & parent pointers. note that we first
2972 * check that fvp has the same name/parent pointers it
2973 * had before the rename call... this is a 'weak' check
2976 if (oname
== fvp
->v_name
&& oparent
== fvp
->v_parent
) {
2978 update_flags
= VNODE_UPDATE_NAME
;
2980 update_flags
|= VNODE_UPDATE_PARENT
;
2981 vnode_update_identity(fvp
, tdvp
, toni
.ni_cnd
.cn_nameptr
,
2982 toni
.ni_cnd
.cn_namelen
, toni
.ni_cnd
.cn_hash
, update_flags
);
2986 * If the rename is OK and we've got the paths
2987 * then add an fsevent.
2990 if (nfsrv_fsevents_enabled
&& !error
&& from_name
&& to_name
) {
2992 add_fsevent(FSE_RENAME
, ctx
,
2993 FSE_ARG_STRING
, from_len
, from_name
,
2994 FSE_ARG_FINFO
, &from_finfo
,
2995 FSE_ARG_STRING
, to_len
, to_name
,
2996 FSE_ARG_FINFO
, &to_finfo
,
2999 add_fsevent(FSE_RENAME
, ctx
,
3000 FSE_ARG_STRING
, from_len
, from_name
,
3001 FSE_ARG_FINFO
, &from_finfo
,
3002 FSE_ARG_STRING
, to_len
, to_name
,
3007 release_pathbuff(from_name
);
3009 release_pathbuff(to_name
);
3010 #endif /* CONFIG_FSE */
3011 from_name
= to_name
= NULL
;
3014 if (holding_mntlock
) {
3015 mount_unlock_renames(locked_mp
);
3016 mount_drop(locked_mp
, 0);
3017 holding_mntlock
= 0;
3021 * nameidone has to happen before we vnode_put(tdvp)
3022 * since it may need to release the fs_nodelock on the tdvp
3033 * nameidone has to happen before we vnode_put(fdvp)
3034 * since it may need to release the fs_nodelock on the fdvp
3045 nfsm_srv_vattr_init(&fdpostattr
, nd
->nd_vers
);
3046 fdpostattrerr
= vnode_getattr(fdirp
, &fdpostattr
, ctx
);
3051 nfsm_srv_vattr_init(&tdpostattr
, nd
->nd_vers
);
3052 tdpostattrerr
= vnode_getattr(tdirp
, &tdpostattr
, ctx
);
3058 /* assemble reply */
3059 nd
->nd_repstat
= error
;
3060 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 2 * NFSX_WCCDATA(nd
->nd_vers
));
3062 *mrepp
= nmrep
.nmc_mhead
;
3063 nfsmout_on_status(nd
, error
);
3064 if (nd
->nd_vers
== NFS_VER3
) {
3065 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3066 fdpreattrerr
, &fdpreattr
, fdpostattrerr
, &fdpostattr
);
3067 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3068 tdpreattrerr
, &tdpreattr
, tdpostattrerr
, &tdpostattr
);
3071 nfsm_chain_build_done(error
, &nmrep
);
3072 if (holding_mntlock
) {
3073 mount_unlock_renames(locked_mp
);
3074 mount_drop(locked_mp
, 0);
3078 * nameidone has to happen before we vnode_put(tdvp)
3079 * since it may need to release the fs_nodelock on the tdvp
3089 * nameidone has to happen before we vnode_put(fdvp)
3090 * since it may need to release the fs_nodelock on the fdvp
3103 FREE_ZONE(frompath
, MAXPATHLEN
, M_NAMEI
);
3105 FREE_ZONE(topath
, MAXPATHLEN
, M_NAMEI
);
3107 kauth_cred_unref(&saved_cred
);
3109 nfsm_chain_cleanup(&nmrep
);
3120 struct nfsrv_descript
*nd
,
3121 struct nfsrv_sock
*slp
,
3125 struct nameidata ni
;
3126 int error
, dpreattrerr
, dpostattrerr
, attrerr
;
3128 vnode_t vp
, xp
, dvp
, dirp
;
3129 struct vnode_attr dpreattr
, dpostattr
, attr
;
3130 struct nfs_filehandle nfh
, dnfh
;
3131 struct nfs_export
*nx
;
3132 struct nfs_export_options
*nxo
;
3133 struct nfsm_chain
*nmreq
, nmrep
;
3136 dpreattrerr
= dpostattrerr
= attrerr
= ENOENT
;
3137 vp
= xp
= dvp
= dirp
= NULL
;
3138 nmreq
= &nd
->nd_nmreq
;
3139 nfsm_chain_null(&nmrep
);
3141 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3142 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
3143 nfsm_chain_get_32(error
, nmreq
, len
);
3144 nfsm_name_len_check(error
, nd
, len
);
3146 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
3149 /* update export stats */
3150 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3152 /* update active user stats */
3153 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
3155 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
3158 /* we're not allowed to link to directories... */
3159 if (vnode_vtype(vp
) == VDIR
) {
3160 error
= EPERM
; /* POSIX */
3164 /* ...or to anything that kauth doesn't want us to (eg. immutable items) */
3165 if ((error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LINKTARGET
, ctx
, nxo
, 0)) != 0)
3168 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3169 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3170 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3172 error
= nfsrv_namei(nd
, ctx
, &ni
, &dnfh
, &dirp
, &nx
, &nxo
);
3174 if (nd
->nd_vers
== NFS_VER3
) {
3175 nfsm_srv_pre_vattr_init(&dpreattr
);
3176 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3189 else if (vnode_mount(vp
) != vnode_mount(dvp
))
3192 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3195 error
= VNOP_LINK(vp
, dvp
, &ni
.ni_cnd
, ctx
);
3198 if (nfsrv_fsevents_enabled
&& !error
&& need_fsevent(FSE_CREATE_FILE
, dvp
)) {
3199 char *target_path
= NULL
;
3203 /* build the path to the new link file */
3205 if ((target_path
= get_pathbuff()) && !vn_getpath(dvp
, target_path
, &plen
)) {
3206 if ((plen
+ 1 + ni
.ni_cnd
.cn_namelen
+ 1) < MAXPATHLEN
) {
3207 target_path
[plen
-1] = '/';
3208 strlcpy(&target_path
[plen
], ni
.ni_cnd
.cn_nameptr
, MAXPATHLEN
-plen
);
3209 plen
+= ni
.ni_cnd
.cn_namelen
;
3211 if (get_fse_info(vp
, &finfo
, ctx
) == 0)
3212 add_fsevent(FSE_CREATE_FILE
, ctx
,
3213 FSE_ARG_STRING
, plen
, target_path
,
3214 FSE_ARG_FINFO
, &finfo
,
3218 release_pathbuff(target_path
);
3223 * nameidone has to happen before we vnode_put(dvp)
3224 * since it may need to release the fs_nodelock on the dvp
3232 if (nd
->nd_vers
== NFS_VER3
) {
3233 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
3234 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
3237 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3238 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3246 /* assemble reply */
3247 nd
->nd_repstat
= error
;
3248 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3250 *mrepp
= nmrep
.nmc_mhead
;
3251 nfsmout_on_status(nd
, error
);
3252 if (nd
->nd_vers
== NFS_VER3
) {
3253 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
3254 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3255 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3258 nfsm_chain_build_done(error
, &nmrep
);
3262 nfsm_chain_cleanup(&nmrep
);
3269 * nfs symbolic link service
3273 struct nfsrv_descript
*nd
,
3274 struct nfsrv_sock
*slp
,
3278 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3279 struct vnode_attr va
, *vap
= &va
;
3280 struct nameidata ni
;
3281 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3282 uint32_t len
, linkdatalen
;
3285 vnode_t vp
, dvp
, dirp
;
3286 struct nfs_filehandle nfh
;
3287 struct nfs_export
*nx
= NULL
;
3288 struct nfs_export_options
*nxo
;
3290 char uio_buf
[ UIO_SIZEOF(1) ];
3291 struct nfsm_chain
*nmreq
, nmrep
;
3294 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3295 nmreq
= &nd
->nd_nmreq
;
3296 nfsm_chain_null(&nmrep
);
3301 * Save the original credential UID in case they are
3302 * mapped and we need to map the IDs in the attributes.
3304 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3306 ni
.ni_cnd
.cn_nameiop
= 0;
3309 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3310 nfsm_chain_get_32(error
, nmreq
, len
);
3311 nfsm_name_len_check(error
, nd
, len
);
3314 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3315 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3316 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3318 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3320 /* update export stats */
3321 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3323 /* update active user stats */
3324 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3328 if (nd
->nd_vers
== NFS_VER3
) {
3329 nfsm_srv_pre_vattr_init(&dpreattr
);
3330 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3337 ni
.ni_cnd
.cn_nameiop
= 0;
3344 if (nd
->nd_vers
== NFS_VER3
)
3345 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3346 nfsm_chain_get_32(error
, nmreq
, linkdatalen
);
3347 if (!error
&& (((nd
->nd_vers
== NFS_VER2
) && (linkdatalen
> NFS_MAXPATHLEN
)) ||
3348 ((nd
->nd_vers
== NFS_VER3
) && (linkdatalen
> MAXPATHLEN
))))
3349 error
= NFSERR_NAMETOL
;
3351 MALLOC(linkdata
, caddr_t
, linkdatalen
+ 1, M_TEMP
, M_WAITOK
);
3353 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
3354 &uio_buf
[0], sizeof(uio_buf
));
3355 if (!linkdata
|| !auio
) {
3359 uio_addiov(auio
, CAST_USER_ADDR_T(linkdata
), linkdatalen
);
3360 error
= nfsm_chain_get_uio(nmreq
, linkdatalen
, auio
);
3361 if (!error
&& (nd
->nd_vers
== NFS_VER2
))
3362 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3364 *(linkdata
+ linkdatalen
) = '\0';
3371 * If the credentials were mapped, we should
3372 * map the same values in the attributes.
3374 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
3376 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
3377 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
)
3378 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
3380 VATTR_SET(vap
, va_type
, VLNK
);
3381 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3382 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3384 /* authorize before creating */
3385 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
, nxo
, 0);
3387 /* validate given attributes */
3389 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3390 if (error
&& (VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
))) {
3392 * Most NFS servers just ignore the UID/GID attributes, so we
3393 * try ignoring them if that'll help the request succeed.
3395 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3396 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3397 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3401 error
= VNOP_SYMLINK(dvp
, &vp
, &ni
.ni_cnd
, vap
, linkdata
, ctx
);
3403 if (!error
&& (nd
->nd_vers
== NFS_VER3
)) {
3405 ni
.ni_cnd
.cn_nameiop
= LOOKUP
;
3406 ni
.ni_cnd
.cn_flags
&= ~(LOCKPARENT
| FOLLOW
);
3407 ni
.ni_cnd
.cn_flags
|= (NOFOLLOW
| LOCKLEAF
);
3408 ni
.ni_cnd
.cn_context
= ctx
;
3409 ni
.ni_startdir
= dvp
;
3411 error
= lookup(&ni
);
3416 error
= nfsrv_vptofh(nx
, NFS_VER3
, NULL
, vp
, ctx
, &nfh
);
3418 nfsm_srv_vattr_init(&postattr
, NFS_VER3
);
3419 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
3425 if (nfsrv_fsevents_enabled
&& !error
&& vp
) {
3426 add_fsevent(FSE_CREATE_FILE
, ctx
,
3433 * nameidone has to happen before we vnode_put(dvp)
3434 * since it may need to release the fs_nodelock on the dvp
3437 ni
.ni_cnd
.cn_nameiop
= 0;
3443 FREE(linkdata
, M_TEMP
);
3447 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3448 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3454 /* assemble reply */
3455 nd
->nd_repstat
= error
;
3456 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
3457 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3459 *mrepp
= nmrep
.nmc_mhead
;
3460 nfsmout_on_status(nd
, error
);
3461 if (nd
->nd_vers
== NFS_VER3
) {
3462 if (!nd
->nd_repstat
) {
3463 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3464 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
3466 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3467 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3470 nfsm_chain_build_done(error
, &nmrep
);
3471 if (ni
.ni_cnd
.cn_nameiop
) {
3473 * nameidone has to happen before we vnode_put(dvp)
3474 * since it may need to release the fs_nodelock on the dvp
3485 FREE(linkdata
, M_TEMP
);
3487 nfsm_chain_cleanup(&nmrep
);
3498 struct nfsrv_descript
*nd
,
3499 struct nfsrv_sock
*slp
,
3503 struct vnode_attr dpreattr
, dpostattr
, postattr
;
3504 struct vnode_attr va
, *vap
= &va
;
3505 struct nameidata ni
;
3506 int error
, dpreattrerr
, dpostattrerr
, postattrerr
;
3508 vnode_t vp
, dvp
, dirp
;
3509 struct nfs_filehandle nfh
;
3510 struct nfs_export
*nx
= NULL
;
3511 struct nfs_export_options
*nxo
;
3513 kauth_acl_t xacl
= NULL
;
3514 struct nfsm_chain
*nmreq
, nmrep
;
3517 dpreattrerr
= dpostattrerr
= postattrerr
= ENOENT
;
3518 nmreq
= &nd
->nd_nmreq
;
3519 nfsm_chain_null(&nmrep
);
3522 * Save the original credential UID in case they are
3523 * mapped and we need to map the IDs in the attributes.
3525 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3527 ni
.ni_cnd
.cn_nameiop
= 0;
3528 vp
= dvp
= dirp
= NULL
;
3530 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3531 nfsm_chain_get_32(error
, nmreq
, len
);
3532 nfsm_name_len_check(error
, nd
, len
);
3535 ni
.ni_cnd
.cn_nameiop
= CREATE
;
3536 ni
.ni_cnd
.cn_flags
= LOCKPARENT
;
3537 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3539 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3541 /* update export stats */
3542 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3544 /* update active user stats */
3545 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3549 if (nd
->nd_vers
== NFS_VER3
) {
3550 nfsm_srv_pre_vattr_init(&dpreattr
);
3551 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3558 ni
.ni_cnd
.cn_nameiop
= 0;
3565 error
= nfsm_chain_get_sattr(nd
, nmreq
, vap
);
3567 VATTR_SET(vap
, va_type
, VDIR
);
3571 * nameidone has to happen before we vnode_put(dvp)
3572 * since it may need to release the fs_nodelock on the dvp
3582 * If the credentials were mapped, we should
3583 * map the same values in the attributes.
3585 if ((vap
->va_uid
== saved_uid
) && (kauth_cred_getuid(nd
->nd_cr
) != saved_uid
)) {
3587 VATTR_SET(vap
, va_uid
, kauth_cred_getuid(nd
->nd_cr
));
3588 if (kauth_cred_ismember_gid(nd
->nd_cr
, vap
->va_gid
, &ismember
) || !ismember
)
3589 VATTR_SET(vap
, va_gid
, kauth_cred_getgid(nd
->nd_cr
));
3592 error
= nfsrv_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_SUBDIRECTORY
, ctx
, nxo
, 0);
3594 /* construct ACL and handle inheritance */
3596 error
= kauth_acl_inherit(dvp
,
3602 if (!error
&& xacl
!= NULL
)
3603 VATTR_SET(vap
, va_acl
, xacl
);
3605 VATTR_CLEAR_ACTIVE(vap
, va_data_size
);
3606 VATTR_CLEAR_ACTIVE(vap
, va_access_time
);
3608 /* validate new-file security information */
3610 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3611 if (error
&& (VATTR_IS_ACTIVE(vap
, va_uid
) || VATTR_IS_ACTIVE(vap
, va_gid
))) {
3613 * Most NFS servers just ignore the UID/GID attributes, so we
3614 * try ignoring them if that'll help the request succeed.
3616 VATTR_CLEAR_ACTIVE(vap
, va_uid
);
3617 VATTR_CLEAR_ACTIVE(vap
, va_gid
);
3618 error
= vnode_authattr_new(dvp
, vap
, 0, ctx
);
3623 error
= VNOP_MKDIR(dvp
, &vp
, &ni
.ni_cnd
, vap
, ctx
);
3626 if (nfsrv_fsevents_enabled
&& !error
)
3627 add_fsevent(FSE_CREATE_DIR
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
3630 if (!error
&& !VATTR_ALL_SUPPORTED(vap
))
3632 * If some of the requested attributes weren't handled by the VNOP,
3633 * use our fallback code.
3635 error
= vnode_setattr_fallback(vp
, vap
, ctx
);
3638 kauth_acl_free(xacl
);
3641 error
= nfsrv_vptofh(nx
, nd
->nd_vers
, NULL
, vp
, ctx
, &nfh
);
3643 nfsm_srv_vattr_init(&postattr
, nd
->nd_vers
);
3644 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
3645 if (nd
->nd_vers
== NFS_VER2
)
3646 error
= postattrerr
;
3652 * nameidone has to happen before we vnode_put(dvp)
3653 * since it may need to release the fs_nodelock on the dvp
3658 ni
.ni_cnd
.cn_nameiop
= 0;
3661 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3662 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3668 /* assemble reply */
3669 nd
->nd_repstat
= error
;
3670 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_SRVFH(nd
->nd_vers
, &nfh
) +
3671 NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_WCCDATA(nd
->nd_vers
));
3673 *mrepp
= nmrep
.nmc_mhead
;
3674 nfsmout_on_status(nd
, error
);
3675 if (nd
->nd_vers
== NFS_VER3
) {
3676 if (!nd
->nd_repstat
) {
3677 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3678 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, postattrerr
, &postattr
);
3680 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3681 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3683 nfsm_chain_add_fh(error
, &nmrep
, NFS_VER2
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3685 error
= nfsm_chain_add_fattr(nd
, &nmrep
, &postattr
);
3688 nfsm_chain_build_done(error
, &nmrep
);
3689 if (ni
.ni_cnd
.cn_nameiop
) {
3691 * nameidone has to happen before we vnode_put(dvp)
3692 * since it may need to release the fs_nodelock on the dvp
3702 nfsm_chain_cleanup(&nmrep
);
3713 struct nfsrv_descript
*nd
,
3714 struct nfsrv_sock
*slp
,
3718 int error
, dpreattrerr
, dpostattrerr
;
3721 vnode_t vp
, dvp
, dirp
;
3722 struct vnode_attr dpreattr
, dpostattr
;
3723 struct nfs_filehandle nfh
;
3724 struct nfs_export
*nx
= NULL
;
3725 struct nfs_export_options
*nxo
;
3726 struct nameidata ni
;
3727 struct nfsm_chain
*nmreq
, nmrep
;
3730 dpreattrerr
= dpostattrerr
= ENOENT
;
3731 saved_uid
= kauth_cred_getuid(nd
->nd_cr
);
3732 nmreq
= &nd
->nd_nmreq
;
3733 nfsm_chain_null(&nmrep
);
3735 vp
= dvp
= dirp
= NULL
;
3737 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3738 nfsm_chain_get_32(error
, nmreq
, len
);
3739 nfsm_name_len_check(error
, nd
, len
);
3742 ni
.ni_cnd
.cn_nameiop
= DELETE
;
3743 ni
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
3744 error
= nfsm_chain_get_path_namei(nmreq
, len
, &ni
);
3746 error
= nfsrv_namei(nd
, ctx
, &ni
, &nfh
, &dirp
, &nx
, &nxo
);
3748 /* update export stats */
3749 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3751 /* update active user stats */
3752 nfsrv_update_user_stat(nx
, nd
, saved_uid
, 1, 0, 0);
3756 if (nd
->nd_vers
== NFS_VER3
) {
3757 nfsm_srv_pre_vattr_init(&dpreattr
);
3758 dpreattrerr
= vnode_getattr(dirp
, &dpreattr
, ctx
);
3769 if (vnode_vtype(vp
) != VDIR
) {
3774 * No rmdir "." please.
3781 * The root of a mounted filesystem cannot be deleted.
3783 if (vnode_isvroot(vp
))
3786 error
= nfsrv_authorize(vp
, dvp
, KAUTH_VNODE_DELETE
, ctx
, nxo
, 0);
3793 if (nfsrv_fsevents_enabled
&& need_fsevent(FSE_DELETE
, dvp
)) {
3795 if ((path
= get_pathbuff()) && !vn_getpath(vp
, path
, &plen
)) {
3796 get_fse_info(vp
, &finfo
, ctx
);
3798 release_pathbuff(path
);
3802 #endif /* CONFIG_FSE */
3804 error
= VNOP_RMDIR(dvp
, vp
, &ni
.ni_cnd
, ctx
);
3809 add_fsevent(FSE_DELETE
, ctx
,
3810 FSE_ARG_STRING
, plen
, path
,
3811 FSE_ARG_FINFO
, &finfo
,
3813 release_pathbuff(path
);
3815 #endif /* CONFIG_FSE */
3819 * nameidone has to happen before we vnode_put(dvp)
3820 * since it may need to release the fs_nodelock on the dvp
3828 nfsm_srv_vattr_init(&dpostattr
, nd
->nd_vers
);
3829 dpostattrerr
= vnode_getattr(dirp
, &dpostattr
, ctx
);
3835 /* assemble reply */
3836 nd
->nd_repstat
= error
;
3837 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_WCCDATA(nd
->nd_vers
));
3839 *mrepp
= nmrep
.nmc_mhead
;
3840 nfsmout_on_status(nd
, error
);
3841 if (nd
->nd_vers
== NFS_VER3
)
3842 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
3843 dpreattrerr
, &dpreattr
, dpostattrerr
, &dpostattr
);
3845 nfsm_chain_build_done(error
, &nmrep
);
3849 nfsm_chain_cleanup(&nmrep
);
3856 * nfs readdir service
3857 * - mallocs what it thinks is enough to read
3858 * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
3859 * - calls VNOP_READDIR()
3860 * - loops around building the reply
3861 * if the output generated exceeds count break out of loop
3862 * The nfsm_clget macro is used here so that the reply will be packed
3863 * tightly in mbuf clusters.
3864 * - it only knows that it has encountered eof when the VNOP_READDIR()
3866 * - as such one readdir rpc will return eof false although you are there
3867 * and then the next will return eof
3868 * - it trims out records with d_fileno == 0
3869 * this doesn't matter for Unix clients, but they might confuse clients
3871 * NB: It is tempting to set eof to true if the VNOP_READDIR() reads less
3872 * than requested, but this may not apply to all filesystems. For
3873 * example, client NFS does not { although it is never remote mounted
3875 * The alternate call nfsrv_readdirplus() does lookups as well.
3876 * PS: The XNFS protocol spec clearly describes what the "count"s arguments
3877 * are supposed to cover. For readdir, the count is the total number of
3878 * bytes included in everything from the directory's postopattr through
3879 * the EOF flag. For readdirplus, the maxcount is the same, and the
3880 * dircount includes all that except for the entry attributes and handles.
3884 struct nfsrv_descript
*nd
,
3885 struct nfsrv_sock
*slp
,
3889 struct direntry
*dp
;
3890 char *cpos
, *cend
, *rbuf
;
3892 struct vnode_attr attr
;
3893 struct nfs_filehandle nfh
;
3894 struct nfs_export
*nx
;
3895 struct nfs_export_options
*nxo
;
3897 char uio_buf
[ UIO_SIZEOF(1) ];
3898 int len
, nlen
, rem
, xfer
, error
, attrerr
;
3899 int siz
, count
, fullsiz
, eofflag
, nentries
;
3900 u_quad_t off
, toff
, verf
;
3902 struct nfsm_chain
*nmreq
, nmrep
;
3907 nmreq
= &nd
->nd_nmreq
;
3908 nfsm_chain_null(&nmrep
);
3912 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
3914 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
3915 if (nd
->nd_vers
== NFS_VER3
) {
3916 nfsm_chain_get_64(error
, nmreq
, toff
);
3917 nfsm_chain_get_64(error
, nmreq
, verf
);
3919 nfsm_chain_get_32(error
, nmreq
, toff
);
3921 nfsm_chain_get_32(error
, nmreq
, count
);
3925 siz
= ((count
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
3926 xfer
= NFS_SRVMAXDATA(nd
);
3931 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
3934 /* update export stats */
3935 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
3937 /* update active user stats */
3938 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
3940 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
3943 if ((nd
->nd_vers
== NFS_VER2
) || (nxo
->nxo_flags
& NX_32BITCLIENTS
))
3944 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
3945 if (nd
->nd_vers
== NFS_VER3
) {
3946 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
3947 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
3948 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
))
3949 error
= NFSERR_BAD_COOKIE
;
3952 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
3955 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
3957 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
3958 &uio_buf
[0], sizeof(uio_buf
));
3959 if (!rbuf
|| !auio
) {
3964 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
3965 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
3967 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
3968 off
= uio_offset(auio
);
3970 if (nd
->nd_vers
== NFS_VER3
) {
3971 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
3972 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
3976 if (uio_resid(auio
) != 0) {
3977 // LP64todo - fix this
3978 siz
-= uio_resid(auio
);
3980 /* If nothing read, return empty reply with eof set */
3985 /* assemble reply */
3986 nd
->nd_repstat
= error
;
3987 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
3988 NFSX_COOKIEVERF(nd
->nd_vers
) + 2 * NFSX_UNSIGNED
);
3990 *mrepp
= nmrep
.nmc_mhead
;
3991 nfsmout_on_status(nd
, error
);
3992 if (nd
->nd_vers
== NFS_VER3
) {
3993 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
3994 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
3996 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
3997 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
3998 nfsm_chain_build_done(error
, &nmrep
);
4004 * Check for degenerate cases of nothing useful read.
4005 * If so go try again
4009 dp
= (struct direntry
*)cpos
;
4010 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4011 cpos
+= dp
->d_reclen
;
4012 dp
= (struct direntry
*)cpos
;
4015 if ((cpos
>= cend
) || (nentries
== 0)) {
4024 /* assemble reply */
4025 nd
->nd_repstat
= error
;
4026 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) +
4027 NFSX_COOKIEVERF(nd
->nd_vers
) + siz
);
4029 *mrepp
= nmrep
.nmc_mhead
;
4030 nfsmout_on_status(nd
, error
);
4031 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4033 len
= 2 * NFSX_UNSIGNED
;
4034 if (nd
->nd_vers
== NFS_VER3
) {
4035 len
+= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
;
4036 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4037 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4041 /* Loop through the records and build reply */
4042 while ((cpos
< cend
) && (nentries
> 0)) {
4043 if (dp
->d_fileno
!= 0) {
4044 nlen
= dp
->d_namlen
;
4045 if ((nd
->nd_vers
== NFS_VER2
) && (nlen
> NFS_MAXNAMLEN
))
4046 nlen
= NFS_MAXNAMLEN
;
4047 rem
= nfsm_rndup(nlen
)-nlen
;
4048 len
+= (4 * NFSX_UNSIGNED
+ nlen
+ rem
);
4049 if (nd
->nd_vers
== NFS_VER3
)
4050 len
+= 2 * NFSX_UNSIGNED
;
4055 /* Build the directory record xdr from the direntry. */
4056 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4057 if (nd
->nd_vers
== NFS_VER3
) {
4058 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4060 nfsm_chain_add_32(error
, &nmrep
, dp
->d_fileno
);
4062 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4063 if (nd
->nd_vers
== NFS_VER3
) {
4064 if (vnopflag
& VNODE_READDIR_SEEKOFF32
)
4065 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4066 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4068 nfsm_chain_add_32(error
, &nmrep
, dp
->d_seekoff
);
4072 cpos
+= dp
->d_reclen
;
4073 dp
= (struct direntry
*)cpos
;
4076 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4077 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4085 nd
->nd_repstat
= error
;
4086 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
));
4088 *mrepp
= nmrep
.nmc_mhead
;
4089 nfsmout_on_status(nd
, error
);
4090 if (nd
->nd_vers
== NFS_VER3
)
4091 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4093 nfsm_chain_build_done(error
, &nmrep
);
4095 nfsm_chain_cleanup(&nmrep
);
4103 struct nfsrv_descript
*nd
,
4104 struct nfsrv_sock
*slp
,
4108 struct direntry
*dp
;
4109 char *cpos
, *cend
, *rbuf
;
4111 struct nfs_filehandle dnfh
, nfh
;
4112 struct nfs_export
*nx
;
4113 struct nfs_export_options
*nxo
;
4115 char uio_buf
[ UIO_SIZEOF(1) ];
4116 struct vnode_attr attr
, va
, *vap
= &va
;
4117 int len
, nlen
, rem
, xfer
, error
, attrerr
, gotfh
, gotattr
;
4118 int siz
, dircount
, maxcount
, fullsiz
, eofflag
, dirlen
, nentries
, isdotdot
;
4119 u_quad_t off
, toff
, verf
;
4121 struct nfsm_chain
*nmreq
, nmrep
;
4126 nmreq
= &nd
->nd_nmreq
;
4127 nfsm_chain_null(&nmrep
);
4130 dircount
= maxcount
= 0;
4132 vnopflag
= VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
;
4134 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, dnfh
.nfh_fhp
, dnfh
.nfh_len
);
4135 nfsm_chain_get_64(error
, nmreq
, toff
);
4136 nfsm_chain_get_64(error
, nmreq
, verf
);
4137 nfsm_chain_get_32(error
, nmreq
, dircount
);
4138 nfsm_chain_get_32(error
, nmreq
, maxcount
);
4142 xfer
= NFS_SRVMAXDATA(nd
);
4143 dircount
= ((dircount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4144 if (dircount
> xfer
)
4146 fullsiz
= siz
= dircount
;
4147 maxcount
= ((maxcount
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
4148 if (maxcount
> xfer
)
4151 error
= nfsrv_fhtovp(&dnfh
, nd
, &vp
, &nx
, &nxo
);
4154 /* update export stats */
4155 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4157 /* update active user stats */
4158 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4160 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4163 if (nxo
->nxo_flags
& NX_32BITCLIENTS
)
4164 vnopflag
|= VNODE_READDIR_SEEKOFF32
;
4166 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4167 error
= attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4168 if (!error
&& toff
&& verf
&& (verf
!= attr
.va_filerev
))
4169 error
= NFSERR_BAD_COOKIE
;
4171 error
= nfsrv_authorize(vp
, NULL
, KAUTH_VNODE_LIST_DIRECTORY
, ctx
, nxo
, 0);
4174 MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
4176 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
4177 &uio_buf
[0], sizeof(uio_buf
));
4178 if (!rbuf
|| !auio
) {
4184 uio_reset(auio
, off
, UIO_SYSSPACE
, UIO_READ
);
4185 uio_addiov(auio
, CAST_USER_ADDR_T(rbuf
), fullsiz
);
4187 error
= VNOP_READDIR(vp
, auio
, vnopflag
, &eofflag
, &nentries
, ctx
);
4188 off
= uio_offset(auio
);
4189 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4190 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4193 if (uio_resid(auio
) != 0) {
4194 // LP64todo - fix this
4195 siz
-= uio_resid(auio
);
4197 /* If nothing read, return empty reply with eof set */
4202 /* assemble reply */
4203 nd
->nd_repstat
= error
;
4204 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+
4205 NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
);
4207 *mrepp
= nmrep
.nmc_mhead
;
4208 nfsmout_on_status(nd
, error
);
4209 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4210 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4211 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4212 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4213 nfsm_chain_build_done(error
, &nmrep
);
4219 * Check for degenerate cases of nothing useful read.
4220 * If so go try again
4224 dp
= (struct direntry
*)cpos
;
4225 while ((dp
->d_fileno
== 0) && (cpos
< cend
) && (nentries
> 0)) {
4226 cpos
+= dp
->d_reclen
;
4227 dp
= (struct direntry
*)cpos
;
4230 if ((cpos
>= cend
) || (nentries
== 0)) {
4237 * Probe one of the directory entries to see if the filesystem
4240 if ((error
= VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
))) {
4241 if (error
== ENOTSUP
) /* let others get passed back */
4242 error
= NFSERR_NOTSUPP
;
4247 /* assemble reply */
4248 nd
->nd_repstat
= error
;
4249 error
= nfsrv_rephead(nd
, slp
, &nmrep
, maxcount
);
4251 *mrepp
= nmrep
.nmc_mhead
;
4252 nfsmout_on_status(nd
, error
);
4253 nmrep
.nmc_flags
|= NFSM_CHAIN_FLAG_ADD_CLUSTERS
;
4255 dirlen
= len
= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
;
4256 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4257 nfsm_chain_add_64(error
, &nmrep
, attr
.va_filerev
);
4260 /* Loop through the records and build reply */
4261 while ((cpos
< cend
) && (nentries
> 0)) {
4262 if (dp
->d_fileno
!= 0) {
4263 nlen
= dp
->d_namlen
;
4264 rem
= nfsm_rndup(nlen
)-nlen
;
4265 gotfh
= gotattr
= 1;
4267 /* Got to get the vnode for lookup per entry. */
4268 if (VFS_VGET(vnode_mount(vp
), (ino64_t
)dp
->d_fileno
, &nvp
, ctx
)) {
4269 /* Can't get the vnode... so no fh or attrs */
4270 gotfh
= gotattr
= 0;
4272 isdotdot
= ((dp
->d_namlen
== 2) &&
4273 (dp
->d_name
[0] == '.') && (dp
->d_name
[1] == '.'));
4274 if (nfsrv_vptofh(nx
, 0, (isdotdot
? &dnfh
: NULL
), nvp
, ctx
, &nfh
))
4276 nfsm_srv_vattr_init(vap
, NFS_VER3
);
4277 if (vnode_getattr(nvp
, vap
, ctx
))
4283 * If either the dircount or maxcount will be
4284 * exceeded, get out now. Both of these lengths
4285 * are calculated conservatively, including all
4288 len
+= 8 * NFSX_UNSIGNED
+ nlen
+ rem
;
4290 len
+= NFSX_V3FATTR
;
4292 len
+= NFSX_UNSIGNED
+ nfsm_rndup(nfh
.nfh_len
);
4293 dirlen
+= 6 * NFSX_UNSIGNED
+ nlen
+ rem
;
4294 if ((len
> maxcount
) || (dirlen
> dircount
)) {
4299 /* Build the directory record xdr from the direntry. */
4300 nfsm_chain_add_32(error
, &nmrep
, TRUE
);
4301 nfsm_chain_add_64(error
, &nmrep
, dp
->d_fileno
);
4302 nfsm_chain_add_string(error
, &nmrep
, dp
->d_name
, nlen
);
4303 if (vnopflag
& VNODE_READDIR_SEEKOFF32
)
4304 dp
->d_seekoff
&= 0x00000000ffffffffULL
;
4305 nfsm_chain_add_64(error
, &nmrep
, dp
->d_seekoff
);
4306 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, (gotattr
? 0 : ENOENT
), vap
);
4308 nfsm_chain_add_postop_fh(error
, &nmrep
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4310 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4313 cpos
+= dp
->d_reclen
;
4314 dp
= (struct direntry
*)cpos
;
4319 nfsm_chain_add_32(error
, &nmrep
, FALSE
);
4320 nfsm_chain_add_32(error
, &nmrep
, eofflag
? TRUE
: FALSE
);
4326 nd
->nd_repstat
= error
;
4327 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
);
4329 *mrepp
= nmrep
.nmc_mhead
;
4330 nfsmout_on_status(nd
, error
);
4331 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4333 nfsm_chain_build_done(error
, &nmrep
);
4337 nfsm_chain_cleanup(&nmrep
);
4344 * nfs commit service
4348 struct nfsrv_descript
*nd
,
4349 struct nfsrv_sock
*slp
,
4354 struct nfs_filehandle nfh
;
4355 struct nfs_export
*nx
;
4356 struct nfs_export_options
*nxo
;
4357 int error
, preattrerr
, postattrerr
, count
;
4358 struct vnode_attr preattr
, postattr
;
4360 struct nfsm_chain
*nmreq
, nmrep
;
4363 preattrerr
= postattrerr
= ENOENT
;
4364 nmreq
= &nd
->nd_nmreq
;
4365 nfsm_chain_null(&nmrep
);
4369 * XXX At this time VNOP_FSYNC() does not accept offset and byte
4370 * count parameters, so those arguments are useless (someday maybe).
4373 nfsm_chain_get_fh_ptr(error
, nmreq
, NFS_VER3
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4374 nfsm_chain_get_64(error
, nmreq
, off
);
4375 nfsm_chain_get_32(error
, nmreq
, count
);
4378 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4381 /* update export stats */
4382 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4384 /* update active user stats */
4385 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4387 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4390 nfsm_srv_pre_vattr_init(&preattr
);
4391 preattrerr
= vnode_getattr(vp
, &preattr
, ctx
);
4393 error
= VNOP_FSYNC(vp
, MNT_WAIT
, ctx
);
4395 nfsm_srv_vattr_init(&postattr
, 1);
4396 postattrerr
= vnode_getattr(vp
, &postattr
, ctx
);
4402 /* assemble reply */
4403 nd
->nd_repstat
= error
;
4404 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3WCCDATA
+ NFSX_V3WRITEVERF
);
4406 *mrepp
= nmrep
.nmc_mhead
;
4407 nfsmout_on_status(nd
, error
);
4408 nfsm_chain_add_wcc_data(error
, nd
, &nmrep
,
4409 preattrerr
, &preattr
, postattrerr
, &postattr
);
4410 if (!nd
->nd_repstat
) {
4411 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_sec
);
4412 nfsm_chain_add_32(error
, &nmrep
, nx
->nx_exptime
.tv_usec
);
4415 nfsm_chain_build_done(error
, &nmrep
);
4417 nfsm_chain_cleanup(&nmrep
);
4424 * nfs statfs service
4428 struct nfsrv_descript
*nd
,
4429 struct nfsrv_sock
*slp
,
4436 struct vnode_attr attr
;
4437 struct nfs_filehandle nfh
;
4438 struct nfs_export
*nx
;
4439 struct nfs_export_options
*nxo
;
4441 struct nfsm_chain
*nmreq
, nmrep
;
4445 nmreq
= &nd
->nd_nmreq
;
4446 nfsm_chain_null(&nmrep
);
4450 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4452 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4455 /* update export stats */
4456 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4458 /* update active user stats */
4459 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4461 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4465 VFSATTR_WANTED(&va
, f_blocks
);
4466 VFSATTR_WANTED(&va
, f_bavail
);
4467 VFSATTR_WANTED(&va
, f_files
);
4468 VFSATTR_WANTED(&va
, f_ffree
);
4469 error
= vfs_getattr(vnode_mount(vp
), &va
, ctx
);
4470 blksize
= vnode_mount(vp
)->mnt_vfsstat
.f_bsize
;
4472 if (nd
->nd_vers
== NFS_VER3
) {
4473 nfsm_srv_vattr_init(&attr
, nd
->nd_vers
);
4474 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4481 /* assemble reply */
4482 nd
->nd_repstat
= error
;
4483 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_POSTOPATTR(nd
->nd_vers
) + NFSX_STATFS(nd
->nd_vers
));
4485 *mrepp
= nmrep
.nmc_mhead
;
4486 nfsmout_on_status(nd
, error
);
4487 if (nd
->nd_vers
== NFS_VER3
)
4488 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4489 nfsmout_if(nd
->nd_repstat
);
4491 if (nd
->nd_vers
== NFS_VER3
) {
4492 nfsm_chain_add_64(error
, &nmrep
, va
.f_blocks
* blksize
);
4493 nfsm_chain_add_64(error
, &nmrep
, va
.f_bfree
* blksize
);
4494 nfsm_chain_add_64(error
, &nmrep
, va
.f_bavail
* blksize
);
4495 nfsm_chain_add_64(error
, &nmrep
, va
.f_files
);
4496 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4497 nfsm_chain_add_64(error
, &nmrep
, va
.f_ffree
);
4498 nfsm_chain_add_32(error
, &nmrep
, 0); /* invarsec */
4500 nfsm_chain_add_32(error
, &nmrep
, NFS_V2MAXDATA
);
4501 nfsm_chain_add_32(error
, &nmrep
, blksize
);
4502 nfsm_chain_add_32(error
, &nmrep
, va
.f_blocks
);
4503 nfsm_chain_add_32(error
, &nmrep
, va
.f_bfree
);
4504 nfsm_chain_add_32(error
, &nmrep
, va
.f_bavail
);
4507 nfsm_chain_build_done(error
, &nmrep
);
4509 nfsm_chain_cleanup(&nmrep
);
4516 * nfs fsinfo service
4520 struct nfsrv_descript
*nd
,
4521 struct nfsrv_sock
*slp
,
4525 int error
, attrerr
, prefsize
, maxsize
;
4527 struct vnode_attr attr
;
4528 struct nfs_filehandle nfh
;
4529 struct nfs_export
*nx
;
4530 struct nfs_export_options
*nxo
;
4531 struct nfsm_chain
*nmreq
, nmrep
;
4535 nmreq
= &nd
->nd_nmreq
;
4536 nfsm_chain_null(&nmrep
);
4539 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4541 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4544 /* update export stats */
4545 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4547 /* update active user stats */
4548 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4550 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4553 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4554 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4560 /* assemble reply */
4561 nd
->nd_repstat
= error
;
4562 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3FSINFO
);
4564 *mrepp
= nmrep
.nmc_mhead
;
4565 nfsmout_on_status(nd
, error
);
4566 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4567 nfsmout_if(nd
->nd_repstat
);
4570 * XXX There should be file system VFS OP(s) to get this information.
4571 * For now, assume our usual NFS defaults.
4573 if (slp
->ns_sotype
== SOCK_DGRAM
) {
4574 maxsize
= NFS_MAXDGRAMDATA
;
4575 prefsize
= NFS_PREFDGRAMDATA
;
4577 maxsize
= prefsize
= NFS_MAXDATA
;
4579 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
4580 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
4581 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
4582 nfsm_chain_add_32(error
, &nmrep
, maxsize
);
4583 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
4584 nfsm_chain_add_32(error
, &nmrep
, NFS_FABLKSIZE
);
4585 nfsm_chain_add_32(error
, &nmrep
, prefsize
);
4586 nfsm_chain_add_64(error
, &nmrep
, 0xffffffffffffffffULL
);
4587 nfsm_chain_add_32(error
, &nmrep
, 0);
4588 nfsm_chain_add_32(error
, &nmrep
, 1);
4589 /* XXX link/symlink support should be taken from volume capabilities */
4590 nfsm_chain_add_32(error
, &nmrep
,
4591 NFSV3FSINFO_LINK
| NFSV3FSINFO_SYMLINK
|
4592 NFSV3FSINFO_HOMOGENEOUS
| NFSV3FSINFO_CANSETTIME
);
4595 nfsm_chain_build_done(error
, &nmrep
);
4597 nfsm_chain_cleanup(&nmrep
);
4604 * nfs pathconf service
4608 struct nfsrv_descript
*nd
,
4609 struct nfsrv_sock
*slp
,
4613 int error
, attrerr
, linkmax
, namemax
;
4614 int chownres
, notrunc
, case_sensitive
, case_preserving
;
4616 struct vnode_attr attr
;
4617 struct nfs_filehandle nfh
;
4618 struct nfs_export
*nx
;
4619 struct nfs_export_options
*nxo
;
4620 struct nfsm_chain
*nmreq
, nmrep
;
4624 nmreq
= &nd
->nd_nmreq
;
4625 nfsm_chain_null(&nmrep
);
4628 nfsm_chain_get_fh_ptr(error
, nmreq
, nd
->nd_vers
, nfh
.nfh_fhp
, nfh
.nfh_len
);
4630 error
= nfsrv_fhtovp(&nfh
, nd
, &vp
, &nx
, &nxo
);
4633 /* update export stats */
4634 NFSStatAdd64(&nx
->nx_stats
.ops
, 1);
4636 /* update active user stats */
4637 nfsrv_update_user_stat(nx
, nd
, kauth_cred_getuid(nd
->nd_cr
), 1, 0, 0);
4639 error
= nfsrv_credcheck(nd
, ctx
, nx
, nxo
);
4642 error
= VNOP_PATHCONF(vp
, _PC_LINK_MAX
, &linkmax
, ctx
);
4644 error
= VNOP_PATHCONF(vp
, _PC_NAME_MAX
, &namemax
, ctx
);
4646 error
= VNOP_PATHCONF(vp
, _PC_CHOWN_RESTRICTED
, &chownres
, ctx
);
4648 error
= VNOP_PATHCONF(vp
, _PC_NO_TRUNC
, ¬runc
, ctx
);
4650 error
= VNOP_PATHCONF(vp
, _PC_CASE_SENSITIVE
, &case_sensitive
, ctx
);
4652 error
= VNOP_PATHCONF(vp
, _PC_CASE_PRESERVING
, &case_preserving
, ctx
);
4654 nfsm_srv_vattr_init(&attr
, NFS_VER3
);
4655 attrerr
= vnode_getattr(vp
, &attr
, ctx
);
4661 /* assemble reply */
4662 nd
->nd_repstat
= error
;
4663 error
= nfsrv_rephead(nd
, slp
, &nmrep
, NFSX_V3POSTOPATTR
+ NFSX_V3PATHCONF
);
4665 *mrepp
= nmrep
.nmc_mhead
;
4666 nfsmout_on_status(nd
, error
);
4667 nfsm_chain_add_postop_attr(error
, nd
, &nmrep
, attrerr
, &attr
);
4668 nfsmout_if(nd
->nd_repstat
);
4670 nfsm_chain_add_32(error
, &nmrep
, linkmax
);
4671 nfsm_chain_add_32(error
, &nmrep
, namemax
);
4672 nfsm_chain_add_32(error
, &nmrep
, notrunc
);
4673 nfsm_chain_add_32(error
, &nmrep
, chownres
);
4674 nfsm_chain_add_32(error
, &nmrep
, !case_sensitive
);
4675 nfsm_chain_add_32(error
, &nmrep
, case_preserving
);
4678 nfsm_chain_build_done(error
, &nmrep
);
4680 nfsm_chain_cleanup(&nmrep
);
4687 * Null operation, used by clients to ping server
4692 struct nfsrv_descript
*nd
,
4693 struct nfsrv_sock
*slp
,
4694 __unused vfs_context_t ctx
,
4697 int error
= NFSERR_RETVOID
;
4698 struct nfsm_chain nmrep
;
4701 * RPCSEC_GSS context setup ?
4703 if (nd
->nd_gss_context
)
4704 return(nfs_gss_svc_ctx_init(nd
, slp
, mrepp
));
4706 nfsm_chain_null(&nmrep
);
4708 /* assemble reply */
4709 nd
->nd_repstat
= error
;
4710 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
4712 *mrepp
= nmrep
.nmc_mhead
;
4714 nfsm_chain_build_done(error
, &nmrep
);
4716 nfsm_chain_cleanup(&nmrep
);
4723 * No operation, used for obsolete procedures
4728 struct nfsrv_descript
*nd
,
4729 struct nfsrv_sock
*slp
,
4730 __unused vfs_context_t ctx
,
4734 struct nfsm_chain nmrep
;
4736 nfsm_chain_null(&nmrep
);
4739 error
= nd
->nd_repstat
;
4741 error
= EPROCUNAVAIL
;
4743 /* assemble reply */
4744 nd
->nd_repstat
= error
;
4745 error
= nfsrv_rephead(nd
, slp
, &nmrep
, 0);
4747 *mrepp
= nmrep
.nmc_mhead
;
4749 nfsm_chain_build_done(error
, &nmrep
);
4751 nfsm_chain_cleanup(&nmrep
);
4757 int (*nfsrv_procs
[NFS_NPROCS
])(struct nfsrv_descript
*nd
,
4758 struct nfsrv_sock
*slp
,
4787 * Perform access checking for vnodes obtained from file handles that would
4788 * refer to files already opened by a Unix client. You cannot just use
4789 * vnode_authorize() for two reasons.
4790 * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
4791 * 2 - The owner is to be given access irrespective of mode bits so that
4792 * processes that chmod after opening a file don't break. I don't like
4793 * this because it opens a security hole, but since the nfs server opens
4794 * a security hole the size of a barn door anyhow, what the heck.
4796 * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize()
4797 * will return EPERM instead of EACCESS. EPERM is always an error.
4804 kauth_action_t action
,
4806 struct nfs_export_options
*nxo
,
4809 struct vnode_attr vattr
;
4812 if (action
& KAUTH_VNODE_WRITE_RIGHTS
) {
4814 * Disallow write attempts on read-only exports;
4815 * unless the file is a socket or a block or character
4816 * device resident on the file system.
4818 if (nxo
->nxo_flags
& NX_READONLY
) {
4819 switch (vnode_vtype(vp
)) {
4820 case VREG
: case VDIR
: case VLNK
: case VCPLX
:
4827 error
= vnode_authorize(vp
, dvp
, action
, ctx
);
4829 * Allow certain operations for the owner (reads and writes
4830 * on files that are already open). Picking up from FreeBSD.
4832 if (override
&& (error
== EACCES
)) {
4834 VATTR_WANTED(&vattr
, va_uid
);
4835 if ((vnode_getattr(vp
, &vattr
, ctx
) == 0) &&
4836 (kauth_cred_getuid(vfs_context_ucred(ctx
)) == vattr
.va_uid
))
4842 #endif /* NFSSERVER */