git: 5c025978fc36 - main - unionfs: Implement VOP_GETLOWVNODE
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 12 Dec 2025 06:32:55 UTC
The branch main has been updated by jah:
URL: https://cgit.FreeBSD.org/src/commit/?id=5c025978fc3649730329994eecc56ada119e6717
commit 5c025978fc3649730329994eecc56ada119e6717
Author: Jason A. Harmening <jah@FreeBSD.org>
AuthorDate: 2025-11-28 07:36:24 +0000
Commit: Jason A. Harmening <jah@FreeBSD.org>
CommitDate: 2025-12-12 06:32:05 +0000
unionfs: Implement VOP_GETLOWVNODE
This function returns the vnode that will be used to resolve the
access type specified in the 'flags' argument, and is useful for
optimal behavior of vn_copy_file_range(). While most filesystems
can simply use the default implementation which returns the passed-
in vnode, unionfs (like nullfs) ideally should resolve the access
request to whichever base layer vnode will be used for the I/O.
For unionfs, write accesses must be resolved through the upper vnode,
while read accesses will be resolved through the upper vnode if
present or the lower vnode otherwise. Provide a simple
unionfs_getlowvnode() implementation that reflects this policy.
Reviewed by: kib, olce
Tested by: pho
MFC after: 1 week
Differential Revision: https://reviews.freebsd.org/D53988
---
sys/fs/unionfs/union_vnops.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
sys/kern/vfs_default.c | 3 +--
sys/sys/vnode.h | 1 +
3 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c
index 66fee97a07d5..060863866fcf 100644
--- a/sys/fs/unionfs/union_vnops.c
+++ b/sys/fs/unionfs/union_vnops.c
@@ -2105,6 +2105,49 @@ unionfs_getwritemount(struct vop_getwritemount_args *ap)
return (error);
}
+static int
+unionfs_getlowvnode(struct vop_getlowvnode_args *ap)
+{
+ struct unionfs_node *unp;
+ struct vnode *vp, *basevp;
+
+ vp = ap->a_vp;
+ VI_LOCK(vp);
+ unp = VTOUNIONFS(vp);
+ if (unp == NULL) {
+ VI_UNLOCK(vp);
+ return (EBADF);
+ }
+
+ if (ap->a_flags & FWRITE) {
+ basevp = unp->un_uppervp;
+ /*
+ * If write access is being requested, we expect the unionfs
+ * vnode has already been opened for write access and thus any
+ * necessary copy-up has already been performed. Return an
+ * error if that expectation is not met and an upper vnode has
+ * not been instantiated. We could proactively do a copy-up
+ * here, but that would require additional locking as well as
+ * the addition of a 'cred' argument to VOP_GETLOWVNODE().
+ */
+ if (basevp == NULL) {
+ VI_UNLOCK(vp);
+ return (EACCES);
+ }
+ } else {
+ basevp = (unp->un_uppervp != NULL) ?
+ unp->un_uppervp : unp->un_lowervp;
+ }
+
+ VNASSERT(basevp != NULL, vp, ("%s: no upper/lower vnode", __func__));
+
+ vholdnz(basevp);
+ VI_UNLOCK(vp);
+ VOP_GETLOWVNODE(basevp, ap->a_vplp, ap->a_flags);
+ vdrop(basevp);
+ return (0);
+}
+
static int
unionfs_inactive(struct vop_inactive_args *ap)
{
@@ -3000,6 +3043,7 @@ struct vop_vector unionfs_vnodeops = {
.vop_getattr = unionfs_getattr,
.vop_getextattr = unionfs_getextattr,
.vop_getwritemount = unionfs_getwritemount,
+ .vop_getlowvnode = unionfs_getlowvnode,
.vop_inactive = unionfs_inactive,
.vop_need_inactive = vop_stdneed_inactive,
.vop_islocked = vop_stdislocked,
@@ -3039,5 +3083,6 @@ struct vop_vector unionfs_vnodeops = {
.vop_unp_bind = unionfs_unp_bind,
.vop_unp_connect = unionfs_unp_connect,
.vop_unp_detach = unionfs_unp_detach,
+ .vop_copy_file_range = vop_stdcopy_file_range,
};
VFS_VOP_VECTOR_REGISTER(unionfs_vnodeops);
diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c
index 4eca09aef145..468d5d18b02b 100644
--- a/sys/kern/vfs_default.c
+++ b/sys/kern/vfs_default.c
@@ -77,7 +77,6 @@ static int dirent_exists(struct vnode *vp, const char *dirname,
static int vop_stdis_text(struct vop_is_text_args *ap);
static int vop_stdunset_text(struct vop_unset_text_args *ap);
static int vop_stdadd_writecount(struct vop_add_writecount_args *ap);
-static int vop_stdcopy_file_range(struct vop_copy_file_range_args *ap);
static int vop_stdfdatasync(struct vop_fdatasync_args *ap);
static int vop_stdgetpages_async(struct vop_getpages_async_args *ap);
static int vop_stdread_pgcache(struct vop_read_pgcache_args *ap);
@@ -1426,7 +1425,7 @@ vfs_stdnosync(struct mount *mp, int waitfor)
return (0);
}
-static int
+int
vop_stdcopy_file_range(struct vop_copy_file_range_args *ap)
{
int error;
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index 0bf438a1b821..1a267e0e272c 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -906,6 +906,7 @@ int vop_stdunp_bind(struct vop_unp_bind_args *ap);
int vop_stdunp_connect(struct vop_unp_connect_args *ap);
int vop_stdunp_detach(struct vop_unp_detach_args *ap);
int vop_stdadd_writecount_nomsync(struct vop_add_writecount_args *ap);
+int vop_stdcopy_file_range(struct vop_copy_file_range_args *ap);
int vop_eopnotsupp(struct vop_generic_args *ap);
int vop_ebadf(struct vop_generic_args *ap);
int vop_einval(struct vop_generic_args *ap);