Skip to content

Commit cf562a4

Browse files
committed
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
2 parents 9066e15 + 10bc8e4 commit cf562a4

File tree

4 files changed

+28
-9
lines changed

4 files changed

+28
-9
lines changed

fs/ksmbd/vfs.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,9 +1794,9 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
17941794
ret = vfs_copy_file_range(src_fp->filp, src_off,
17951795
dst_fp->filp, dst_off, len, 0);
17961796
if (ret == -EOPNOTSUPP || ret == -EXDEV)
1797-
ret = generic_copy_file_range(src_fp->filp, src_off,
1798-
dst_fp->filp, dst_off,
1799-
len, 0);
1797+
ret = vfs_copy_file_range(src_fp->filp, src_off,
1798+
dst_fp->filp, dst_off, len,
1799+
COPY_FILE_SPLICE);
18001800
if (ret < 0)
18011801
return ret;
18021802

fs/nfsd/vfs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,8 @@ ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
596596
ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0);
597597

598598
if (ret == -EOPNOTSUPP || ret == -EXDEV)
599-
ret = generic_copy_file_range(src, src_pos, dst, dst_pos,
600-
count, 0);
599+
ret = vfs_copy_file_range(src, src_pos, dst, dst_pos, count,
600+
COPY_FILE_SPLICE);
601601
return ret;
602602
}
603603

fs/read_write.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,8 @@ ssize_t generic_copy_file_range(struct file *file_in, loff_t pos_in,
13881388
struct file *file_out, loff_t pos_out,
13891389
size_t len, unsigned int flags)
13901390
{
1391+
lockdep_assert(sb_write_started(file_inode(file_out)->i_sb));
1392+
13911393
return do_splice_direct(file_in, &pos_in, file_out, &pos_out,
13921394
len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0);
13931395
}
@@ -1424,7 +1426,9 @@ static int generic_copy_file_checks(struct file *file_in, loff_t pos_in,
14241426
* and several different sets of file_operations, but they all end up
14251427
* using the same ->copy_file_range() function pointer.
14261428
*/
1427-
if (file_out->f_op->copy_file_range) {
1429+
if (flags & COPY_FILE_SPLICE) {
1430+
/* cross sb splice is allowed */
1431+
} else if (file_out->f_op->copy_file_range) {
14281432
if (file_in->f_op->copy_file_range !=
14291433
file_out->f_op->copy_file_range)
14301434
return -EXDEV;
@@ -1474,8 +1478,9 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
14741478
size_t len, unsigned int flags)
14751479
{
14761480
ssize_t ret;
1481+
bool splice = flags & COPY_FILE_SPLICE;
14771482

1478-
if (flags != 0)
1483+
if (flags & ~COPY_FILE_SPLICE)
14791484
return -EINVAL;
14801485

14811486
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,
15011506
* same sb using clone, but for filesystems where both clone and copy
15021507
* are supported (e.g. nfs,cifs), we only call the copy method.
15031508
*/
1504-
if (file_out->f_op->copy_file_range) {
1509+
if (!splice && file_out->f_op->copy_file_range) {
15051510
ret = file_out->f_op->copy_file_range(file_in, pos_in,
15061511
file_out, pos_out,
15071512
len, flags);
15081513
goto done;
15091514
}
15101515

1511-
if (file_in->f_op->remap_file_range &&
1516+
if (!splice && file_in->f_op->remap_file_range &&
15121517
file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) {
15131518
ret = file_in->f_op->remap_file_range(file_in, pos_in,
15141519
file_out, pos_out,
@@ -1528,6 +1533,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
15281533
* consistent story about which filesystems support copy_file_range()
15291534
* and which filesystems do not, that will allow userspace tools to
15301535
* make consistent desicions w.r.t using copy_file_range().
1536+
*
1537+
* We also get here if caller (e.g. nfsd) requested COPY_FILE_SPLICE.
15311538
*/
15321539
ret = generic_copy_file_range(file_in, pos_in, file_out, pos_out, len,
15331540
flags);
@@ -1582,6 +1589,10 @@ SYSCALL_DEFINE6(copy_file_range, int, fd_in, loff_t __user *, off_in,
15821589
pos_out = f_out.file->f_pos;
15831590
}
15841591

1592+
ret = -EINVAL;
1593+
if (flags != 0)
1594+
goto out;
1595+
15851596
ret = vfs_copy_file_range(f_in.file, pos_in, f_out.file, pos_out, len,
15861597
flags);
15871598
if (ret > 0) {

include/linux/fs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,14 @@ struct dir_context {
20892089
*/
20902090
#define REMAP_FILE_ADVISORY (REMAP_FILE_CAN_SHORTEN)
20912091

2092+
/*
2093+
* These flags control the behavior of vfs_copy_file_range().
2094+
* They are not available to the user via syscall.
2095+
*
2096+
* COPY_FILE_SPLICE: call splice direct instead of fs clone/copy ops
2097+
*/
2098+
#define COPY_FILE_SPLICE (1 << 0)
2099+
20922100
struct iov_iter;
20932101
struct io_uring_cmd;
20942102

0 commit comments

Comments
 (0)