|
@@ -1681,6 +1681,9 @@ static int is_inode_existent(struct send_ctx *sctx, u64 ino, u64 gen)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
+ if (ino == BTRFS_FIRST_FREE_OBJECTID)
|
|
|
+ return 1;
|
|
|
+
|
|
|
ret = get_cur_inode_state(sctx, ino, gen);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
@@ -1866,7 +1869,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
|
|
|
* not deleted and then re-created, if it was then we have no overwrite
|
|
|
* and we can just unlink this entry.
|
|
|
*/
|
|
|
- if (sctx->parent_root) {
|
|
|
+ if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
|
|
|
NULL, NULL, NULL);
|
|
|
if (ret < 0 && ret != -ENOENT)
|
|
@@ -1934,6 +1937,19 @@ static int did_overwrite_ref(struct send_ctx *sctx,
|
|
|
if (ret <= 0)
|
|
|
goto out;
|
|
|
|
|
|
+ if (dir != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
+ ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL,
|
|
|
+ NULL, NULL, NULL);
|
|
|
+ if (ret < 0 && ret != -ENOENT)
|
|
|
+ goto out;
|
|
|
+ if (ret) {
|
|
|
+ ret = 0;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (gen != dir_gen)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
/* check if the ref was overwritten by another ref */
|
|
|
ret = lookup_dir_item_inode(sctx->send_root, dir, name, name_len,
|
|
|
&ow_inode, &other_type);
|
|
@@ -3556,6 +3572,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
|
|
|
{
|
|
|
int ret = 0;
|
|
|
u64 ino = parent_ref->dir;
|
|
|
+ u64 ino_gen = parent_ref->dir_gen;
|
|
|
u64 parent_ino_before, parent_ino_after;
|
|
|
struct fs_path *path_before = NULL;
|
|
|
struct fs_path *path_after = NULL;
|
|
@@ -3576,6 +3593,8 @@ static int wait_for_parent_move(struct send_ctx *sctx,
|
|
|
* at get_cur_path()).
|
|
|
*/
|
|
|
while (ino > BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
+ u64 parent_ino_after_gen;
|
|
|
+
|
|
|
if (is_waiting_for_move(sctx, ino)) {
|
|
|
/*
|
|
|
* If the current inode is an ancestor of ino in the
|
|
@@ -3598,7 +3617,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
|
|
|
fs_path_reset(path_after);
|
|
|
|
|
|
ret = get_first_ref(sctx->send_root, ino, &parent_ino_after,
|
|
|
- NULL, path_after);
|
|
|
+ &parent_ino_after_gen, path_after);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before,
|
|
@@ -3615,10 +3634,20 @@ static int wait_for_parent_move(struct send_ctx *sctx,
|
|
|
if (ino > sctx->cur_ino &&
|
|
|
(parent_ino_before != parent_ino_after || len1 != len2 ||
|
|
|
memcmp(path_before->start, path_after->start, len1))) {
|
|
|
- ret = 1;
|
|
|
- break;
|
|
|
+ u64 parent_ino_gen;
|
|
|
+
|
|
|
+ ret = get_inode_info(sctx->parent_root, ino, NULL,
|
|
|
+ &parent_ino_gen, NULL, NULL, NULL,
|
|
|
+ NULL);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ if (ino_gen == parent_ino_gen) {
|
|
|
+ ret = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
ino = parent_ino_after;
|
|
|
+ ino_gen = parent_ino_after_gen;
|
|
|
}
|
|
|
|
|
|
out:
|
|
@@ -5277,6 +5306,81 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int range_is_hole_in_parent(struct send_ctx *sctx,
|
|
|
+ const u64 start,
|
|
|
+ const u64 end)
|
|
|
+{
|
|
|
+ struct btrfs_path *path;
|
|
|
+ struct btrfs_key key;
|
|
|
+ struct btrfs_root *root = sctx->parent_root;
|
|
|
+ u64 search_start = start;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ path = alloc_path_for_send();
|
|
|
+ if (!path)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ key.objectid = sctx->cur_ino;
|
|
|
+ key.type = BTRFS_EXTENT_DATA_KEY;
|
|
|
+ key.offset = search_start;
|
|
|
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ if (ret > 0 && path->slots[0] > 0)
|
|
|
+ path->slots[0]--;
|
|
|
+
|
|
|
+ while (search_start < end) {
|
|
|
+ struct extent_buffer *leaf = path->nodes[0];
|
|
|
+ int slot = path->slots[0];
|
|
|
+ struct btrfs_file_extent_item *fi;
|
|
|
+ u64 extent_end;
|
|
|
+
|
|
|
+ if (slot >= btrfs_header_nritems(leaf)) {
|
|
|
+ ret = btrfs_next_leaf(root, path);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ else if (ret > 0)
|
|
|
+ break;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ btrfs_item_key_to_cpu(leaf, &key, slot);
|
|
|
+ if (key.objectid < sctx->cur_ino ||
|
|
|
+ key.type < BTRFS_EXTENT_DATA_KEY)
|
|
|
+ goto next;
|
|
|
+ if (key.objectid > sctx->cur_ino ||
|
|
|
+ key.type > BTRFS_EXTENT_DATA_KEY ||
|
|
|
+ key.offset >= end)
|
|
|
+ break;
|
|
|
+
|
|
|
+ fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
|
|
|
+ if (btrfs_file_extent_type(leaf, fi) ==
|
|
|
+ BTRFS_FILE_EXTENT_INLINE) {
|
|
|
+ u64 size = btrfs_file_extent_inline_len(leaf, slot, fi);
|
|
|
+
|
|
|
+ extent_end = ALIGN(key.offset + size,
|
|
|
+ root->fs_info->sectorsize);
|
|
|
+ } else {
|
|
|
+ extent_end = key.offset +
|
|
|
+ btrfs_file_extent_num_bytes(leaf, fi);
|
|
|
+ }
|
|
|
+ if (extent_end <= start)
|
|
|
+ goto next;
|
|
|
+ if (btrfs_file_extent_disk_bytenr(leaf, fi) == 0) {
|
|
|
+ search_start = extent_end;
|
|
|
+ goto next;
|
|
|
+ }
|
|
|
+ ret = 0;
|
|
|
+ goto out;
|
|
|
+next:
|
|
|
+ path->slots[0]++;
|
|
|
+ }
|
|
|
+ ret = 1;
|
|
|
+out:
|
|
|
+ btrfs_free_path(path);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
|
|
|
struct btrfs_key *key)
|
|
|
{
|
|
@@ -5321,8 +5425,17 @@ static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- if (sctx->cur_inode_last_extent < key->offset)
|
|
|
- ret = send_hole(sctx, key->offset);
|
|
|
+ if (sctx->cur_inode_last_extent < key->offset) {
|
|
|
+ ret = range_is_hole_in_parent(sctx,
|
|
|
+ sctx->cur_inode_last_extent,
|
|
|
+ key->offset);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ else if (ret == 0)
|
|
|
+ ret = send_hole(sctx, key->offset);
|
|
|
+ else
|
|
|
+ ret = 0;
|
|
|
+ }
|
|
|
sctx->cur_inode_last_extent = extent_end;
|
|
|
return ret;
|
|
|
}
|