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 @@ -3046,6 +3046,7 @@ off_t *outoffp, size_t *lenp, unsigned int flags, struct ucred *incred, struct ucred *outcred, struct thread *fsize_td) { + struct mount *inmp, *outmp; int error; size_t len; uint64_t uval; @@ -3075,20 +3076,50 @@ if (len == 0) goto out; + inmp = invp->v_mount; + outmp = outvp->v_mount; + if (inmp == NULL || outmp == NULL) { + error = EBADF; + goto out; + } + + for (;;) { + error = vfs_busy(inmp, 0); + if (error != 0) + goto out; + if (inmp == outmp) + break; + error = vfs_busy(outmp, MBF_NOWAIT); + if (error != 0) { + vfs_unbusy(inmp); + if (error == ENOENT) { + error = vfs_busy(outmp, 0); + if (error == 0) { + vfs_unbusy(outmp); + continue; + } + } + goto out; + } + break; + } + /* * If the two vnodes are for the same file system type, call * VOP_COPY_FILE_RANGE(), otherwise call vn_generic_copy_file_range() * which can handle copies across multiple file system types. */ *lenp = len; - if (invp->v_mount == outvp->v_mount || - strcmp(invp->v_mount->mnt_vfc->vfc_name, - outvp->v_mount->mnt_vfc->vfc_name) == 0) + if (inmp == outmp || strcmp(inmp->mnt_vfc->vfc_name, + outmp->mnt_vfc->vfc_name) == 0) error = VOP_COPY_FILE_RANGE(invp, inoffp, outvp, outoffp, lenp, flags, incred, outcred, fsize_td); else error = vn_generic_copy_file_range(invp, inoffp, outvp, outoffp, lenp, flags, incred, outcred, fsize_td); + vfs_unbusy(outmp); + if (inmp != outmp) + vfs_unbusy(inmp); out: return (error); }