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/vnode_internal.h>
77 #include <sys/xattr.h>
79 #include <sys/types.h>
80 #include <sys/dirent.h>
84 #define BIND_ROOT_INO 2
86 vop_t
* bindfs_vnodeop_p
= NULL
;
89 bindfs_default(__unused
struct vnop_generic_args
* args
)
95 bindfs_getattr(struct vnop_getattr_args
* args
)
98 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
100 struct vnode
* lowervp
= BINDVPTOLOWERVP(args
->a_vp
);
102 error
= vnode_getwithref(lowervp
);
104 error
= VNOP_GETATTR(lowervp
, args
->a_vap
, args
->a_context
);
108 if (VATTR_IS_ACTIVE(args
->a_vap
, va_fsid
)) {
109 /* fix up fsid so it doesn't say the underlying fs*/
110 VATTR_RETURN(args
->a_vap
, va_fsid
, vfs_statfs(vnode_mount(args
->a_vp
))->f_fsid
.val
[0]);
112 if (VATTR_IS_ACTIVE(args
->a_vap
, va_fsid64
)) {
113 /* fix up fsid so it doesn't say the underlying fs*/
114 VATTR_RETURN(args
->a_vap
, va_fsid64
, vfs_statfs(vnode_mount(args
->a_vp
))->f_fsid
);
116 struct vnode
* parent
= vnode_parent(args
->a_vp
);
117 if (vnode_isvroot(args
->a_vp
)) {
118 // We can use the lower answers for most questions about the root vnode but need to fix up a few things
119 if (VATTR_IS_ACTIVE(args
->a_vap
, va_fileid
)) {
120 VATTR_RETURN(args
->a_vap
, va_fileid
, BIND_ROOT_INO
);
122 if (VATTR_IS_ACTIVE(args
->a_vap
, va_linkid
)) {
123 VATTR_RETURN(args
->a_vap
, va_linkid
, BIND_ROOT_INO
);
125 if (VATTR_IS_ACTIVE(args
->a_vap
, va_parentid
)) {
126 // The parent of the root is itself
127 VATTR_RETURN(args
->a_vap
, va_parentid
, BIND_ROOT_INO
);
129 } else if (parent
!= NULL
&& vnode_isvroot(parent
)) {
130 if (VATTR_IS_ACTIVE(args
->a_vap
, va_parentid
)) {
131 // This vnode's parent is the root.
132 VATTR_RETURN(args
->a_vap
, va_parentid
, BIND_ROOT_INO
);
142 bindfs_open(struct vnop_open_args
* args
)
145 struct vnode
*vp
, *lvp
;
147 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
150 lvp
= BINDVPTOLOWERVP(vp
);
151 error
= vnode_getwithref(lvp
);
153 error
= VNOP_OPEN(lvp
, args
->a_mode
, args
->a_context
);
161 bindfs_close(struct vnop_close_args
* args
)
164 struct vnode
*vp
, *lvp
;
166 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
169 lvp
= BINDVPTOLOWERVP(vp
);
171 error
= vnode_getwithref(lvp
);
173 error
= VNOP_CLOSE(lvp
, args
->a_fflag
, args
->a_context
);
180 * We have to carry on the locking protocol on the bind layer vnodes
181 * as we progress through the tree. We also have to enforce read-only
182 * if this layer is mounted read-only.
185 bind_lookup(struct vnop_lookup_args
* ap
)
187 struct componentname
* cnp
= ap
->a_cnp
;
188 struct vnode
* dvp
= ap
->a_dvp
;
189 struct vnode
*vp
, *ldvp
, *lvp
;
191 struct bind_mount
* bind_mp
;
194 BINDFSDEBUG("%s parent: %p component: %.*s\n", __FUNCTION__
, ap
->a_dvp
, cnp
->cn_namelen
, cnp
->cn_nameptr
);
196 mp
= vnode_mount(dvp
);
197 /* rename and delete are not allowed. this is a read only file system */
198 if (cnp
->cn_nameiop
== DELETE
|| cnp
->cn_nameiop
== RENAME
|| cnp
->cn_nameiop
== CREATE
) {
201 bind_mp
= MOUNTTOBINDMOUNT(mp
);
204 if (cnp
->cn_nameptr
[0] == '.') {
205 if (cnp
->cn_namelen
== 1) {
207 } else if (cnp
->cn_namelen
== 2 && cnp
->cn_nameptr
[1] == '.') {
208 vp
= (vnode_isvroot(dvp
)) ? dvp
: vnode_parent(dvp
);
213 error
= vp
? vnode_get(vp
) : ENOENT
;
223 ldvp
= BINDVPTOLOWERVP(dvp
);
227 * Hold ldvp. The reference on it, owned by dvp, is lost in
228 * case of dvp reclamation.
230 error
= vnode_getwithref(ldvp
);
235 error
= VNOP_LOOKUP(ldvp
, &lvp
, cnp
, ap
->a_context
);
239 if ((error
== 0 || error
== EJUSTRETURN
) && lvp
!= NULL
) {
242 error
= vnode_get(vp
);
244 error
= bind_nodeget(mp
, lvp
, dvp
, &vp
, cnp
, 0);
251 /* if we got lvp, drop the iocount from VNOP_LOOKUP */
260 * Don't think this needs to do anything
263 bind_inactive(__unused
struct vnop_inactive_args
* ap
)
265 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
271 bind_reclaim(struct vnop_reclaim_args
* ap
)
274 struct bind_node
* xp
;
275 struct vnode
* lowervp
;
277 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
282 lowervp
= xp
->bind_lowervp
;
284 vnode_removefsref(vp
);
287 vnode_getwithref(lowervp
);
292 vnode_clearfsnode(vp
);
299 /* Get dirent length padded to 4 byte alignment */
300 #define DIRENT_LEN(namelen) \
301 ((sizeof(struct dirent) + (namelen + 1) - (__DARWIN_MAXNAMLEN + 1) + 3) & ~3)
303 /* Get the end of this dirent */
304 #define DIRENT_END(dep) \
305 (((char *)(dep)) + (dep)->d_reclen - 1)
308 bindfs_readdir(struct vnop_readdir_args
* ap
)
310 struct vnode
*vp
, *lvp
, *dvp
;
312 uio_t uio
= ap
->a_uio
;
314 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
315 /* assumption is that any vp that comes through here had to go through lookup
318 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
)) {
323 dvp
= vnode_parent(vp
);
324 lvp
= BINDVPTOLOWERVP(vp
);
325 error
= vnode_getwithref(lvp
);
330 if (vnode_isvroot(vp
) || (dvp
!= NULL
&& vnode_isvroot(dvp
))) {
336 bufsize
= 3 * MIN((user_size_t
)uio_resid(uio
), 87371u) / 8;
337 MALLOC(bufptr
, void *, bufsize
, M_TEMP
, M_WAITOK
);
338 if (bufptr
== NULL
) {
341 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
342 uio_addiov(auio
, (uintptr_t)bufptr
, bufsize
);
343 uio_setoffset(auio
, uio_offset(uio
));
344 error
= VNOP_READDIR(lvp
, auio
, ap
->a_flags
, ap
->a_eofflag
, ap
->a_numdirent
, ap
->a_context
);
350 dep
= (struct dirent
*)bufptr
;
351 bytesread
= bufsize
- uio_resid(auio
);
352 while (error
== 0 && (char *)dep
< ((char *)bufptr
+ bytesread
)) {
353 if (DIRENT_END(dep
) > ((char *)bufptr
+ bytesread
) ||
354 DIRENT_LEN(dep
->d_namlen
) > dep
->d_reclen
) {
355 printf("%s: %s: Bad dirent received from directory %s\n", __func__
,
356 vfs_statfs(vnode_mount(vp
))->f_mntonname
,
357 vp
->v_name
? vp
->v_name
: "<unknown>");
361 if (dep
->d_name
[0] == '.') {
362 /* re-write the inode number for the mount root */
363 /* if vp is the mount root then . = 2 and .. = 2 */
364 /* if the parent of vp is the mount root then .. = 2 */
365 if ((vnode_isvroot(vp
) && dep
->d_namlen
== 1) ||
366 (dep
->d_namlen
== 2 && dep
->d_name
[1] == '.')) {
367 dep
->d_ino
= BIND_ROOT_INO
;
370 /* Copy entry64 to user's buffer. */
371 error
= uiomove((caddr_t
)dep
, dep
->d_reclen
, uio
);
372 /* Move to next entry. */
373 dep
= (struct dirent
*)((char *)dep
+ dep
->d_reclen
);
375 /* Update the real offset using the offset we got from VNOP_READDIR. */
377 uio_setoffset(uio
, uio_offset(auio
));
380 FREE(bufptr
, M_TEMP
);
382 error
= VNOP_READDIR(lvp
, ap
->a_uio
, ap
->a_flags
, ap
->a_eofflag
, ap
->a_numdirent
, ap
->a_context
);
391 bindfs_readlink(struct vnop_readlink_args
* ap
)
393 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
395 struct vnode
*vp
, *lvp
;
398 lvp
= BINDVPTOLOWERVP(vp
);
400 error
= vnode_getwithref(lvp
);
402 error
= VNOP_READLINK(lvp
, ap
->a_uio
, ap
->a_context
);
406 printf("bindfs: readlink failed: %d\n", error
);
414 bindfs_pathconf(__unused
struct vnop_pathconf_args
* args
)
416 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
421 bindfs_fsync(__unused
struct vnop_fsync_args
* args
)
423 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
428 bindfs_mmap(struct vnop_mmap_args
* args
)
431 struct vnode
*vp
, *lvp
;
433 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
436 lvp
= BINDVPTOLOWERVP(vp
);
437 error
= vnode_getwithref(lvp
);
439 error
= VNOP_MMAP(lvp
, args
->a_fflags
, args
->a_context
);
447 bindfs_mnomap(struct vnop_mnomap_args
* args
)
450 struct vnode
*vp
, *lvp
;
452 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
455 lvp
= BINDVPTOLOWERVP(vp
);
456 error
= vnode_getwithref(lvp
);
458 error
= VNOP_MNOMAP(lvp
, args
->a_context
);
466 bindfs_getxattr(struct vnop_getxattr_args
* args
)
469 struct vnode
*vp
, *lvp
;
471 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
474 lvp
= BINDVPTOLOWERVP(vp
);
475 error
= vnode_getwithref(lvp
);
477 error
= VNOP_GETXATTR(lvp
, args
->a_name
, args
->a_uio
, args
->a_size
, args
->a_options
, args
->a_context
);
485 bindfs_listxattr(struct vnop_listxattr_args
* args
)
488 struct vnode
*vp
, *lvp
;
490 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
493 lvp
= BINDVPTOLOWERVP(vp
);
494 error
= vnode_getwithref(lvp
);
496 error
= VNOP_LISTXATTR(lvp
, args
->a_uio
, args
->a_size
, args
->a_options
, args
->a_context
);
503 /* relies on v1 paging */
505 bindfs_pagein(struct vnop_pagein_args
* ap
)
508 struct vnode
*vp
, *lvp
;
510 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
513 lvp
= BINDVPTOLOWERVP(vp
);
515 if (vnode_vtype(vp
) != VREG
) {
520 * Ask VM/UBC/VFS to do our bidding
522 if (vnode_getwithvid(lvp
, BINDVPTOLOWERVID(vp
)) == 0) {
526 off_t bytes_to_commit
;
528 upl_t upl
= ap
->a_pl
;
529 user_ssize_t bytes_remaining
= 0;
531 auio
= uio_create(1, ap
->a_f_offset
, UIO_SYSSPACE
, UIO_READ
);
537 kret
= ubc_upl_map(upl
, &ioaddr
);
538 if (KERN_SUCCESS
!= kret
) {
539 panic("bindfs_pagein: ubc_upl_map() failed with (%d)", kret
);
542 ioaddr
+= ap
->a_pl_offset
;
544 error
= uio_addiov(auio
, (user_addr_t
)ioaddr
, ap
->a_size
);
549 lowersize
= ubc_getsize(lvp
);
550 if (lowersize
!= ubc_getsize(vp
)) {
551 (void)ubc_setsize(vp
, lowersize
); /* ignore failures, nothing can be done */
554 error
= VNOP_READ(lvp
, auio
, ((ap
->a_flags
& UPL_IOSYNC
) ? IO_SYNC
: 0), ap
->a_context
);
556 bytes_remaining
= uio_resid(auio
);
557 if (bytes_remaining
> 0 && bytes_remaining
<= (user_ssize_t
)ap
->a_size
) {
558 /* zero bytes that weren't read in to the upl */
559 bzero((void*)((uintptr_t)(ioaddr
+ ap
->a_size
- bytes_remaining
)), (size_t) bytes_remaining
);
563 kret
= ubc_upl_unmap(upl
);
564 if (KERN_SUCCESS
!= kret
) {
565 panic("bindfs_pagein: ubc_upl_unmap() failed with (%d)", kret
);
573 if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
574 if (!error
&& (bytes_remaining
>= 0) && (bytes_remaining
<= (user_ssize_t
)ap
->a_size
)) {
575 /* only commit what was read in (page aligned)*/
576 bytes_to_commit
= ap
->a_size
- bytes_remaining
;
577 if (bytes_to_commit
) {
578 /* need to make sure bytes_to_commit and byte_remaining are page aligned before calling ubc_upl_commit_range*/
579 if (bytes_to_commit
& PAGE_MASK
) {
580 bytes_to_commit
= (bytes_to_commit
& (~PAGE_MASK
)) + (PAGE_MASK
+ 1);
581 assert(bytes_to_commit
<= (off_t
)ap
->a_size
);
583 bytes_remaining
= ap
->a_size
- bytes_to_commit
;
585 ubc_upl_commit_range(upl
, ap
->a_pl_offset
, (upl_size_t
)bytes_to_commit
, UPL_COMMIT_FREE_ON_EMPTY
);
588 /* abort anything thats left */
589 if (bytes_remaining
) {
590 ubc_upl_abort_range(upl
, ap
->a_pl_offset
+ (upl_offset_t
)bytes_to_commit
, (upl_size_t
)bytes_remaining
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
593 ubc_upl_abort_range(upl
, ap
->a_pl_offset
, (upl_size_t
)ap
->a_size
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
597 } else if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
598 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
);
604 bindfs_read(struct vnop_read_args
* ap
)
608 struct vnode
*vp
, *lvp
;
610 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
613 lvp
= BINDVPTOLOWERVP(vp
);
616 * First some house keeping
618 if (vnode_getwithvid(lvp
, BINDVPTOLOWERVID(vp
)) == 0) {
619 if (!vnode_isreg(lvp
) && !vnode_islnk(lvp
)) {
624 if (uio_resid(ap
->a_uio
) == 0) {
630 * Now ask VM/UBC/VFS to do our bidding
633 error
= VNOP_READ(lvp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
635 printf("bindfs: VNOP_READ failed: %d\n", error
);
644 * Global vfs data structures
647 static const struct vnodeopv_entry_desc bindfs_vnodeop_entries
[] = {
648 {.opve_op
= &vnop_default_desc
, .opve_impl
= (vop_t
)bindfs_default
}, /* default */
649 {.opve_op
= &vnop_getattr_desc
, .opve_impl
= (vop_t
)bindfs_getattr
}, /* getattr */
650 {.opve_op
= &vnop_open_desc
, .opve_impl
= (vop_t
)bindfs_open
}, /* open */
651 {.opve_op
= &vnop_close_desc
, .opve_impl
= (vop_t
)bindfs_close
}, /* close */
652 {.opve_op
= &vnop_inactive_desc
, .opve_impl
= (vop_t
)bind_inactive
}, /* inactive */
653 {.opve_op
= &vnop_reclaim_desc
, .opve_impl
= (vop_t
)bind_reclaim
}, /* reclaim */
654 {.opve_op
= &vnop_lookup_desc
, .opve_impl
= (vop_t
)bind_lookup
}, /* lookup */
655 {.opve_op
= &vnop_readdir_desc
, .opve_impl
= (vop_t
)bindfs_readdir
}, /* readdir */
656 {.opve_op
= &vnop_readlink_desc
, .opve_impl
= (vop_t
)bindfs_readlink
}, /* readlink */
657 {.opve_op
= &vnop_pathconf_desc
, .opve_impl
= (vop_t
)bindfs_pathconf
}, /* pathconf */
658 {.opve_op
= &vnop_fsync_desc
, .opve_impl
= (vop_t
)bindfs_fsync
}, /* fsync */
659 {.opve_op
= &vnop_mmap_desc
, .opve_impl
= (vop_t
)bindfs_mmap
}, /* mmap */
660 {.opve_op
= &vnop_mnomap_desc
, .opve_impl
= (vop_t
)bindfs_mnomap
}, /* mnomap */
661 {.opve_op
= &vnop_getxattr_desc
, .opve_impl
= (vop_t
)bindfs_getxattr
}, /* getxattr */
662 {.opve_op
= &vnop_pagein_desc
, .opve_impl
= (vop_t
)bindfs_pagein
}, /* pagein */
663 {.opve_op
= &vnop_read_desc
, .opve_impl
= (vop_t
)bindfs_read
}, /* read */
664 {.opve_op
= &vnop_listxattr_desc
, .opve_impl
= (vop_t
)bindfs_listxattr
}, /* listxattr */
665 {.opve_op
= NULL
, .opve_impl
= NULL
},
668 const struct vnodeopv_desc bindfs_vnodeop_opv_desc
= {.opv_desc_vector_p
= &bindfs_vnodeop_p
, .opv_desc_ops
= bindfs_vnodeop_entries
};
670 //BINDFS Specific helper function
673 bindfs_getbackingvnode(vnode_t in_vp
, vnode_t
* out_vpp
)
677 if (out_vpp
== NULL
|| in_vp
== NULL
) {
681 struct vfsstatfs
* sp
= NULL
;
682 mount_t mp
= vnode_mount(in_vp
);
685 //If this isn't a bindfs vnode or it is but it's a special vnode
686 if (strcmp(sp
->f_fstypename
, "bindfs") != 0) {
692 vnode_t lvp
= BINDVPTOLOWERVP(in_vp
);
693 if ((result
= vnode_getwithvid(lvp
, BINDVPTOLOWERVID(in_vp
)))) {