aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-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);