Btrfs: simplify inline extent handling when doing reflinks
authorFilipe Manana <fdmanana@suse.com>
Fri, 28 Feb 2020 13:04:18 +0000 (13:04 +0000)
committerDavid Sterba <dsterba@suse.com>
Mon, 23 Mar 2020 16:01:54 +0000 (17:01 +0100)
We can not reflink parts of an inline extent, we must always reflink the
whole inline extent. We know that inline extents always start at file
offset 0 and that can never represent an amount of data larger then the
filesystem's sector size (both compressed and uncompressed). We also have
had the constraints that reflink operations must have a start offset that
is aligned to the sector size and an end offset that is also aligned or
it ends the inode's i_size, so there's no way for user space to be able
to do a reflink operation that will refer to only a part of an inline
extent.

Initially there was a bug in the inlining code that could allow compressed
inline extents that encoded more than 1 page, but that was fixed in 2008
by commit 70b99e6959a4c2 ("Btrfs: Compression corner fixes") since that
was problematic.

So remove all the extent cloning code that deals with the possibility
of cloning only partial inline extents.

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/reflink.c

index 367b116..829adbb 100644 (file)
@@ -73,9 +73,8 @@ static int clone_copy_inline_extent(struct inode *dst,
                                    struct btrfs_key *new_key,
                                    const u64 drop_start,
                                    const u64 datal,
-                                   const u64 skip,
                                    const u64 size,
-                                   char *inline_data)
+                                   const char *inline_data)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(dst->i_sb);
        struct btrfs_root *root = BTRFS_I(dst)->root;
@@ -171,12 +170,6 @@ copy_inline_extent:
        if (ret)
                return ret;
 
-       if (skip) {
-               const u32 start = btrfs_file_extent_calc_inline_size(0);
-
-               memmove(inline_data + start, inline_data + start + skip, datal);
-       }
-
        write_extent_buffer(path->nodes[0], inline_data,
                            btrfs_item_ptr_offset(path->nodes[0],
                                                  path->slots[0]),
@@ -240,7 +233,6 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
                struct btrfs_key new_key;
                u64 disko = 0, diskl = 0;
                u64 datao = 0, datal = 0;
-               u8 comp;
                u64 drop_start;
 
                /* Note the key will change type as we walk through the tree */
@@ -283,7 +275,6 @@ process_slot:
 
                extent = btrfs_item_ptr(leaf, slot,
                                        struct btrfs_file_extent_item);
-               comp = btrfs_file_extent_compression(leaf, extent);
                type = btrfs_file_extent_type(leaf, extent);
                if (type == BTRFS_FILE_EXTENT_REG ||
                    type == BTRFS_FILE_EXTENT_PREALLOC) {
@@ -365,23 +356,18 @@ process_slot:
                        if (ret)
                                goto out;
                } else if (type == BTRFS_FILE_EXTENT_INLINE) {
-                       u64 skip = 0;
-                       u64 trim = 0;
-
-                       if (off > key.offset) {
-                               skip = off - key.offset;
-                               new_key.offset += skip;
-                       }
-
-                       if (key.offset + datal > off + len)
-                               trim = key.offset + datal - (off + len);
-
-                       if (comp && (skip || trim)) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
-                       size -= skip + trim;
-                       datal -= skip + trim;
+                       /*
+                        * Inline extents always have to start at file offset 0
+                        * and can never be bigger then the sector size. We can
+                        * never clone only parts of an inline extent, since all
+                        * reflink operations must start at a sector size aligned
+                        * offset, and the length must be aligned too or end at
+                        * the i_size (which implies the whole inlined data).
+                        */
+                       ASSERT(key.offset == 0);
+                       ASSERT(datal <= fs_info->sectorsize);
+                       if (key.offset != 0 || datal > fs_info->sectorsize)
+                               return -EUCLEAN;
 
                        /*
                         * If our extent is inline, we know we will drop or
@@ -399,7 +385,7 @@ process_slot:
 
                        ret = clone_copy_inline_extent(inode, trans, path,
                                                       &new_key, drop_start,
-                                                      datal, skip, size, buf);
+                                                      datal, size, buf);
                        if (ret) {
                                if (ret != -EOPNOTSUPP)
                                        btrfs_abort_transaction(trans, ret);
@@ -544,17 +530,6 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
        u64 bs = fs_info->sb->s_blocksize;
 
        /*
-        * TODO:
-        * - split compressed inline extents.  annoying: we need to
-        *   decompress into destination's address_space (the file offset
-        *   may change, so source mapping won't do), then recompress (or
-        *   otherwise reinsert) a subrange.
-        *
-        * - split destination inode's inline extents.  The inline extents can
-        *   be either compressed or non-compressed.
-        */
-
-       /*
         * VFS's generic_remap_file_range_prep() protects us from cloning the
         * eof block into the middle of a file, which would result in corruption
         * if the file size is not blocksize aligned. So we don't need to check