[RFC][PATCH 2/5] fs: add support for copy file range from another fs
Amir Goldstein <[email protected]> Thu, 23 Jul 2026 13:48:45 +0200
| Newsgroups | org.kernel.vger.linux-unionfs,org.kernel.vger.linux-fsdevel |
|---|---|
| Message-ID | <[email protected]> |
Add a declarative vfs flag FOP_CROSS_FS_COPY for filesystems that may be able copy from/to other fs and implement the copy from another fs, which is the easy side. When filesystem is copying from another fs, it will typically call vfs read helpers to read from the other fs, so avoid the double accounting and double fsnotify read hooks in vfs_copy_file_range(). Implementation of copy to another fs is a bit more subtle and will be handled by a followup patch. Signed-off-by: Amir Goldstein <[email protected]> --- fs/read_write.c | 18 ++++++++++++++---- include/linux/fs.h | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index d1565a56d089d..78596d9c89119 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1477,6 +1477,7 @@ enum { FS_COPY_SAME_SB = 1, FS_COPY_SAME_FS = 2, FS_COPY_CROSS_FS = 3, + FS_COPY_FROM_OTHER_FS = 4, }; /* @@ -1495,6 +1496,8 @@ static int copy_file_fs_cmp(struct file *f_in, struct file *f_out, else if (f_out->f_op->copy_file_range && f_out->f_op->copy_file_range == f_in->f_op->copy_file_range) return FS_COPY_SAME_FS; + else if (f_out->f_op->fop_flags & FOP_CROSS_FS_COPY) + return FS_COPY_FROM_OTHER_FS; else if (file_inode(f_in)->i_sb == file_inode(f_out)->i_sb) return FS_COPY_SAME_SB; else @@ -1526,10 +1529,13 @@ static int generic_copy_file_checks(struct file *file_in, loff_t pos_in, * We allow some filesystems to handle cross sb copy, but passing * a file of the wrong filesystem type to filesystem driver can result * in an attempt to dereference the wrong type of ->private_data, so - * avoid doing that until we really have a good reason. + * avoid doing that unless at least one of the filesystems declares + * cross fstype copy support with the FOP_CROSS_FS_COPY flag. */ if (fscmp == FS_COPY_SPLICE) { /* cross sb splice is allowed */ + } else if (fscmp == FS_COPY_FROM_OTHER_FS) { + /* Copy from other fs is allowed */ } else if (file_out->f_op->copy_file_range) { if (fscmp != FS_COPY_SAME_FS) return -EXDEV; @@ -1619,6 +1625,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, case FS_COPY_SPLICE: break; case FS_COPY_SAME_FS: + case FS_COPY_FROM_OTHER_FS: ret = file_out->f_op->copy_file_range(file_in, pos_in, file_out, pos_out, len, flags); @@ -1665,13 +1672,16 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, len, 0); done: if (ret > 0) { - fsnotify_access(file_in); - add_rchar(current, ret); + if (fscmp != FS_COPY_FROM_OTHER_FS) { + fsnotify_access(file_in); + add_rchar(current, ret); + } fsnotify_modify(file_out); add_wchar(current, ret); } - inc_syscr(current); + if (fscmp != FS_COPY_FROM_OTHER_FS) + inc_syscr(current); inc_syscw(current); return ret; diff --git a/include/linux/fs.h b/include/linux/fs.h index d10897b3a1e35..c7573e89557b9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1980,6 +1980,8 @@ struct file_operations { #define FOP_ASYNC_LOCK ((__force fop_flags_t)(1 << 6)) /* File system supports uncached read/write buffered IO */ #define FOP_DONTCACHE ((__force fop_flags_t)(1 << 7)) +/* Supports copy_file_range() to/from other fs types */ +#define FOP_CROSS_FS_COPY ((__force fop_flags_t)(1 << 8)) /* Wrap a directory iterator that needs exclusive inode access */ int wrap_directory_iterator(struct file *, struct dir_context *, -- 2.54.0