aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@gmail.com>2014-05-31 20:50:28 -0400
committerChris Mason <clm@fb.com>2014-06-09 20:21:09 -0400
commitf82a9901b0321feb4b4dea1583c51bd75707dd4e (patch)
tree1220f3891d76ecd4effad62c261f2fa41a585d5c /fs/btrfs
parent964930312aec583809a690868119ce716f4ee926 (diff)
Btrfs: fix clone to deal with holes when NO_HOLES feature is enabled
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. Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com> Reviewed-by: Liu Bo <bo.li.liu@oracle.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ioctl.c108
1 files changed, 83 insertions, 25 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 55f4d717d541..44dcfd054ca6 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3012,6 +3012,37 @@ out:
3012 return ret; 3012 return ret;
3013} 3013}
3014 3014
3015static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
3016 struct inode *inode,
3017 u64 endoff,
3018 const u64 destoff,
3019 const u64 olen)
3020{
3021 struct btrfs_root *root = BTRFS_I(inode)->root;
3022 int ret;
3023
3024 inode_inc_iversion(inode);
3025 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
3026 /*
3027 * We round up to the block size at eof when determining which
3028 * extents to clone above, but shouldn't round up the file size.
3029 */
3030 if (endoff > destoff + olen)
3031 endoff = destoff + olen;
3032 if (endoff > inode->i_size)
3033 btrfs_i_size_write(inode, endoff);
3034
3035 ret = btrfs_update_inode(trans, root, inode);
3036 if (ret) {
3037 btrfs_abort_transaction(trans, root, ret);
3038 btrfs_end_transaction(trans, root);
3039 goto out;
3040 }
3041 ret = btrfs_end_transaction(trans, root);
3042out:
3043 return ret;
3044}
3045
3015/** 3046/**
3016 * btrfs_clone() - clone a range from inode file to another 3047 * btrfs_clone() - clone a range from inode file to another
3017 * 3048 *
@@ -3024,7 +3055,8 @@ out:
3024 * @destoff: Offset within @inode to start clone 3055 * @destoff: Offset within @inode to start clone
3025 */ 3056 */
3026static int btrfs_clone(struct inode *src, struct inode *inode, 3057static int btrfs_clone(struct inode *src, struct inode *inode,
3027 u64 off, u64 olen, u64 olen_aligned, u64 destoff) 3058 const u64 off, const u64 olen, const u64 olen_aligned,
3059 const u64 destoff)
3028{ 3060{
3029 struct btrfs_root *root = BTRFS_I(inode)->root; 3061 struct btrfs_root *root = BTRFS_I(inode)->root;
3030 struct btrfs_path *path = NULL; 3062 struct btrfs_path *path = NULL;
@@ -3036,8 +3068,9 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
3036 int slot; 3068 int slot;
3037 int ret; 3069 int ret;
3038 int no_quota; 3070 int no_quota;
3039 u64 len = olen_aligned; 3071 const u64 len = olen_aligned;
3040 u64 last_disko = 0; 3072 u64 last_disko = 0;
3073 u64 last_dest_end = destoff;
3041 3074
3042 ret = -ENOMEM; 3075 ret = -ENOMEM;
3043 buf = vmalloc(btrfs_level_size(root, 0)); 3076 buf = vmalloc(btrfs_level_size(root, 0));
@@ -3105,7 +3138,7 @@ process_slot:
3105 u64 disko = 0, diskl = 0; 3138 u64 disko = 0, diskl = 0;
3106 u64 datao = 0, datal = 0; 3139 u64 datao = 0, datal = 0;
3107 u8 comp; 3140 u8 comp;
3108 u64 endoff; 3141 u64 drop_start;
3109 3142
3110 extent = btrfs_item_ptr(leaf, slot, 3143 extent = btrfs_item_ptr(leaf, slot,
3111 struct btrfs_file_extent_item); 3144 struct btrfs_file_extent_item);
@@ -3154,6 +3187,18 @@ process_slot:
3154 new_key.offset = destoff; 3187 new_key.offset = destoff;
3155 3188
3156 /* 3189 /*
3190 * Deal with a hole that doesn't have an extent item
3191 * that represents it (NO_HOLES feature enabled).
3192 * This hole is either in the middle of the cloning
3193 * range or at the beginning (fully overlaps it or
3194 * partially overlaps it).
3195 */
3196 if (new_key.offset != last_dest_end)
3197 drop_start = last_dest_end;
3198 else
3199 drop_start = new_key.offset;
3200
3201 /*
3157 * 1 - adjusting old extent (we may have to split it) 3202 * 1 - adjusting old extent (we may have to split it)
3158 * 1 - add new extent 3203 * 1 - add new extent
3159 * 1 - inode update 3204 * 1 - inode update
@@ -3182,7 +3227,7 @@ process_slot:
3182 } 3227 }
3183 3228
3184 ret = btrfs_drop_extents(trans, root, inode, 3229 ret = btrfs_drop_extents(trans, root, inode,
3185 new_key.offset, 3230 drop_start,
3186 new_key.offset + datal, 3231 new_key.offset + datal,
3187 1); 3232 1);
3188 if (ret) { 3233 if (ret) {
@@ -3283,7 +3328,7 @@ process_slot:
3283 aligned_end = ALIGN(new_key.offset + datal, 3328 aligned_end = ALIGN(new_key.offset + datal,
3284 root->sectorsize); 3329 root->sectorsize);
3285 ret = btrfs_drop_extents(trans, root, inode, 3330 ret = btrfs_drop_extents(trans, root, inode,
3286 new_key.offset, 3331 drop_start,
3287 aligned_end, 3332 aligned_end,
3288 1); 3333 1);
3289 if (ret) { 3334 if (ret) {
@@ -3321,27 +3366,12 @@ process_slot:
3321 btrfs_mark_buffer_dirty(leaf); 3366 btrfs_mark_buffer_dirty(leaf);
3322 btrfs_release_path(path); 3367 btrfs_release_path(path);
3323 3368
3324 inode_inc_iversion(inode); 3369 last_dest_end = new_key.offset + datal;
3325 inode->i_mtime = inode->i_ctime = CURRENT_TIME; 3370 ret = clone_finish_inode_update(trans, inode,
3326 3371 last_dest_end,
3327 /* 3372 destoff, olen);
3328 * we round up to the block size at eof when 3373 if (ret)
3329 * determining which extents to clone above,
3330 * but shouldn't round up the file size
3331 */
3332 endoff = new_key.offset + datal;
3333 if (endoff > destoff+olen)
3334 endoff = destoff+olen;
3335 if (endoff > inode->i_size)
3336 btrfs_i_size_write(inode, endoff);
3337
3338 ret = btrfs_update_inode(trans, root, inode);
3339 if (ret) {
3340 btrfs_abort_transaction(trans, root, ret);
3341 btrfs_end_transaction(trans, root);
3342 goto out; 3374 goto out;
3343 }
3344 ret = btrfs_end_transaction(trans, root);
3345 if (new_key.offset + datal >= destoff + len) 3375 if (new_key.offset + datal >= destoff + len)
3346 break; 3376 break;
3347 } 3377 }
@@ -3350,6 +3380,34 @@ process_slot:
3350 } 3380 }
3351 ret = 0; 3381 ret = 0;
3352 3382
3383 if (last_dest_end < destoff + len) {
3384 /*
3385 * We have an implicit hole (NO_HOLES feature is enabled) that
3386 * fully or partially overlaps our cloning range at its end.
3387 */
3388 btrfs_release_path(path);
3389
3390 /*
3391 * 1 - remove extent(s)
3392 * 1 - inode update
3393 */
3394 trans = btrfs_start_transaction(root, 2);
3395 if (IS_ERR(trans)) {
3396 ret = PTR_ERR(trans);
3397 goto out;
3398 }
3399 ret = btrfs_drop_extents(trans, root, inode,
3400 last_dest_end, destoff + len, 1);
3401 if (ret) {
3402 if (ret != -EOPNOTSUPP)
3403 btrfs_abort_transaction(trans, root, ret);
3404 btrfs_end_transaction(trans, root);
3405 goto out;
3406 }
3407 ret = clone_finish_inode_update(trans, inode, destoff + len,
3408 destoff, olen);
3409 }
3410
3353out: 3411out:
3354 btrfs_free_path(path); 3412 btrfs_free_path(path);
3355 vfree(buf); 3413 vfree(buf);