2 * Copyright (c) 2019 Apple Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Portions Copyright (c) 1992, 1993
26 * The Regents of the University of California. All rights reserved.
28 * This code is derived from software contributed to Berkeley by
29 * John Heidemann of the UCLA Ficus project.
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * @(#)null_vnops.c 8.6 (Berkeley) 5/27/95
58 * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92
60 * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
65 #include <sys/param.h>
66 #include <sys/systm.h>
68 #include <sys/kernel.h>
70 #include <sys/malloc.h>
71 #include <sys/mount.h>
72 #include <sys/mount_internal.h>
73 #include <sys/namei.h>
74 #include <sys/sysctl.h>
75 #include <sys/vnode.h>
76 #include <sys/xattr.h>
78 #include <sys/types.h>
79 #include <sys/dirent.h>
80 #include <sys/kauth.h>
84 #define NULL_ROOT_INO 2
85 #define NULL_SECOND_INO 3
86 #define NULL_THIRD_INO 4
88 vop_t
* nullfs_vnodeop_p
= NULL
;
90 /* the mountpoint lock should be held going into this function */
92 nullfs_isspecialvp(struct vnode
* vp
)
94 struct null_mount
* null_mp
;
96 null_mp
= MOUNTTONULLMOUNT(vnode_mount(vp
));
98 /* only check for root and second here, third is special in a different way,
99 * related only to lookup and readdir */
100 if (vp
&& (vp
== null_mp
->nullm_rootvp
|| vp
== null_mp
->nullm_secondvp
)) {
106 /* helper function to handle locking where possible */
108 nullfs_checkspecialvp(struct vnode
* vp
)
111 struct null_mount
* null_mp
;
113 null_mp
= MOUNTTONULLMOUNT(vnode_mount(vp
));
115 lck_mtx_lock(&null_mp
->nullm_lock
);
116 result
= (nullfs_isspecialvp(vp
));
117 lck_mtx_unlock(&null_mp
->nullm_lock
);
123 nullfs_get_patched_context(struct null_mount
* null_mp
, vfs_context_t ctx
)
125 struct vfs_context
* ectx
= ctx
;
126 if ((null_mp
->nullm_flags
& NULLM_UNVEIL
) == NULLM_UNVEIL
) {
127 ectx
= vfs_context_create(ctx
);
128 ectx
->vc_ucred
= kauth_cred_setuidgid(ectx
->vc_ucred
, null_mp
->uid
, null_mp
->gid
);
134 nullfs_cleanup_patched_context(struct null_mount
* null_mp
, vfs_context_t ctx
)
136 if ((null_mp
->nullm_flags
& NULLM_UNVEIL
) == NULLM_UNVEIL
) {
137 vfs_context_rele(ctx
);
142 nullfs_default(__unused
struct vnop_generic_args
* args
)
144 NULLFSDEBUG("%s (default)\n", ((struct vnodeop_desc_fake
*)args
->a_desc
)->vdesc_name
);
149 nullfs_special_getattr(struct vnop_getattr_args
* args
)
151 mount_t mp
= vnode_mount(args
->a_vp
);
152 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
154 ino_t ino
= NULL_ROOT_INO
;
155 struct vnode_attr covered_rootattr
;
156 vnode_t checkvp
= null_mp
->nullm_lowerrootvp
;
157 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
159 VATTR_INIT(&covered_rootattr
);
160 VATTR_WANTED(&covered_rootattr
, va_uid
);
161 VATTR_WANTED(&covered_rootattr
, va_gid
);
162 VATTR_WANTED(&covered_rootattr
, va_create_time
);
163 VATTR_WANTED(&covered_rootattr
, va_modify_time
);
164 VATTR_WANTED(&covered_rootattr
, va_access_time
);
166 /* prefer to get this from the lower root vp, but if not (i.e. forced unmount
167 * of lower fs) try the mount point covered vnode */
168 if (vnode_getwithvid(checkvp
, null_mp
->nullm_lowerrootvid
)) {
169 checkvp
= vfs_vnodecovered(mp
);
170 if (checkvp
== NULL
) {
171 nullfs_cleanup_patched_context(null_mp
, ectx
);
176 int error
= vnode_getattr(checkvp
, &covered_rootattr
, ectx
);
180 /* we should have been able to get attributes fore one of the two choices so
181 * fail if we didn't */
182 nullfs_cleanup_patched_context(null_mp
, ectx
);
186 /* we got the attributes of the vnode we cover so plow ahead */
187 if (args
->a_vp
== null_mp
->nullm_secondvp
) {
188 ino
= NULL_SECOND_INO
;
191 VATTR_RETURN(args
->a_vap
, va_type
, vnode_vtype(args
->a_vp
));
192 VATTR_RETURN(args
->a_vap
, va_rdev
, 0);
193 VATTR_RETURN(args
->a_vap
, va_nlink
, 3); /* always just ., .., and the child */
194 VATTR_RETURN(args
->a_vap
, va_total_size
, 0); // hoping this is ok
196 VATTR_RETURN(args
->a_vap
, va_data_size
, 0); // hoping this is ok
197 VATTR_RETURN(args
->a_vap
, va_data_alloc
, 0);
198 VATTR_RETURN(args
->a_vap
, va_iosize
, vfs_statfs(mp
)->f_iosize
);
199 VATTR_RETURN(args
->a_vap
, va_fileid
, ino
);
200 VATTR_RETURN(args
->a_vap
, va_linkid
, ino
);
201 if (VATTR_IS_ACTIVE(args
->a_vap
, va_fsid
)) {
202 VATTR_RETURN(args
->a_vap
, va_fsid
, vfs_statfs(mp
)->f_fsid
.val
[0]); // return the fsid of the mount point
204 if (VATTR_IS_ACTIVE(args
->a_vap
, va_fsid64
)) {
205 VATTR_RETURN(args
->a_vap
, va_fsid64
, vfs_statfs(mp
)->f_fsid
);
207 VATTR_RETURN(args
->a_vap
, va_filerev
, 0);
208 VATTR_RETURN(args
->a_vap
, va_gen
, 0);
209 VATTR_RETURN(args
->a_vap
, va_flags
, UF_HIDDEN
); /* mark our fake directories as hidden. People
210 * shouldn't be enocouraged to poke around in them */
212 if (ino
== NULL_SECOND_INO
) {
213 VATTR_RETURN(args
->a_vap
, va_parentid
, NULL_ROOT_INO
); /* no parent at the root, so
214 * the only other vnode that
215 * goes through this path is
216 * second and its parent is
220 if (VATTR_IS_ACTIVE(args
->a_vap
, va_mode
)) {
221 /* force dr_xr_xr_x */
222 VATTR_RETURN(args
->a_vap
, va_mode
, S_IFDIR
| S_IRUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
);
224 if (VATTR_IS_ACTIVE(args
->a_vap
, va_uid
)) {
225 VATTR_RETURN(args
->a_vap
, va_uid
, covered_rootattr
.va_uid
);
227 if (VATTR_IS_ACTIVE(args
->a_vap
, va_gid
)) {
228 VATTR_RETURN(args
->a_vap
, va_gid
, covered_rootattr
.va_gid
);
231 if (VATTR_IS_ACTIVE(args
->a_vap
, va_create_time
)) {
232 VATTR_SET_SUPPORTED(args
->a_vap
, va_create_time
);
233 args
->a_vap
->va_create_time
.tv_sec
= covered_rootattr
.va_create_time
.tv_sec
;
234 args
->a_vap
->va_create_time
.tv_nsec
= covered_rootattr
.va_create_time
.tv_nsec
;
236 if (VATTR_IS_ACTIVE(args
->a_vap
, va_modify_time
)) {
237 VATTR_SET_SUPPORTED(args
->a_vap
, va_modify_time
);
238 args
->a_vap
->va_modify_time
.tv_sec
= covered_rootattr
.va_modify_time
.tv_sec
;
239 args
->a_vap
->va_modify_time
.tv_nsec
= covered_rootattr
.va_modify_time
.tv_nsec
;
241 if (VATTR_IS_ACTIVE(args
->a_vap
, va_access_time
)) {
242 VATTR_SET_SUPPORTED(args
->a_vap
, va_access_time
);
243 args
->a_vap
->va_modify_time
.tv_sec
= covered_rootattr
.va_access_time
.tv_sec
;
244 args
->a_vap
->va_modify_time
.tv_nsec
= covered_rootattr
.va_access_time
.tv_nsec
;
247 nullfs_cleanup_patched_context(null_mp
, ectx
);
252 nullfs_getattr(struct vnop_getattr_args
* args
)
255 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(args
->a_vp
));
256 kauth_cred_t cred
= vfs_context_ucred(args
->a_context
);
257 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
259 lck_mtx_lock(&null_mp
->nullm_lock
);
260 if (nullfs_isspecialvp(args
->a_vp
)) {
261 error
= nullfs_special_getattr(args
);
262 lck_mtx_unlock(&null_mp
->nullm_lock
);
265 lck_mtx_unlock(&null_mp
->nullm_lock
);
267 /* this will return a different inode for third than read dir will */
268 struct vnode
* lowervp
= NULLVPTOLOWERVP(args
->a_vp
);
269 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
270 error
= vnode_getwithref(lowervp
);
273 error
= VNOP_GETATTR(lowervp
, args
->a_vap
, ectx
);
277 /* fix up fsid so it doesn't say the underlying fs*/
278 if (VATTR_IS_ACTIVE(args
->a_vap
, va_fsid
)) {
279 VATTR_RETURN(args
->a_vap
, va_fsid
, vfs_statfs(vnode_mount(args
->a_vp
))->f_fsid
.val
[0]);
281 if (VATTR_IS_ACTIVE(args
->a_vap
, va_fsid64
)) {
282 VATTR_RETURN(args
->a_vap
, va_fsid64
, vfs_statfs(vnode_mount(args
->a_vp
))->f_fsid
);
285 /* Conjure up permissions */
286 if ((null_mp
->nullm_flags
& NULLM_UNVEIL
) == NULLM_UNVEIL
) {
287 if (VATTR_IS_ACTIVE(args
->a_vap
, va_mode
)) {
288 mode_t mode
= args
->a_vap
->va_mode
; // We will take away permisions if we don't have them
290 // Check for authorizations
292 if (vnode_authorize(lowervp
, NULL
, KAUTH_VNODE_GENERIC_READ_BITS
, ectx
) == 0) {
299 // Directories need an execute bit...
300 if (vnode_authorize(lowervp
, NULL
, KAUTH_VNODE_GENERIC_EXECUTE_BITS
, ectx
) == 0) {
306 NULLFSDEBUG("Settings bits to %d\n", mode
);
307 VATTR_RETURN(args
->a_vap
, va_mode
, mode
);
309 if (VATTR_IS_ACTIVE(args
->a_vap
, va_uid
)) {
310 VATTR_RETURN(args
->a_vap
, va_uid
, kauth_cred_getuid(cred
));
312 if (VATTR_IS_ACTIVE(args
->a_vap
, va_gid
)) {
313 VATTR_RETURN(args
->a_vap
, va_gid
, kauth_cred_getgid(cred
));
319 nullfs_cleanup_patched_context(null_mp
, ectx
);
324 nullfs_open(struct vnop_open_args
* args
)
327 struct vnode
*vp
, *lvp
;
328 mount_t mp
= vnode_mount(args
->a_vp
);
329 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
330 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
332 if (nullfs_checkspecialvp(args
->a_vp
)) {
333 return 0; /* nothing extra needed */
336 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
338 lvp
= NULLVPTOLOWERVP(vp
);
339 error
= vnode_getwithref(lvp
);
341 error
= VNOP_OPEN(lvp
, args
->a_mode
, ectx
);
345 nullfs_cleanup_patched_context(null_mp
, ectx
);
350 nullfs_close(struct vnop_close_args
* args
)
353 struct vnode
*vp
, *lvp
;
354 mount_t mp
= vnode_mount(args
->a_vp
);
355 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
357 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
359 if (nullfs_checkspecialvp(args
->a_vp
)) {
360 return 0; /* nothing extra needed */
363 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
365 lvp
= NULLVPTOLOWERVP(vp
);
367 error
= vnode_getwithref(lvp
);
369 error
= VNOP_CLOSE(lvp
, args
->a_fflag
, ectx
);
373 nullfs_cleanup_patched_context(null_mp
, ectx
);
377 /* get lvp's parent, if possible, even if it isn't set.
379 * lvp is expected to have an iocount before and after this call.
381 * if a dvpp is populated the returned vnode has an iocount. */
383 null_get_lowerparent(vnode_t lvp
, vnode_t
* dvpp
, vfs_context_t ctx
)
386 struct vnode_attr va
;
387 mount_t mp
= vnode_mount(lvp
);
388 vnode_t dvp
= vnode_parent(lvp
);
391 error
= vnode_get(dvp
);
396 if (!(mp
->mnt_kern_flag
& MNTK_PATH_FROM_ID
)) {
401 VATTR_WANTED(&va
, va_parentid
);
403 error
= vnode_getattr(lvp
, &va
, ctx
);
405 if (error
|| !VATTR_IS_SUPPORTED(&va
, va_parentid
)) {
409 error
= VFS_VGET(mp
, (ino64_t
)va
.va_parentid
, &dvp
, ctx
);
418 /* the mountpoint lock should be held going into this function */
420 null_special_lookup(struct vnop_lookup_args
* ap
)
422 struct componentname
* cnp
= ap
->a_cnp
;
423 struct vnode
* dvp
= ap
->a_dvp
;
424 struct vnode
* ldvp
= NULL
;
425 struct vnode
* lvp
= NULL
;
426 struct vnode
* vp
= NULL
;
427 struct mount
* mp
= vnode_mount(dvp
);
428 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(mp
);
430 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, ap
->a_context
);
432 if (dvp
== null_mp
->nullm_rootvp
) {
433 /* handle . and .. */
434 if (cnp
->cn_nameptr
[0] == '.') {
435 if (cnp
->cn_namelen
== 1 || (cnp
->cn_namelen
== 2 && cnp
->cn_nameptr
[1] == '.')) {
436 /* this is the root so both . and .. give back the root */
438 error
= vnode_get(vp
);
443 /* our virtual wrapper directory should be d but D is acceptable if the
444 * lower file system is case insensitive */
445 if (cnp
->cn_namelen
== 1 &&
446 (cnp
->cn_nameptr
[0] == 'd' || (null_mp
->nullm_flags
& NULLM_CASEINSENSITIVE
? cnp
->cn_nameptr
[0] == 'D' : 0))) {
448 if (null_mp
->nullm_secondvp
== NULL
) {
449 error
= null_getnewvnode(mp
, NULL
, dvp
, &vp
, cnp
, 0);
454 null_mp
->nullm_secondvp
= vp
;
456 vp
= null_mp
->nullm_secondvp
;
457 error
= vnode_get(vp
);
460 } else if (dvp
== null_mp
->nullm_secondvp
) {
461 /* handle . and .. */
462 if (cnp
->cn_nameptr
[0] == '.') {
463 if (cnp
->cn_namelen
== 1) {
465 error
= vnode_get(vp
);
467 } else if (cnp
->cn_namelen
== 2 && cnp
->cn_nameptr
[1] == '.') {
468 /* parent here is the root vp */
469 vp
= null_mp
->nullm_rootvp
;
470 error
= vnode_get(vp
);
474 /* nullmp->nullm_lowerrootvp was set at mount time so don't need to lock to
476 /* v_name should be null terminated but cn_nameptr is not necessarily.
477 * cn_namelen is the number of characters before the null in either case */
478 error
= vnode_getwithvid(null_mp
->nullm_lowerrootvp
, null_mp
->nullm_lowerrootvid
);
483 /* We don't want to mess with case insensitivity and unicode, so the plan to
485 * 1. try to get the lower root's parent
486 * 2. If we get a parent, then perform a lookup on the lower file system
487 * using the parent and the passed in cnp
488 * 3. If that worked and we got a vp, then see if the vp is lowerrootvp. If
490 * 4. Anything else results in ENOENT.
492 error
= null_get_lowerparent(null_mp
->nullm_lowerrootvp
, &ldvp
, ectx
);
495 error
= VNOP_LOOKUP(ldvp
, &lvp
, cnp
, ectx
);
499 if (lvp
== null_mp
->nullm_lowerrootvp
) {
500 /* always check the hashmap for a vnode for this, the root of the
502 error
= null_nodeget(mp
, lvp
, dvp
, &vp
, cnp
, 0);
504 if (error
== 0 && null_mp
->nullm_thirdcovervp
== NULL
) {
505 /* if nodeget succeeded then vp has an iocount*/
506 null_mp
->nullm_thirdcovervp
= vp
;
514 vnode_put(null_mp
->nullm_lowerrootvp
);
518 nullfs_cleanup_patched_context(null_mp
, ectx
);
526 * We have to carry on the locking protocol on the null layer vnodes
527 * as we progress through the tree. We also have to enforce read-only
528 * if this layer is mounted read-only.
531 null_lookup(struct vnop_lookup_args
* ap
)
533 struct componentname
* cnp
= ap
->a_cnp
;
534 struct vnode
* dvp
= ap
->a_dvp
;
535 struct vnode
*vp
, *ldvp
, *lvp
;
537 struct null_mount
* null_mp
;
541 NULLFSDEBUG("%s parent: %p component: %.*s\n", __FUNCTION__
, ap
->a_dvp
, cnp
->cn_namelen
, cnp
->cn_nameptr
);
543 mp
= vnode_mount(dvp
);
544 /* rename and delete are not allowed. this is a read only file system */
545 if (cnp
->cn_nameiop
== DELETE
|| cnp
->cn_nameiop
== RENAME
|| cnp
->cn_nameiop
== CREATE
) {
548 null_mp
= MOUNTTONULLMOUNT(mp
);
551 lck_mtx_lock(&null_mp
->nullm_lock
);
552 if (nullfs_isspecialvp(dvp
)) {
553 error
= null_special_lookup(ap
);
554 lck_mtx_unlock(&null_mp
->nullm_lock
);
557 lck_mtx_unlock(&null_mp
->nullm_lock
);
560 if (cnp
->cn_nameptr
[0] == '.') {
561 if (cnp
->cn_namelen
== 1) {
563 } else if (cnp
->cn_namelen
== 2 && cnp
->cn_nameptr
[1] == '.') {
564 /* mount point crossing is handled in null_special_lookup */
565 vp
= vnode_parent(dvp
);
570 error
= vp
? vnode_get(vp
) : ENOENT
;
580 ectx
= nullfs_get_patched_context(null_mp
, ap
->a_context
);
581 ldvp
= NULLVPTOLOWERVP(dvp
);
585 * Hold ldvp. The reference on it, owned by dvp, is lost in
586 * case of dvp reclamation.
588 error
= vnode_getwithref(ldvp
);
590 nullfs_cleanup_patched_context(null_mp
, ectx
);
594 error
= VNOP_LOOKUP(ldvp
, &lvp
, cnp
, ectx
);
598 if ((error
== 0 || error
== EJUSTRETURN
) && lvp
!= NULL
) {
601 error
= vnode_get(vp
);
603 error
= null_nodeget(mp
, lvp
, dvp
, &vp
, cnp
, 0);
610 /* if we got lvp, drop the iocount from VNOP_LOOKUP */
615 nullfs_cleanup_patched_context(null_mp
, ectx
);
620 * Don't think this needs to do anything
623 null_inactive(__unused
struct vnop_inactive_args
* ap
)
625 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
631 null_reclaim(struct vnop_reclaim_args
* ap
)
634 struct null_node
* xp
;
635 struct vnode
* lowervp
;
636 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(ap
->a_vp
));
638 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
643 lowervp
= xp
->null_lowervp
;
645 lck_mtx_lock(&null_mp
->nullm_lock
);
647 vnode_removefsref(vp
);
649 if (lowervp
!= NULL
) {
650 /* root and second don't have a lowervp, so nothing to release and nothing
652 if (xp
->null_flags
& NULL_FLAG_HASHED
) {
653 /* only call this if we actually made it into the hash list. reclaim gets
655 * clean up a vnode that got created when it didn't need to under race
659 vnode_getwithref(lowervp
);
664 if (vp
== null_mp
->nullm_rootvp
) {
665 null_mp
->nullm_rootvp
= NULL
;
666 } else if (vp
== null_mp
->nullm_secondvp
) {
667 null_mp
->nullm_secondvp
= NULL
;
668 } else if (vp
== null_mp
->nullm_thirdcovervp
) {
669 null_mp
->nullm_thirdcovervp
= NULL
;
672 lck_mtx_unlock(&null_mp
->nullm_lock
);
675 vnode_clearfsnode(vp
);
682 #define DIRENT_SZ(dp) ((sizeof(struct dirent) - NAME_MAX) + (((dp)->d_namlen + 1 + 3) & ~3))
685 store_entry_special(ino_t ino
, const char * name
, struct uio
* uio
)
688 size_t namelen
= strlen(name
);
691 if (namelen
+ 1 <= NAME_MAX
) {
692 memset(&e
, 0, sizeof(e
));
697 e
.d_namlen
= namelen
; /* don't include NUL */
698 e
.d_reclen
= DIRENT_SZ(&e
);
699 if (uio_resid(uio
) >= e
.d_reclen
) {
700 strlcpy(e
.d_name
, name
, NAME_MAX
);
701 error
= uiomove((caddr_t
)&e
, e
.d_reclen
, uio
);
710 nullfs_special_readdir(struct vnop_readdir_args
* ap
)
712 struct vnode
* vp
= ap
->a_vp
;
713 struct uio
* uio
= ap
->a_uio
;
714 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(vp
));
715 off_t offset
= uio_offset(uio
);
719 const char * name
= NULL
;
721 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
)) {
727 if (vp
== null_mp
->nullm_rootvp
) {
729 } else { /* only get here if vp matches nullm_rootvp or nullm_secondvp */
730 ino
= NULL_SECOND_INO
;
732 error
= store_entry_special(ino
, ".", uio
);
741 /* only get here if vp matches nullm_rootvp or nullm_secondvp */
744 error
= store_entry_special(ino
, "..", uio
);
752 /* the directory case */
753 if (vp
== null_mp
->nullm_rootvp
) {
754 ino
= NULL_SECOND_INO
;
756 } else { /* only get here if vp matches nullm_rootvp or nullm_secondvp */
757 ino
= NULL_THIRD_INO
;
758 if (vnode_getwithvid(null_mp
->nullm_lowerrootvp
, null_mp
->nullm_lowerrootvid
)) {
759 /* In this case the lower file system has been ripped out from under us,
760 * but we don't want to error out
761 * Instead we just want d to look empty. */
765 name
= vnode_getname_printable(null_mp
->nullm_lowerrootvp
);
767 error
= store_entry_special(ino
, name
, uio
);
769 if (ino
== NULL_THIRD_INO
) {
770 vnode_putname_printable(name
);
771 vnode_put(null_mp
->nullm_lowerrootvp
);
782 if (error
== EMSGSIZE
) {
783 error
= 0; /* return success if we ran out of space, but we wanted to make
784 * sure that we didn't update offset and items incorrectly */
786 uio_setoffset(uio
, offset
);
787 if (ap
->a_numdirent
) {
788 *ap
->a_numdirent
= items
;
794 nullfs_readdir(struct vnop_readdir_args
* ap
)
796 struct vnode
*vp
, *lvp
;
798 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(ap
->a_vp
));
800 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
801 /* assumption is that any vp that comes through here had to go through lookup
804 lck_mtx_lock(&null_mp
->nullm_lock
);
805 if (nullfs_isspecialvp(ap
->a_vp
)) {
806 error
= nullfs_special_readdir(ap
);
807 lck_mtx_unlock(&null_mp
->nullm_lock
);
810 lck_mtx_unlock(&null_mp
->nullm_lock
);
812 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, ap
->a_context
);
814 lvp
= NULLVPTOLOWERVP(vp
);
815 error
= vnode_getwithref(lvp
);
817 error
= VNOP_READDIR(lvp
, ap
->a_uio
, ap
->a_flags
, ap
->a_eofflag
, ap
->a_numdirent
, ectx
);
821 nullfs_cleanup_patched_context(null_mp
, ectx
);
826 nullfs_readlink(struct vnop_readlink_args
* ap
)
828 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
830 struct vnode
*vp
, *lvp
;
831 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(ap
->a_vp
));
833 if (nullfs_checkspecialvp(ap
->a_vp
)) {
834 return ENOTSUP
; /* the special vnodes aren't links */
837 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, ap
->a_context
);
839 lvp
= NULLVPTOLOWERVP(vp
);
841 error
= vnode_getwithref(lvp
);
843 error
= VNOP_READLINK(lvp
, ap
->a_uio
, ectx
);
847 NULLFSDEBUG("readlink failed: %d\n", error
);
851 nullfs_cleanup_patched_context(null_mp
, ectx
);
856 nullfs_pathconf(__unused
struct vnop_pathconf_args
* args
)
858 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
863 nullfs_fsync(__unused
struct vnop_fsync_args
* args
)
865 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
870 nullfs_mmap(struct vnop_mmap_args
* args
)
873 struct vnode
*vp
, *lvp
;
874 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(args
->a_vp
));
876 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
878 if (nullfs_checkspecialvp(args
->a_vp
)) {
879 return 0; /* nothing extra needed */
882 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
884 lvp
= NULLVPTOLOWERVP(vp
);
885 error
= vnode_getwithref(lvp
);
887 error
= VNOP_MMAP(lvp
, args
->a_fflags
, ectx
);
891 nullfs_cleanup_patched_context(null_mp
, ectx
);
896 nullfs_mnomap(struct vnop_mnomap_args
* args
)
899 struct vnode
*vp
, *lvp
;
900 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(args
->a_vp
));
902 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
904 if (nullfs_checkspecialvp(args
->a_vp
)) {
905 return 0; /* nothing extra needed */
908 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
910 lvp
= NULLVPTOLOWERVP(vp
);
911 error
= vnode_getwithref(lvp
);
913 error
= VNOP_MNOMAP(lvp
, ectx
);
917 nullfs_cleanup_patched_context(null_mp
, ectx
);
922 nullfs_getxattr(struct vnop_getxattr_args
* args
)
925 struct vnode
*vp
, *lvp
;
926 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(args
->a_vp
));
928 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
930 if (nullfs_checkspecialvp(args
->a_vp
)) {
931 return ENOATTR
; /* no xattrs on the special vnodes */
934 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
936 lvp
= NULLVPTOLOWERVP(vp
);
937 error
= vnode_getwithref(lvp
);
939 error
= VNOP_GETXATTR(lvp
, args
->a_name
, args
->a_uio
, args
->a_size
, args
->a_options
, ectx
);
943 nullfs_cleanup_patched_context(null_mp
, ectx
);
948 nullfs_listxattr(struct vnop_listxattr_args
* args
)
951 struct vnode
*vp
, *lvp
;
952 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(args
->a_vp
));
954 NULLFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
956 if (nullfs_checkspecialvp(args
->a_vp
)) {
957 return 0; /* no xattrs on the special vnodes */
960 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, args
->a_context
);
962 lvp
= NULLVPTOLOWERVP(vp
);
963 error
= vnode_getwithref(lvp
);
965 error
= VNOP_LISTXATTR(lvp
, args
->a_uio
, args
->a_size
, args
->a_options
, ectx
);
969 nullfs_cleanup_patched_context(null_mp
, ectx
);
973 /* relies on v1 paging */
975 nullfs_pagein(struct vnop_pagein_args
* ap
)
978 struct vnode
*vp
, *lvp
;
979 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(ap
->a_vp
));
980 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
983 lvp
= NULLVPTOLOWERVP(vp
);
985 if (vnode_vtype(vp
) != VREG
) {
989 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, ap
->a_context
);
991 * Ask VM/UBC/VFS to do our bidding
993 if (vnode_getwithvid(lvp
, NULLVPTOLOWERVID(vp
)) == 0) {
997 off_t bytes_to_commit
;
999 upl_t upl
= ap
->a_pl
;
1000 user_ssize_t bytes_remaining
= 0;
1002 auio
= uio_create(1, ap
->a_f_offset
, UIO_SYSSPACE
, UIO_READ
);
1008 kret
= ubc_upl_map(upl
, &ioaddr
);
1009 if (KERN_SUCCESS
!= kret
) {
1010 panic("nullfs_pagein: ubc_upl_map() failed with (%d)", kret
);
1013 ioaddr
+= ap
->a_pl_offset
;
1015 error
= uio_addiov(auio
, (user_addr_t
)ioaddr
, ap
->a_size
);
1020 lowersize
= ubc_getsize(lvp
);
1021 if (lowersize
!= ubc_getsize(vp
)) {
1022 (void)ubc_setsize(vp
, lowersize
); /* ignore failures, nothing can be done */
1025 error
= VNOP_READ(lvp
, auio
, ((ap
->a_flags
& UPL_IOSYNC
) ? IO_SYNC
: 0), ectx
);
1027 bytes_remaining
= uio_resid(auio
);
1028 if (bytes_remaining
> 0 && bytes_remaining
<= (user_ssize_t
)ap
->a_size
) {
1029 /* zero bytes that weren't read in to the upl */
1030 bzero((void*)((uintptr_t)(ioaddr
+ ap
->a_size
- bytes_remaining
)), (size_t) bytes_remaining
);
1034 kret
= ubc_upl_unmap(upl
);
1035 if (KERN_SUCCESS
!= kret
) {
1036 panic("nullfs_pagein: ubc_upl_unmap() failed with (%d)", kret
);
1044 if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
1045 if (!error
&& (bytes_remaining
>= 0) && (bytes_remaining
<= (user_ssize_t
)ap
->a_size
)) {
1046 /* only commit what was read in (page aligned)*/
1047 bytes_to_commit
= ap
->a_size
- bytes_remaining
;
1048 if (bytes_to_commit
) {
1049 /* need to make sure bytes_to_commit and byte_remaining are page aligned before calling ubc_upl_commit_range*/
1050 if (bytes_to_commit
& PAGE_MASK
) {
1051 bytes_to_commit
= (bytes_to_commit
& (~PAGE_MASK
)) + (PAGE_MASK
+ 1);
1052 assert(bytes_to_commit
<= (off_t
)ap
->a_size
);
1054 bytes_remaining
= ap
->a_size
- bytes_to_commit
;
1056 ubc_upl_commit_range(upl
, ap
->a_pl_offset
, (upl_size_t
)bytes_to_commit
, UPL_COMMIT_FREE_ON_EMPTY
);
1059 /* abort anything thats left */
1060 if (bytes_remaining
) {
1061 ubc_upl_abort_range(upl
, ap
->a_pl_offset
+ bytes_to_commit
, (upl_size_t
)bytes_remaining
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
1064 ubc_upl_abort_range(upl
, ap
->a_pl_offset
, (upl_size_t
)ap
->a_size
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
1068 } else if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
1069 ubc_upl_abort_range(ap
->a_pl
, ap
->a_pl_offset
, (upl_size_t
)ap
->a_size
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
1072 nullfs_cleanup_patched_context(null_mp
, ectx
);
1077 nullfs_read(struct vnop_read_args
* ap
)
1081 struct vnode
*vp
, *lvp
;
1082 struct null_mount
* null_mp
= MOUNTTONULLMOUNT(vnode_mount(ap
->a_vp
));
1083 NULLFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
1085 if (nullfs_checkspecialvp(ap
->a_vp
)) {
1086 return ENOTSUP
; /* the special vnodes can't be read */
1089 vfs_context_t ectx
= nullfs_get_patched_context(null_mp
, ap
->a_context
);
1091 lvp
= NULLVPTOLOWERVP(vp
);
1094 * First some house keeping
1096 if (vnode_getwithvid(lvp
, NULLVPTOLOWERVID(vp
)) == 0) {
1097 if (!vnode_isreg(lvp
) && !vnode_islnk(lvp
)) {
1102 if (uio_resid(ap
->a_uio
) == 0) {
1108 * Now ask VM/UBC/VFS to do our bidding
1111 error
= VNOP_READ(lvp
, ap
->a_uio
, ap
->a_ioflag
, ectx
);
1113 NULLFSDEBUG("VNOP_READ failed: %d\n", error
);
1119 nullfs_cleanup_patched_context(null_mp
, ectx
);
1124 * Global vfs data structures
1127 static const struct vnodeopv_entry_desc nullfs_vnodeop_entries
[] = {
1128 {.opve_op
= &vnop_default_desc
, .opve_impl
= (vop_t
)nullfs_default
}, {.opve_op
= &vnop_getattr_desc
, .opve_impl
= (vop_t
)nullfs_getattr
},
1129 {.opve_op
= &vnop_open_desc
, .opve_impl
= (vop_t
)nullfs_open
}, {.opve_op
= &vnop_close_desc
, .opve_impl
= (vop_t
)nullfs_close
},
1130 {.opve_op
= &vnop_inactive_desc
, .opve_impl
= (vop_t
)null_inactive
}, {.opve_op
= &vnop_reclaim_desc
, .opve_impl
= (vop_t
)null_reclaim
},
1131 {.opve_op
= &vnop_lookup_desc
, .opve_impl
= (vop_t
)null_lookup
}, {.opve_op
= &vnop_readdir_desc
, .opve_impl
= (vop_t
)nullfs_readdir
},
1132 {.opve_op
= &vnop_readlink_desc
, .opve_impl
= (vop_t
)nullfs_readlink
}, {.opve_op
= &vnop_pathconf_desc
, .opve_impl
= (vop_t
)nullfs_pathconf
},
1133 {.opve_op
= &vnop_fsync_desc
, .opve_impl
= (vop_t
)nullfs_fsync
}, {.opve_op
= &vnop_mmap_desc
, .opve_impl
= (vop_t
)nullfs_mmap
},
1134 {.opve_op
= &vnop_mnomap_desc
, .opve_impl
= (vop_t
)nullfs_mnomap
}, {.opve_op
= &vnop_getxattr_desc
, .opve_impl
= (vop_t
)nullfs_getxattr
},
1135 {.opve_op
= &vnop_pagein_desc
, .opve_impl
= (vop_t
)nullfs_pagein
}, {.opve_op
= &vnop_read_desc
, .opve_impl
= (vop_t
)nullfs_read
},
1136 {.opve_op
= &vnop_listxattr_desc
, .opve_impl
= (vop_t
)nullfs_listxattr
}, {.opve_op
= NULL
, .opve_impl
= NULL
},
1139 const struct vnodeopv_desc nullfs_vnodeop_opv_desc
= {.opv_desc_vector_p
= &nullfs_vnodeop_p
, .opv_desc_ops
= nullfs_vnodeop_entries
};
1141 //NULLFS Specific helper function
1144 nullfs_getbackingvnode(vnode_t in_vp
, vnode_t
* out_vpp
)
1146 int result
= EINVAL
;
1148 if (out_vpp
== NULL
|| in_vp
== NULL
) {
1152 struct vfsstatfs
* sp
= NULL
;
1153 mount_t mp
= vnode_mount(in_vp
);
1155 sp
= vfs_statfs(mp
);
1156 //If this isn't a nullfs vnode or it is but it's a special vnode
1157 if (strcmp(sp
->f_fstypename
, "nullfs") != 0 || nullfs_checkspecialvp(in_vp
)) {
1163 vnode_t lvp
= NULLVPTOLOWERVP(in_vp
);
1164 if ((result
= vnode_getwithvid(lvp
, NULLVPTOLOWERVID(in_vp
)))) {