[PATCH 5.3 055/193] ceph: dont allow copy_file_range when stripe_count != 1

From: Greg Kroah-Hartman
Date: Mon Nov 11 2019 - 13:50:22 EST


From: Luis Henriques <lhenriques@xxxxxxxx>

commit a3a0819388b2bf15e7eafe38ff6aacfc27b12df0 upstream.

copy_file_range tries to use the OSD 'copy-from' operation, which simply
performs a full object copy. Unfortunately, the implementation of this
system call assumes that stripe_count is always set to 1 and doesn't take
into account that the data may be striped across an object set. If the
file layout has stripe_count different from 1, then the destination file
data will be corrupted.

For example:

Consider a 8 MiB file with 4 MiB object size, stripe_count of 2 and
stripe_size of 2 MiB; the first half of the file will be filled with 'A's
and the second half will be filled with 'B's:

0 4M 8M Obj1 Obj2
+------+------+ +----+ +----+
file: | AAAA | BBBB | | AA | | AA |
+------+------+ |----| |----|
| BB | | BB |
+----+ +----+

If we copy_file_range this file into a new file (which needs to have the
same file layout!), then it will start by copying the object starting at
file offset 0 (Obj1). And then it will copy the object starting at file
offset 4M -- which is Obj1 again.

Unfortunately, the solution for this is to not allow remote object copies
to be performed when the file layout stripe_count is not 1 and simply
fallback to the default (VFS) copy_file_range implementation.

Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Luis Henriques <lhenriques@xxxxxxxx>
Reviewed-by: Jeff Layton <jlayton@xxxxxxxxxx>
Signed-off-by: Ilya Dryomov <idryomov@xxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

---
fs/ceph/file.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -1934,10 +1934,18 @@ static ssize_t __ceph_copy_file_range(st
if (ceph_test_mount_opt(ceph_inode_to_client(src_inode), NOCOPYFROM))
return -EOPNOTSUPP;

+ /*
+ * Striped file layouts require that we copy partial objects, but the
+ * OSD copy-from operation only supports full-object copies. Limit
+ * this to non-striped file layouts for now.
+ */
if ((src_ci->i_layout.stripe_unit != dst_ci->i_layout.stripe_unit) ||
- (src_ci->i_layout.stripe_count != dst_ci->i_layout.stripe_count) ||
- (src_ci->i_layout.object_size != dst_ci->i_layout.object_size))
+ (src_ci->i_layout.stripe_count != 1) ||
+ (dst_ci->i_layout.stripe_count != 1) ||
+ (src_ci->i_layout.object_size != dst_ci->i_layout.object_size)) {
+ dout("Invalid src/dst files layout\n");
return -EOPNOTSUPP;
+ }

if (len < src_ci->i_layout.object_size)
return -EOPNOTSUPP; /* no remote copy will be done */