diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ioctl.c | 108 |
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 | ||
3015 | static 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); | ||
3042 | out: | ||
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 | */ |
3026 | static int btrfs_clone(struct inode *src, struct inode *inode, | 3057 | static 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 | |||
3353 | out: | 3411 | out: |
3354 | btrfs_free_path(path); | 3412 | btrfs_free_path(path); |
3355 | vfree(buf); | 3413 | vfree(buf); |