Re: [PATCH 04/11] vfs: add missing checks to copy_file_range

Luis Henriques
2018-12-13T10:29:42Z

Olga Kornievskaia <olga.kornievskaia@gmail.com> writes:

> On Wed, Dec 12, 2018 at 2:43 PM Matthew Wilcox <willy@infradead.org> wrote:
>>
>> On Wed, Dec 12, 2018 at 01:55:28PM -0500, Olga Kornievskaia wrote:
>> > On Wed, Dec 12, 2018 at 6:31 AM Luis Henriques <lhenriques@suse.com> wrote:
>> > > I was wondering if, with the above check, it would make sense to also
>> > > have an extra patch changing some filesystems (ceph, nfs and cifs) to
>> > > simply return -EOPNOTSUPP (instead of -EINVAL) when inode_in ==
>> > > inode_out. Something like the diff below (not tested!).
>>
>> > > +++ b/fs/nfs/nfs4file.c
>> > > @@ -136,7 +136,7 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
>> > > ssize_t ret;
>> > >
>> > > if (file_inode(file_in) == file_inode(file_out))
>> > > - return -EINVAL;
>> > > + return -EOPNOTSUPP;
>> >
>> > Please don't change the NFS bits. This is against the NFS
>> > specifications. RFC 7862 15.2.3
>> >
>> > (snippet)
>> > SAVED_FH and CURRENT_FH must be different files. If SAVED_FH and
>> > CURRENT_FH refer to the same file, the operation MUST fail with
>> > NFS4ERR_INVAL.
>>
>> I don't see how that applies. That refers to a requirement _in the
>> protocol_ that determines what the server MUST do if the client sends
>> it two FHs which refer to the same file.
>>
>> What we're talking about here is how a Linux filesystem behaves when
>> receiving a copy_file_range() referring to the same file. As long as
>> the Linux filesystem doesn't react by sending out one of these invalid
>> protocol messages, I don't see the problem.
>
> Ok then this should be changed to call generic_copy_file_range() not
> returning the EOPNOTSUPP since there is no longer fallback in vfs to
> call the generic_copy_file_range() and in turn responsibility of each
> file system.

Ah, I didn't look close enough and didn't realised the nfs code was
doing something slightly different from the other 2 FSs. In that case
simply deleting that check seems to be enough to fallback to the vfs
generic_copy_file_range.

Anyway, please find below an updated patch (with proper changelog).

Cheers,
--
Luis

From f66a07e22dc93827bdafc1666d4980edc986bce4 Mon Sep 17 00:00:00 2001
From: Luis Henriques <lhenriques@suse.com>
Date: Thu, 13 Dec 2018 10:19:54 +0000
Subject: [PATCH] vfs: fallback to generic_copy_file_range if copying within
the same file

If source and destination inode are the same simply fallback to the VFS
generic_copy_file_range, as we've already checked overlapping areas in
generic_copy_file_checks.

Signed-off-by: Luis Henriques <lhenriques@suse.com>
---
fs/ceph/file.c | 2 +-
fs/cifs/cifsfs.c | 2 +-
fs/nfs/nfs4file.c | 3 ---
3 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index eb876e19c1dc..ff48dc52c30e 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -1904,7 +1904,7 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off,
bool do_final_copy = false;

if (src_inode == dst_inode)
- return -EINVAL;
+ return -EOPNOTSUPP;
if (src_inode->i_sb != dst_inode->i_sb)
return -EXDEV;
if (ceph_snap(dst_inode) != CEPH_NOSNAP)
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 03e4b9eacbd1..3c66454c59b6 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1068,7 +1068,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
cifs_dbg(FYI, "copychunk range\n");

if (src_inode == target_inode) {
- rc = -EINVAL;
+ rc = -EOPNOTSUPP;
goto out;
}

diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 4783c0c1c49e..dc7f344849e9 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -135,9 +135,6 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
{
ssize_t ret = -EXDEV;

- if (file_inode(file_in) == file_inode(file_out))
- return -EINVAL;
-
/* only offload copy if superblock is the same */
if (file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) {
do {