diff options
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r-- | fs/btrfs/send.c | 158 |
1 files changed, 149 insertions, 9 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 945d1db98f26..29803b4129fc 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
@@ -111,6 +111,7 @@ struct send_ctx { | |||
111 | int cur_inode_deleted; | 111 | int cur_inode_deleted; |
112 | u64 cur_inode_size; | 112 | u64 cur_inode_size; |
113 | u64 cur_inode_mode; | 113 | u64 cur_inode_mode; |
114 | u64 cur_inode_last_extent; | ||
114 | 115 | ||
115 | u64 send_progress; | 116 | u64 send_progress; |
116 | 117 | ||
@@ -145,6 +146,13 @@ struct name_cache_entry { | |||
145 | char name[]; | 146 | char name[]; |
146 | }; | 147 | }; |
147 | 148 | ||
149 | static int need_send_hole(struct send_ctx *sctx) | ||
150 | { | ||
151 | return (sctx->parent_root && !sctx->cur_inode_new && | ||
152 | !sctx->cur_inode_new_gen && !sctx->cur_inode_deleted && | ||
153 | S_ISREG(sctx->cur_inode_mode)); | ||
154 | } | ||
155 | |||
148 | static void fs_path_reset(struct fs_path *p) | 156 | static void fs_path_reset(struct fs_path *p) |
149 | { | 157 | { |
150 | if (p->reversed) { | 158 | if (p->reversed) { |
@@ -3752,6 +3760,39 @@ out: | |||
3752 | return ret; | 3760 | return ret; |
3753 | } | 3761 | } |
3754 | 3762 | ||
3763 | static int send_hole(struct send_ctx *sctx, u64 end) | ||
3764 | { | ||
3765 | struct fs_path *p = NULL; | ||
3766 | u64 offset = sctx->cur_inode_last_extent; | ||
3767 | u64 len; | ||
3768 | int ret = 0; | ||
3769 | |||
3770 | p = fs_path_alloc(); | ||
3771 | if (!p) | ||
3772 | return -ENOMEM; | ||
3773 | memset(sctx->read_buf, 0, BTRFS_SEND_READ_SIZE); | ||
3774 | while (offset < end) { | ||
3775 | len = min_t(u64, end - offset, BTRFS_SEND_READ_SIZE); | ||
3776 | |||
3777 | ret = begin_cmd(sctx, BTRFS_SEND_C_WRITE); | ||
3778 | if (ret < 0) | ||
3779 | break; | ||
3780 | ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p); | ||
3781 | if (ret < 0) | ||
3782 | break; | ||
3783 | TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p); | ||
3784 | TLV_PUT_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, offset); | ||
3785 | TLV_PUT(sctx, BTRFS_SEND_A_DATA, sctx->read_buf, len); | ||
3786 | ret = send_cmd(sctx); | ||
3787 | if (ret < 0) | ||
3788 | break; | ||
3789 | offset += len; | ||
3790 | } | ||
3791 | tlv_put_failure: | ||
3792 | fs_path_free(p); | ||
3793 | return ret; | ||
3794 | } | ||
3795 | |||
3755 | static int send_write_or_clone(struct send_ctx *sctx, | 3796 | static int send_write_or_clone(struct send_ctx *sctx, |
3756 | struct btrfs_path *path, | 3797 | struct btrfs_path *path, |
3757 | struct btrfs_key *key, | 3798 | struct btrfs_key *key, |
@@ -3979,6 +4020,84 @@ out: | |||
3979 | return ret; | 4020 | return ret; |
3980 | } | 4021 | } |
3981 | 4022 | ||
4023 | static int get_last_extent(struct send_ctx *sctx, u64 offset) | ||
4024 | { | ||
4025 | struct btrfs_path *path; | ||
4026 | struct btrfs_root *root = sctx->send_root; | ||
4027 | struct btrfs_file_extent_item *fi; | ||
4028 | struct btrfs_key key; | ||
4029 | u64 extent_end; | ||
4030 | u8 type; | ||
4031 | int ret; | ||
4032 | |||
4033 | path = alloc_path_for_send(); | ||
4034 | if (!path) | ||
4035 | return -ENOMEM; | ||
4036 | |||
4037 | sctx->cur_inode_last_extent = 0; | ||
4038 | |||
4039 | key.objectid = sctx->cur_ino; | ||
4040 | key.type = BTRFS_EXTENT_DATA_KEY; | ||
4041 | key.offset = offset; | ||
4042 | ret = btrfs_search_slot_for_read(root, &key, path, 0, 1); | ||
4043 | if (ret < 0) | ||
4044 | goto out; | ||
4045 | ret = 0; | ||
4046 | btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); | ||
4047 | if (key.objectid != sctx->cur_ino || key.type != BTRFS_EXTENT_DATA_KEY) | ||
4048 | goto out; | ||
4049 | |||
4050 | fi = btrfs_item_ptr(path->nodes[0], path->slots[0], | ||
4051 | struct btrfs_file_extent_item); | ||
4052 | type = btrfs_file_extent_type(path->nodes[0], fi); | ||
4053 | if (type == BTRFS_FILE_EXTENT_INLINE) { | ||
4054 | u64 size = btrfs_file_extent_inline_len(path->nodes[0], fi); | ||
4055 | extent_end = ALIGN(key.offset + size, | ||
4056 | sctx->send_root->sectorsize); | ||
4057 | } else { | ||
4058 | extent_end = key.offset + | ||
4059 | btrfs_file_extent_num_bytes(path->nodes[0], fi); | ||
4060 | } | ||
4061 | sctx->cur_inode_last_extent = extent_end; | ||
4062 | out: | ||
4063 | btrfs_free_path(path); | ||
4064 | return ret; | ||
4065 | } | ||
4066 | |||
4067 | static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path, | ||
4068 | struct btrfs_key *key) | ||
4069 | { | ||
4070 | struct btrfs_file_extent_item *fi; | ||
4071 | u64 extent_end; | ||
4072 | u8 type; | ||
4073 | int ret = 0; | ||
4074 | |||
4075 | if (sctx->cur_ino != key->objectid || !need_send_hole(sctx)) | ||
4076 | return 0; | ||
4077 | |||
4078 | if (sctx->cur_inode_last_extent == (u64)-1) { | ||
4079 | ret = get_last_extent(sctx, key->offset - 1); | ||
4080 | if (ret) | ||
4081 | return ret; | ||
4082 | } | ||
4083 | |||
4084 | fi = btrfs_item_ptr(path->nodes[0], path->slots[0], | ||
4085 | struct btrfs_file_extent_item); | ||
4086 | type = btrfs_file_extent_type(path->nodes[0], fi); | ||
4087 | if (type == BTRFS_FILE_EXTENT_INLINE) { | ||
4088 | u64 size = btrfs_file_extent_inline_len(path->nodes[0], fi); | ||
4089 | extent_end = ALIGN(key->offset + size, | ||
4090 | sctx->send_root->sectorsize); | ||
4091 | } else { | ||
4092 | extent_end = key->offset + | ||
4093 | btrfs_file_extent_num_bytes(path->nodes[0], fi); | ||
4094 | } | ||
4095 | if (sctx->cur_inode_last_extent < key->offset) | ||
4096 | ret = send_hole(sctx, key->offset); | ||
4097 | sctx->cur_inode_last_extent = extent_end; | ||
4098 | return ret; | ||
4099 | } | ||
4100 | |||
3982 | static int process_extent(struct send_ctx *sctx, | 4101 | static int process_extent(struct send_ctx *sctx, |
3983 | struct btrfs_path *path, | 4102 | struct btrfs_path *path, |
3984 | struct btrfs_key *key) | 4103 | struct btrfs_key *key) |
@@ -3995,7 +4114,7 @@ static int process_extent(struct send_ctx *sctx, | |||
3995 | goto out; | 4114 | goto out; |
3996 | if (ret) { | 4115 | if (ret) { |
3997 | ret = 0; | 4116 | ret = 0; |
3998 | goto out; | 4117 | goto out_hole; |
3999 | } | 4118 | } |
4000 | } else { | 4119 | } else { |
4001 | struct btrfs_file_extent_item *ei; | 4120 | struct btrfs_file_extent_item *ei; |
@@ -4031,7 +4150,10 @@ static int process_extent(struct send_ctx *sctx, | |||
4031 | goto out; | 4150 | goto out; |
4032 | 4151 | ||
4033 | ret = send_write_or_clone(sctx, path, key, found_clone); | 4152 | ret = send_write_or_clone(sctx, path, key, found_clone); |
4034 | 4153 | if (ret) | |
4154 | goto out; | ||
4155 | out_hole: | ||
4156 | ret = maybe_send_hole(sctx, path, key); | ||
4035 | out: | 4157 | out: |
4036 | return ret; | 4158 | return ret; |
4037 | } | 4159 | } |
@@ -4157,6 +4279,19 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end) | |||
4157 | } | 4279 | } |
4158 | 4280 | ||
4159 | if (S_ISREG(sctx->cur_inode_mode)) { | 4281 | if (S_ISREG(sctx->cur_inode_mode)) { |
4282 | if (need_send_hole(sctx)) { | ||
4283 | if (sctx->cur_inode_last_extent == (u64)-1) { | ||
4284 | ret = get_last_extent(sctx, (u64)-1); | ||
4285 | if (ret) | ||
4286 | goto out; | ||
4287 | } | ||
4288 | if (sctx->cur_inode_last_extent < | ||
4289 | sctx->cur_inode_size) { | ||
4290 | ret = send_hole(sctx, sctx->cur_inode_size); | ||
4291 | if (ret) | ||
4292 | goto out; | ||
4293 | } | ||
4294 | } | ||
4160 | ret = send_truncate(sctx, sctx->cur_ino, sctx->cur_inode_gen, | 4295 | ret = send_truncate(sctx, sctx->cur_ino, sctx->cur_inode_gen, |
4161 | sctx->cur_inode_size); | 4296 | sctx->cur_inode_size); |
4162 | if (ret < 0) | 4297 | if (ret < 0) |
@@ -4200,6 +4335,7 @@ static int changed_inode(struct send_ctx *sctx, | |||
4200 | 4335 | ||
4201 | sctx->cur_ino = key->objectid; | 4336 | sctx->cur_ino = key->objectid; |
4202 | sctx->cur_inode_new_gen = 0; | 4337 | sctx->cur_inode_new_gen = 0; |
4338 | sctx->cur_inode_last_extent = (u64)-1; | ||
4203 | 4339 | ||
4204 | /* | 4340 | /* |
4205 | * Set send_progress to current inode. This will tell all get_cur_xxx | 4341 | * Set send_progress to current inode. This will tell all get_cur_xxx |
@@ -4480,14 +4616,18 @@ static int changed_cb(struct btrfs_root *left_root, | |||
4480 | struct send_ctx *sctx = ctx; | 4616 | struct send_ctx *sctx = ctx; |
4481 | 4617 | ||
4482 | if (result == BTRFS_COMPARE_TREE_SAME) { | 4618 | if (result == BTRFS_COMPARE_TREE_SAME) { |
4483 | if (key->type != BTRFS_INODE_REF_KEY && | 4619 | if (key->type == BTRFS_INODE_REF_KEY || |
4484 | key->type != BTRFS_INODE_EXTREF_KEY) | 4620 | key->type == BTRFS_INODE_EXTREF_KEY) { |
4485 | return 0; | 4621 | ret = compare_refs(sctx, left_path, key); |
4486 | ret = compare_refs(sctx, left_path, key); | 4622 | if (!ret) |
4487 | if (!ret) | 4623 | return 0; |
4624 | if (ret < 0) | ||
4625 | return ret; | ||
4626 | } else if (key->type == BTRFS_EXTENT_DATA_KEY) { | ||
4627 | return maybe_send_hole(sctx, left_path, key); | ||
4628 | } else { | ||
4488 | return 0; | 4629 | return 0; |
4489 | if (ret < 0) | 4630 | } |
4490 | return ret; | ||
4491 | result = BTRFS_COMPARE_TREE_CHANGED; | 4631 | result = BTRFS_COMPARE_TREE_CHANGED; |
4492 | ret = 0; | 4632 | ret = 0; |
4493 | } | 4633 | } |