diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c @@ -6321,9 +6321,7 @@ bad_write_fallback: if (mp != NULL) vn_finished_write(mp); - error = vn_generic_copy_file_range(ap->a_invp, ap->a_inoffp, - ap->a_outvp, ap->a_outoffp, ap->a_lenp, ap->a_flags, - ap->a_incred, ap->a_outcred, ap->a_fsizetd); + error = ENOSYS; return (error); } diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -861,6 +861,7 @@ pid_t pid; int err; + err = ENOSYS; if (mp == NULL || mp != vnode_mount(outvp)) goto fallback; @@ -943,13 +944,9 @@ VOP_UNLOCK(invp); VOP_UNLOCK(outvp); - if (err == ENOSYS) { + if (err == ENOSYS) fsess_set_notimpl(mp, FUSE_COPY_FILE_RANGE); fallback: - err = vn_generic_copy_file_range(ap->a_invp, ap->a_inoffp, - ap->a_outvp, ap->a_outoffp, ap->a_lenp, ap->a_flags, - ap->a_incred, ap->a_outcred, ap->a_fsizetd); - } /* * No need to call vn_rlimit_fsizex_res before return, since the uio is diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -3888,9 +3888,7 @@ */ if (invp == outvp || invp->v_mount != outvp->v_mount) { generic_copy: - return (vn_generic_copy_file_range(invp, ap->a_inoffp, - outvp, ap->a_outoffp, ap->a_lenp, ap->a_flags, - ap->a_incred, ap->a_outcred, ap->a_fsizetd)); + return (ENOSYS); } /* Lock both vnodes, avoiding risk of deadlock. */ diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -1131,6 +1131,23 @@ return (res); } +static int +null_getwritevnode(struct vop_getwritevnode_args *ap) +{ + struct vnode *vp, *vpl; + + vp = ap->a_vp; + if (vn_lock(vp, LK_SHARED) != 0) + return (EBADF); + + vpl = NULLVPTOLOWERVP(vp); + vhold(vpl); + VOP_UNLOCK(vp); + VOP_GETWRITEVNODE(vpl, ap->a_vplp, ap->a_flags); + vdrop(vpl); + return (0); +} + /* * Global vfs data structures */ @@ -1144,6 +1161,7 @@ .vop_stat = null_stat, .vop_getattr = null_getattr, .vop_getwritemount = null_getwritemount, + .vop_getwritevnode = null_getwritevnode, .vop_inactive = null_inactive, .vop_need_inactive = null_need_inactive, .vop_islocked = vop_stdislocked, @@ -1163,5 +1181,6 @@ .vop_vptofh = null_vptofh, .vop_add_writecount = null_add_writecount, .vop_vput_pair = null_vput_pair, + .vop_copy_file_range = VOP_PANIC, }; VFS_VOP_VECTOR_REGISTER(null_vnodeops); diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c --- a/sys/kern/vfs_default.c +++ b/sys/kern/vfs_default.c @@ -85,6 +85,7 @@ static int vop_stdread_pgcache(struct vop_read_pgcache_args *ap); static int vop_stdstat(struct vop_stat_args *ap); static int vop_stdvput_pair(struct vop_vput_pair_args *ap); +static int vop_stdgetwritevnode(struct vop_getwritevnode_args *ap); /* * This vnode table stores what we want to do if the filesystem doesn't @@ -118,6 +119,7 @@ .vop_getpages = vop_stdgetpages, .vop_getpages_async = vop_stdgetpages_async, .vop_getwritemount = vop_stdgetwritemount, + .vop_getwritevnode = vop_stdgetwritevnode, .vop_inactive = VOP_NULL, .vop_need_inactive = vop_stdneed_inactive, .vop_ioctl = vop_stdioctl, @@ -1610,3 +1612,11 @@ vput(vp); return (0); } + +static int +vop_stdgetwritevnode(struct vop_getwritevnode_args *ap) +{ + vref(ap->a_vp); + *ap->a_vplp = ap->a_vp; + return (0); +} diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -3072,10 +3072,12 @@ struct ucred *outcred, struct thread *fsize_td) { struct mount *inmp, *outmp; + struct vnode *invpl, *outvpl; int error; size_t len; uint64_t uval; + invpl = outvpl = NULL; len = *lenp; *lenp = 0; /* For error returns. */ error = 0; @@ -3101,17 +3103,22 @@ if (len == 0) goto out; - inmp = invp->v_mount; - outmp = outvp->v_mount; - if (inmp == NULL || outmp == NULL) { - error = EBADF; + error = VOP_GETWRITEVNODE(invp, &invpl, FREAD); + if (error != 0) goto out; - } + error = VOP_GETWRITEVNODE(outvp, &outvpl, FWRITE); + if (error != 0) + goto out1; + + inmp = invpl->v_mount; + outmp = outvpl->v_mount; + if (inmp == NULL || outmp == NULL) + goto out2; for (;;) { error = vfs_busy(inmp, 0); if (error != 0) - goto out; + goto out2; if (inmp == outmp) break; error = vfs_busy(outmp, MBF_NOWAIT); @@ -3122,7 +3129,7 @@ vfs_unbusy(outmp); continue; } - goto out; + goto out2; } break; } @@ -3133,16 +3140,23 @@ * which can handle copies across multiple file system types. */ *lenp = len; - if (inmp == outmp || strcmp(inmp->mnt_vfc->vfc_name, - outmp->mnt_vfc->vfc_name) == 0) - error = VOP_COPY_FILE_RANGE(invp, inoffp, outvp, outoffp, + if (inmp == outmp || inmp->mnt_vfc == outmp->mnt_vfc) + error = VOP_COPY_FILE_RANGE(invpl, inoffp, outvpl, outoffp, lenp, flags, incred, outcred, fsize_td); else - error = vn_generic_copy_file_range(invp, inoffp, outvp, + error = ENOSYS; + if (error == ENOSYS) + error = vn_generic_copy_file_range(invpl, inoffp, outvpl, outoffp, lenp, flags, incred, outcred, fsize_td); vfs_unbusy(outmp); if (inmp != outmp) vfs_unbusy(inmp); +out2: + if (outvpl != NULL) + vrele(outvpl); +out1: + if (invpl != NULL) + vrele(invpl); out: return (error); } diff --git a/sys/kern/vnode_if.src b/sys/kern/vnode_if.src --- a/sys/kern/vnode_if.src +++ b/sys/kern/vnode_if.src @@ -471,6 +471,13 @@ OUT struct mount **mpp; }; +%% getwritevnode vp = = = + +vop_getwritevnode { + IN struct vnode *vp; + OUT struct vnode **vplp; + IN int flags; +}; %% print vp - - -