aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/send.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r--fs/btrfs/send.c158
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
149static 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
148static void fs_path_reset(struct fs_path *p) 156static 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
3763static 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 }
3791tlv_put_failure:
3792 fs_path_free(p);
3793 return ret;
3794}
3795
3755static int send_write_or_clone(struct send_ctx *sctx, 3796static 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
4023static 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;
4062out:
4063 btrfs_free_path(path);
4064 return ret;
4065}
4066
4067static 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
3982static int process_extent(struct send_ctx *sctx, 4101static 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;
4155out_hole:
4156 ret = maybe_send_hole(sctx, path, key);
4035out: 4157out:
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 }