Commit cf562a45 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs fix from Al Viro:
 "Amir's copy_file_range() fix"

* tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  vfs: fix copy_file_range() averts filesystem freeze protection
parents 9066e151 10bc8e4a
...@@ -1794,9 +1794,9 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, ...@@ -1794,9 +1794,9 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
ret = vfs_copy_file_range(src_fp->filp, src_off, ret = vfs_copy_file_range(src_fp->filp, src_off,
dst_fp->filp, dst_off, len, 0); dst_fp->filp, dst_off, len, 0);
if (ret == -EOPNOTSUPP || ret == -EXDEV) if (ret == -EOPNOTSUPP || ret == -EXDEV)
ret = generic_copy_file_range(src_fp->filp, src_off, ret = vfs_copy_file_range(src_fp->filp, src_off,
dst_fp->filp, dst_off, dst_fp->filp, dst_off, len,
len, 0); COPY_FILE_SPLICE);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -596,8 +596,8 @@ ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst, ...@@ -596,8 +596,8 @@ ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0); ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0);
if (ret == -EOPNOTSUPP || ret == -EXDEV) if (ret == -EOPNOTSUPP || ret == -EXDEV)
ret = generic_copy_file_range(src, src_pos, dst, dst_pos, ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count,
count, 0); COPY_FILE_SPLICE);
return ret; return ret;
} }
......
...@@ -1388,6 +1388,8 @@ ssize_t generic_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -1388,6 +1388,8 @@ ssize_t generic_copy_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out, struct file *file_out, loff_t pos_out,
size_t len, unsigned int flags) size_t len, unsigned int flags)
{ {
lockdep_assert(sb_write_started(file_inode(file_out)->i_sb));
return do_splice_direct(file_in, &pos_in, file_out, &pos_out, return do_splice_direct(file_in, &pos_in, file_out, &pos_out,
len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0); len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0);
} }
...@@ -1424,7 +1426,9 @@ static int generic_copy_file_checks(struct file *file_in, loff_t pos_in, ...@@ -1424,7 +1426,9 @@ static int generic_copy_file_checks(struct file *file_in, loff_t pos_in,
* and several different sets of file_operations, but they all end up * and several different sets of file_operations, but they all end up
* using the same ->copy_file_range() function pointer. * using the same ->copy_file_range() function pointer.
*/ */
if (file_out->f_op->copy_file_range) { if (flags & COPY_FILE_SPLICE) {
/* cross sb splice is allowed */
} else if (file_out->f_op->copy_file_range) {
if (file_in->f_op->copy_file_range != if (file_in->f_op->copy_file_range !=
file_out->f_op->copy_file_range) file_out->f_op->copy_file_range)
return -EXDEV; return -EXDEV;
...@@ -1474,8 +1478,9 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -1474,8 +1478,9 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
size_t len, unsigned int flags) size_t len, unsigned int flags)
{ {
ssize_t ret; ssize_t ret;
bool splice = flags & COPY_FILE_SPLICE;
if (flags != 0) if (flags & ~COPY_FILE_SPLICE)
return -EINVAL; return -EINVAL;
ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len, ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len,
...@@ -1501,14 +1506,14 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -1501,14 +1506,14 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
* same sb using clone, but for filesystems where both clone and copy * same sb using clone, but for filesystems where both clone and copy
* are supported (e.g. nfs,cifs), we only call the copy method. * are supported (e.g. nfs,cifs), we only call the copy method.
*/ */
if (file_out->f_op->copy_file_range) { if (!splice && file_out->f_op->copy_file_range) {
ret = file_out->f_op->copy_file_range(file_in, pos_in, ret = file_out->f_op->copy_file_range(file_in, pos_in,
file_out, pos_out, file_out, pos_out,
len, flags); len, flags);
goto done; goto done;
} }
if (file_in->f_op->remap_file_range && if (!splice && file_in->f_op->remap_file_range &&
file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) { file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) {
ret = file_in->f_op->remap_file_range(file_in, pos_in, ret = file_in->f_op->remap_file_range(file_in, pos_in,
file_out, pos_out, file_out, pos_out,
...@@ -1528,6 +1533,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -1528,6 +1533,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
* consistent story about which filesystems support copy_file_range() * consistent story about which filesystems support copy_file_range()
* and which filesystems do not, that will allow userspace tools to * and which filesystems do not, that will allow userspace tools to
* make consistent desicions w.r.t using copy_file_range(). * make consistent desicions w.r.t using copy_file_range().
*
* We also get here if caller (e.g. nfsd) requested COPY_FILE_SPLICE.
*/ */
ret = generic_copy_file_range(file_in, pos_in, file_out, pos_out, len, ret = generic_copy_file_range(file_in, pos_in, file_out, pos_out, len,
flags); flags);
...@@ -1582,6 +1589,10 @@ SYSCALL_DEFINE6(copy_file_range, int, fd_in, loff_t __user *, off_in, ...@@ -1582,6 +1589,10 @@ SYSCALL_DEFINE6(copy_file_range, int, fd_in, loff_t __user *, off_in,
pos_out = f_out.file->f_pos; pos_out = f_out.file->f_pos;
} }
ret = -EINVAL;
if (flags != 0)
goto out;
ret = vfs_copy_file_range(f_in.file, pos_in, f_out.file, pos_out, len, ret = vfs_copy_file_range(f_in.file, pos_in, f_out.file, pos_out, len,
flags); flags);
if (ret > 0) { if (ret > 0) {
......
...@@ -2089,6 +2089,14 @@ struct dir_context { ...@@ -2089,6 +2089,14 @@ struct dir_context {
*/ */
#define REMAP_FILE_ADVISORY (REMAP_FILE_CAN_SHORTEN) #define REMAP_FILE_ADVISORY (REMAP_FILE_CAN_SHORTEN)
/*
* These flags control the behavior of vfs_copy_file_range().
* They are not available to the user via syscall.
*
* COPY_FILE_SPLICE: call splice direct instead of fs clone/copy ops
*/
#define COPY_FILE_SPLICE (1 << 0)
struct iov_iter; struct iov_iter;
struct io_uring_cmd; struct io_uring_cmd;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment