diff options
-rw-r--r-- | fs/btrfs/ioctl.c | 26 |
1 files changed, 22 insertions, 4 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 38f2169b73a4..f0b4237cedfc 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -3020,7 +3020,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode, | |||
3020 | /* clone data */ | 3020 | /* clone data */ |
3021 | key.objectid = btrfs_ino(src); | 3021 | key.objectid = btrfs_ino(src); |
3022 | key.type = BTRFS_EXTENT_DATA_KEY; | 3022 | key.type = BTRFS_EXTENT_DATA_KEY; |
3023 | key.offset = 0; | 3023 | key.offset = off; |
3024 | 3024 | ||
3025 | while (1) { | 3025 | while (1) { |
3026 | /* | 3026 | /* |
@@ -3032,6 +3032,17 @@ static int btrfs_clone(struct inode *src, struct inode *inode, | |||
3032 | 0, 0); | 3032 | 0, 0); |
3033 | if (ret < 0) | 3033 | if (ret < 0) |
3034 | goto out; | 3034 | goto out; |
3035 | /* | ||
3036 | * First search, if no extent item that starts at offset off was | ||
3037 | * found but the previous item is an extent item, it's possible | ||
3038 | * it might overlap our target range, therefore process it. | ||
3039 | */ | ||
3040 | if (key.offset == off && ret > 0 && path->slots[0] > 0) { | ||
3041 | btrfs_item_key_to_cpu(path->nodes[0], &key, | ||
3042 | path->slots[0] - 1); | ||
3043 | if (key.type == BTRFS_EXTENT_DATA_KEY) | ||
3044 | path->slots[0]--; | ||
3045 | } | ||
3035 | 3046 | ||
3036 | nritems = btrfs_header_nritems(path->nodes[0]); | 3047 | nritems = btrfs_header_nritems(path->nodes[0]); |
3037 | process_slot: | 3048 | process_slot: |
@@ -3081,10 +3092,16 @@ process_slot: | |||
3081 | extent); | 3092 | extent); |
3082 | } | 3093 | } |
3083 | 3094 | ||
3084 | if (key.offset + datal <= off || | 3095 | /* |
3085 | key.offset >= off + len - 1) { | 3096 | * The first search might have left us at an extent |
3097 | * item that ends before our target range's start, can | ||
3098 | * happen if we have holes and NO_HOLES feature enabled. | ||
3099 | */ | ||
3100 | if (key.offset + datal <= off) { | ||
3086 | path->slots[0]++; | 3101 | path->slots[0]++; |
3087 | goto process_slot; | 3102 | goto process_slot; |
3103 | } else if (key.offset >= off + len) { | ||
3104 | break; | ||
3088 | } | 3105 | } |
3089 | 3106 | ||
3090 | size = btrfs_item_size_nr(leaf, slot); | 3107 | size = btrfs_item_size_nr(leaf, slot); |
@@ -3291,6 +3308,8 @@ process_slot: | |||
3291 | goto out; | 3308 | goto out; |
3292 | } | 3309 | } |
3293 | ret = btrfs_end_transaction(trans, root); | 3310 | ret = btrfs_end_transaction(trans, root); |
3311 | if (new_key.offset + datal >= destoff + len) | ||
3312 | break; | ||
3294 | } | 3313 | } |
3295 | btrfs_release_path(path); | 3314 | btrfs_release_path(path); |
3296 | key.offset++; | 3315 | key.offset++; |
@@ -3298,7 +3317,6 @@ process_slot: | |||
3298 | ret = 0; | 3317 | ret = 0; |
3299 | 3318 | ||
3300 | out: | 3319 | out: |
3301 | btrfs_release_path(path); | ||
3302 | btrfs_free_path(path); | 3320 | btrfs_free_path(path); |
3303 | vfree(buf); | 3321 | vfree(buf); |
3304 | return ret; | 3322 | return ret; |