On Sun, Jun 01, 2014 at 01:50:28AM +0100, Filipe David Borba Manana wrote:
> If the NO_HOLES feature is enabled holes don't have file extent items in
> the btree that represent them anymore. This made the clone operation
> ignore the gaps that exist between consecutive file extent items and
> therefore not create the holes at the destination. When not using the
> NO_HOLES feature, the holes were created at the destination.
> 
> A test case for xfstests follows.

Reviewed-by: Liu Bo <bo.li....@oracle.com>

-liubo

> 
> Signed-off-by: Filipe David Borba Manana <fdman...@gmail.com>
> ---
> 
> V2: Deal with holes at the boundaries of the cloning range and that
>     either overlap the boundary completely or partially.
>     Test case for xfstests updated too to test these 2 cases.
> 
> V3: Deal with the case where the cloning range overlaps (partially or
>     completely) a hole at the end of the source file, and might increase
>     the size of the target file.
>     Updated the test for xfstests to cover these cases too.
> 
> V4: Moved some duplicated code into an helper function.
> 
>  fs/btrfs/ioctl.c | 108 
> ++++++++++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 83 insertions(+), 25 deletions(-)
> 
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 04ece8f..95194a9 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -2983,6 +2983,37 @@ out:
>       return ret;
>  }
>  
> +static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
> +                                  struct inode *inode,
> +                                  u64 endoff,
> +                                  const u64 destoff,
> +                                  const u64 olen)
> +{
> +     struct btrfs_root *root = BTRFS_I(inode)->root;
> +     int ret;
> +
> +     inode_inc_iversion(inode);
> +     inode->i_mtime = inode->i_ctime = CURRENT_TIME;
> +     /*
> +      * We round up to the block size at eof when determining which
> +      * extents to clone above, but shouldn't round up the file size.
> +      */
> +     if (endoff > destoff + olen)
> +             endoff = destoff + olen;
> +     if (endoff > inode->i_size)
> +             btrfs_i_size_write(inode, endoff);
> +
> +     ret = btrfs_update_inode(trans, root, inode);
> +     if (ret) {
> +             btrfs_abort_transaction(trans, root, ret);
> +             btrfs_end_transaction(trans, root);
> +             goto out;
> +     }
> +     ret = btrfs_end_transaction(trans, root);
> +out:
> +     return ret;
> +}
> +
>  /**
>   * btrfs_clone() - clone a range from inode file to another
>   *
> @@ -2995,7 +3026,8 @@ out:
>   * @destoff: Offset within @inode to start clone
>   */
>  static int btrfs_clone(struct inode *src, struct inode *inode,
> -                    u64 off, u64 olen, u64 olen_aligned, u64 destoff)
> +                    const u64 off, const u64 olen, const u64 olen_aligned,
> +                    const u64 destoff)
>  {
>       struct btrfs_root *root = BTRFS_I(inode)->root;
>       struct btrfs_path *path = NULL;
> @@ -3007,8 +3039,9 @@ static int btrfs_clone(struct inode *src, struct inode 
> *inode,
>       int slot;
>       int ret;
>       int no_quota;
> -     u64 len = olen_aligned;
> +     const u64 len = olen_aligned;
>       u64 last_disko = 0;
> +     u64 last_dest_end = destoff;
>  
>       ret = -ENOMEM;
>       buf = vmalloc(btrfs_level_size(root, 0));
> @@ -3076,7 +3109,7 @@ process_slot:
>                       u64 disko = 0, diskl = 0;
>                       u64 datao = 0, datal = 0;
>                       u8 comp;
> -                     u64 endoff;
> +                     u64 drop_start;
>  
>                       extent = btrfs_item_ptr(leaf, slot,
>                                               struct btrfs_file_extent_item);
> @@ -3125,6 +3158,18 @@ process_slot:
>                               new_key.offset = destoff;
>  
>                       /*
> +                      * Deal with a hole that doesn't have an extent item
> +                      * that represents it (NO_HOLES feature enabled).
> +                      * This hole is either in the middle of the cloning
> +                      * range or at the beginning (fully overlaps it or
> +                      * partially overlaps it).
> +                      */
> +                     if (new_key.offset != last_dest_end)
> +                             drop_start = last_dest_end;
> +                     else
> +                             drop_start = new_key.offset;
> +
> +                     /*
>                        * 1 - adjusting old extent (we may have to split it)
>                        * 1 - add new extent
>                        * 1 - inode update
> @@ -3153,7 +3198,7 @@ process_slot:
>                               }
>  
>                               ret = btrfs_drop_extents(trans, root, inode,
> -                                                      new_key.offset,
> +                                                      drop_start,
>                                                        new_key.offset + datal,
>                                                        1);
>                               if (ret) {
> @@ -3254,7 +3299,7 @@ process_slot:
>                               aligned_end = ALIGN(new_key.offset + datal,
>                                                   root->sectorsize);
>                               ret = btrfs_drop_extents(trans, root, inode,
> -                                                      new_key.offset,
> +                                                      drop_start,
>                                                        aligned_end,
>                                                        1);
>                               if (ret) {
> @@ -3292,27 +3337,12 @@ process_slot:
>                       btrfs_mark_buffer_dirty(leaf);
>                       btrfs_release_path(path);
>  
> -                     inode_inc_iversion(inode);
> -                     inode->i_mtime = inode->i_ctime = CURRENT_TIME;
> -
> -                     /*
> -                      * we round up to the block size at eof when
> -                      * determining which extents to clone above,
> -                      * but shouldn't round up the file size
> -                      */
> -                     endoff = new_key.offset + datal;
> -                     if (endoff > destoff+olen)
> -                             endoff = destoff+olen;
> -                     if (endoff > inode->i_size)
> -                             btrfs_i_size_write(inode, endoff);
> -
> -                     ret = btrfs_update_inode(trans, root, inode);
> -                     if (ret) {
> -                             btrfs_abort_transaction(trans, root, ret);
> -                             btrfs_end_transaction(trans, root);
> +                     last_dest_end = new_key.offset + datal;
> +                     ret = clone_finish_inode_update(trans, inode,
> +                                                     last_dest_end,
> +                                                     destoff, olen);
> +                     if (ret)
>                               goto out;
> -                     }
> -                     ret = btrfs_end_transaction(trans, root);
>                       if (new_key.offset + datal >= destoff + len)
>                               break;
>               }
> @@ -3321,6 +3351,34 @@ process_slot:
>       }
>       ret = 0;
>  
> +     if (last_dest_end < destoff + len) {
> +             /*
> +              * We have an implicit hole (NO_HOLES feature is enabled) that
> +              * fully or partially overlaps our cloning range at its end.
> +              */
> +             btrfs_release_path(path);
> +
> +             /*
> +              * 1 - remove extent(s)
> +              * 1 - inode update
> +              */
> +             trans = btrfs_start_transaction(root, 2);
> +             if (IS_ERR(trans)) {
> +                     ret = PTR_ERR(trans);
> +                     goto out;
> +             }
> +             ret = btrfs_drop_extents(trans, root, inode,
> +                                      last_dest_end, destoff + len, 1);
> +             if (ret) {
> +                     if (ret != -EOPNOTSUPP)
> +                             btrfs_abort_transaction(trans, root, ret);
> +                     btrfs_end_transaction(trans, root);
> +                     goto out;
> +             }
> +             ret = clone_finish_inode_update(trans, inode, destoff + len,
> +                                             destoff, olen);
> +     }
> +
>  out:
>       btrfs_free_path(path);
>       vfree(buf);
> -- 
> 1.9.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to