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);
249 /* if we got lvp, drop the iocount from VNOP_LOOKUP */
259 * Don't think this needs to do anything
262 bind_inactive(__unused
struct vnop_inactive_args
* ap
)
264 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
270 bind_reclaim(struct vnop_reclaim_args
* ap
)
273 struct bind_node
* xp
;
274 struct vnode
* lowervp
;
276 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
281 lowervp
= xp
->bind_lowervp
;
283 vnode_removefsref(vp
);
286 vnode_getwithref(lowervp
);
291 vnode_clearfsnode(vp
);
298 /* Get dirent length padded to 4 byte alignment */
299 #define DIRENT_LEN(namelen) \
300 ((sizeof(struct dirent) + (namelen + 1) - (__DARWIN_MAXNAMLEN + 1) + 3) & ~3)
302 /* Get the end of this dirent */
303 #define DIRENT_END(dep) \
304 (((char *)(dep)) + (dep)->d_reclen - 1)
307 bindfs_readdir(struct vnop_readdir_args
* ap
)
309 struct vnode
*vp
, *lvp
, *dvp
;
311 uio_t uio
= ap
->a_uio
;
313 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
314 /* assumption is that any vp that comes through here had to go through lookup
317 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
)) {
322 dvp
= vnode_parent(vp
);
323 lvp
= BINDVPTOLOWERVP(vp
);
324 error
= vnode_getwithref(lvp
);
329 if (vnode_isvroot(vp
) || (dvp
!= NULL
&& vnode_isvroot(dvp
))) {
335 bufsize
= 3 * MIN((user_size_t
)uio_resid(uio
), 87371u) / 8;
336 bufptr
= kheap_alloc(KHEAP_TEMP
, bufsize
, Z_WAITOK
);
337 if (bufptr
== NULL
) {
340 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
341 uio_addiov(auio
, (uintptr_t)bufptr
, bufsize
);
342 uio_setoffset(auio
, uio_offset(uio
));
343 error
= VNOP_READDIR(lvp
, auio
, ap
->a_flags
, ap
->a_eofflag
, ap
->a_numdirent
, ap
->a_context
);
349 dep
= (struct dirent
*)bufptr
;
350 bytesread
= bufsize
- uio_resid(auio
);
351 while (error
== 0 && (char *)dep
< ((char *)bufptr
+ bytesread
)) {
352 if (DIRENT_END(dep
) > ((char *)bufptr
+ bytesread
) ||
353 DIRENT_LEN(dep
->d_namlen
) > dep
->d_reclen
) {
354 printf("%s: %s: Bad dirent received from directory %s\n", __func__
,
355 vfs_statfs(vnode_mount(vp
))->f_mntonname
,
356 vp
->v_name
? vp
->v_name
: "<unknown>");
360 if (dep
->d_name
[0] == '.') {
361 /* re-write the inode number for the mount root */
362 /* if vp is the mount root then . = 2 and .. = 2 */
363 /* if the parent of vp is the mount root then .. = 2 */
364 if ((vnode_isvroot(vp
) && dep
->d_namlen
== 1) ||
365 (dep
->d_namlen
== 2 && dep
->d_name
[1] == '.')) {
366 dep
->d_ino
= BIND_ROOT_INO
;
369 /* Copy entry64 to user's buffer. */
370 error
= uiomove((caddr_t
)dep
, dep
->d_reclen
, uio
);
371 /* Move to next entry. */
372 dep
= (struct dirent
*)((char *)dep
+ dep
->d_reclen
);
374 /* Update the real offset using the offset we got from VNOP_READDIR. */
376 uio_setoffset(uio
, uio_offset(auio
));
379 kheap_free(KHEAP_TEMP
, bufptr
, bufsize
);
381 error
= VNOP_READDIR(lvp
, ap
->a_uio
, ap
->a_flags
, ap
->a_eofflag
, ap
->a_numdirent
, ap
->a_context
);
390 bindfs_readlink(struct vnop_readlink_args
* ap
)
392 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
394 struct vnode
*vp
, *lvp
;
397 lvp
= BINDVPTOLOWERVP(vp
);
399 error
= vnode_getwithref(lvp
);
401 error
= VNOP_READLINK(lvp
, ap
->a_uio
, ap
->a_context
);
405 printf("bindfs: readlink failed: %d\n", error
);
413 bindfs_pathconf(__unused
struct vnop_pathconf_args
* args
)
415 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
420 bindfs_fsync(__unused
struct vnop_fsync_args
* args
)
422 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
427 bindfs_mmap(struct vnop_mmap_args
* args
)
430 struct vnode
*vp
, *lvp
;
432 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
435 lvp
= BINDVPTOLOWERVP(vp
);
436 error
= vnode_getwithref(lvp
);
438 error
= VNOP_MMAP(lvp
, args
->a_fflags
, args
->a_context
);
446 bindfs_mnomap(struct vnop_mnomap_args
* args
)
449 struct vnode
*vp
, *lvp
;
451 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
454 lvp
= BINDVPTOLOWERVP(vp
);
455 error
= vnode_getwithref(lvp
);
457 error
= VNOP_MNOMAP(lvp
, args
->a_context
);
465 bindfs_getxattr(struct vnop_getxattr_args
* args
)
468 struct vnode
*vp
, *lvp
;
470 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
473 lvp
= BINDVPTOLOWERVP(vp
);
474 error
= vnode_getwithref(lvp
);
476 error
= VNOP_GETXATTR(lvp
, args
->a_name
, args
->a_uio
, args
->a_size
, args
->a_options
, args
->a_context
);
484 bindfs_listxattr(struct vnop_listxattr_args
* args
)
487 struct vnode
*vp
, *lvp
;
489 BINDFSDEBUG("%s %p\n", __FUNCTION__
, args
->a_vp
);
492 lvp
= BINDVPTOLOWERVP(vp
);
493 error
= vnode_getwithref(lvp
);
495 error
= VNOP_LISTXATTR(lvp
, args
->a_uio
, args
->a_size
, args
->a_options
, args
->a_context
);
502 /* relies on v1 paging */
504 bindfs_pagein(struct vnop_pagein_args
* ap
)
507 struct vnode
*vp
, *lvp
;
509 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
512 lvp
= BINDVPTOLOWERVP(vp
);
514 if (vnode_vtype(vp
) != VREG
) {
519 * Ask VM/UBC/VFS to do our bidding
521 if (vnode_getwithvid(lvp
, BINDVPTOLOWERVID(vp
)) == 0) {
525 off_t bytes_to_commit
;
527 upl_t upl
= ap
->a_pl
;
528 user_ssize_t bytes_remaining
= 0;
530 auio
= uio_create(1, ap
->a_f_offset
, UIO_SYSSPACE
, UIO_READ
);
536 kret
= ubc_upl_map(upl
, &ioaddr
);
537 if (KERN_SUCCESS
!= kret
) {
538 panic("bindfs_pagein: ubc_upl_map() failed with (%d)", kret
);
541 ioaddr
+= ap
->a_pl_offset
;
543 error
= uio_addiov(auio
, (user_addr_t
)ioaddr
, ap
->a_size
);
548 lowersize
= ubc_getsize(lvp
);
549 if (lowersize
!= ubc_getsize(vp
)) {
550 (void)ubc_setsize(vp
, lowersize
); /* ignore failures, nothing can be done */
553 error
= VNOP_READ(lvp
, auio
, ((ap
->a_flags
& UPL_IOSYNC
) ? IO_SYNC
: 0), ap
->a_context
);
555 bytes_remaining
= uio_resid(auio
);
556 if (bytes_remaining
> 0 && bytes_remaining
<= (user_ssize_t
)ap
->a_size
) {
557 /* zero bytes that weren't read in to the upl */
558 bzero((void*)((uintptr_t)(ioaddr
+ ap
->a_size
- bytes_remaining
)), (size_t) bytes_remaining
);
562 kret
= ubc_upl_unmap(upl
);
563 if (KERN_SUCCESS
!= kret
) {
564 panic("bindfs_pagein: ubc_upl_unmap() failed with (%d)", kret
);
572 if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
573 if (!error
&& (bytes_remaining
>= 0) && (bytes_remaining
<= (user_ssize_t
)ap
->a_size
)) {
574 /* only commit what was read in (page aligned)*/
575 bytes_to_commit
= ap
->a_size
- bytes_remaining
;
576 if (bytes_to_commit
) {
577 /* need to make sure bytes_to_commit and byte_remaining are page aligned before calling ubc_upl_commit_range*/
578 if (bytes_to_commit
& PAGE_MASK
) {
579 bytes_to_commit
= (bytes_to_commit
& (~PAGE_MASK
)) + (PAGE_MASK
+ 1);
580 assert(bytes_to_commit
<= (off_t
)ap
->a_size
);
582 bytes_remaining
= ap
->a_size
- bytes_to_commit
;
584 ubc_upl_commit_range(upl
, ap
->a_pl_offset
, (upl_size_t
)bytes_to_commit
, UPL_COMMIT_FREE_ON_EMPTY
);
587 /* abort anything thats left */
588 if (bytes_remaining
) {
589 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
);
592 ubc_upl_abort_range(upl
, ap
->a_pl_offset
, (upl_size_t
)ap
->a_size
, UPL_ABORT_ERROR
| UPL_ABORT_FREE_ON_EMPTY
);
596 } else if ((ap
->a_flags
& UPL_NOCOMMIT
) == 0) {
597 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
);
603 bindfs_read(struct vnop_read_args
* ap
)
607 struct vnode
*vp
, *lvp
;
609 BINDFSDEBUG("%s %p\n", __FUNCTION__
, ap
->a_vp
);
612 lvp
= BINDVPTOLOWERVP(vp
);
615 * First some house keeping
617 if (vnode_getwithvid(lvp
, BINDVPTOLOWERVID(vp
)) == 0) {
618 if (!vnode_isreg(lvp
) && !vnode_islnk(lvp
)) {
623 if (uio_resid(ap
->a_uio
) == 0) {
629 * Now ask VM/UBC/VFS to do our bidding
632 error
= VNOP_READ(lvp
, ap
->a_uio
, ap
->a_ioflag
, ap
->a_context
);
634 printf("bindfs: VNOP_READ failed: %d\n", error
);
643 * Global vfs data structures
646 static const struct vnodeopv_entry_desc bindfs_vnodeop_entries
[] = {
647 {.opve_op
= &vnop_default_desc
, .opve_impl
= (vop_t
)bindfs_default
}, /* default */
648 {.opve_op
= &vnop_getattr_desc
, .opve_impl
= (vop_t
)bindfs_getattr
}, /* getattr */
649 {.opve_op
= &vnop_open_desc
, .opve_impl
= (vop_t
)bindfs_open
}, /* open */
650 {.opve_op
= &vnop_close_desc
, .opve_impl
= (vop_t
)bindfs_close
}, /* close */
651 {.opve_op
= &vnop_inactive_desc
, .opve_impl
= (vop_t
)bind_inactive
}, /* inactive */
652 {.opve_op
= &vnop_reclaim_desc
, .opve_impl
= (vop_t
)bind_reclaim
}, /* reclaim */
653 {.opve_op
= &vnop_lookup_desc
, .opve_impl
= (vop_t
)bind_lookup
}, /* lookup */
654 {.opve_op
= &vnop_readdir_desc
, .opve_impl
= (vop_t
)bindfs_readdir
}, /* readdir */
655 {.opve_op
= &vnop_readlink_desc
, .opve_impl
= (vop_t
)bindfs_readlink
}, /* readlink */
656 {.opve_op
= &vnop_pathconf_desc
, .opve_impl
= (vop_t
)bindfs_pathconf
}, /* pathconf */
657 {.opve_op
= &vnop_fsync_desc
, .opve_impl
= (vop_t
)bindfs_fsync
}, /* fsync */
658 {.opve_op
= &vnop_mmap_desc
, .opve_impl
= (vop_t
)bindfs_mmap
}, /* mmap */
659 {.opve_op
= &vnop_mnomap_desc
, .opve_impl
= (vop_t
)bindfs_mnomap
}, /* mnomap */
660 {.opve_op
= &vnop_getxattr_desc
, .opve_impl
= (vop_t
)bindfs_getxattr
}, /* getxattr */
661 {.opve_op
= &vnop_pagein_desc
, .opve_impl
= (vop_t
)bindfs_pagein
}, /* pagein */
662 {.opve_op
= &vnop_read_desc
, .opve_impl
= (vop_t
)bindfs_read
}, /* read */
663 {.opve_op
= &vnop_listxattr_desc
, .opve_impl
= (vop_t
)bindfs_listxattr
}, /* listxattr */
664 {.opve_op
= NULL
, .opve_impl
= NULL
},
667 const struct vnodeopv_desc bindfs_vnodeop_opv_desc
= {.opv_desc_vector_p
= &bindfs_vnodeop_p
, .opv_desc_ops
= bindfs_vnodeop_entries
};
669 //BINDFS Specific helper function
672 bindfs_getbackingvnode(vnode_t in_vp
, vnode_t
* out_vpp
)
676 if (out_vpp
== NULL
|| in_vp
== NULL
) {
680 struct vfsstatfs
* sp
= NULL
;
681 mount_t mp
= vnode_mount(in_vp
);
684 //If this isn't a bindfs vnode or it is but it's a special vnode
685 if (strcmp(sp
->f_fstypename
, "bindfs") != 0) {
691 vnode_t lvp
= BINDVPTOLOWERVP(in_vp
);
692 if ((result
= vnode_getwithvid(lvp
, BINDVPTOLOWERVID(in_vp
)))) {