From: Su Yue <suy.f...@cn.fujitsu.com> Introduce delete_extent_tree_item() and repair_extent_item() to do delete only.
while checking a extent tree, just delete wrong item. For extent item, free wrong backref. Otherwise, do delete. So the rest items in extent tree should be correct. Signed-off-by: Su Yue <suy.f...@cn.fujitsu.com> --- cmds-check.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 138 insertions(+), 13 deletions(-) diff --git a/cmds-check.c b/cmds-check.c index 7c9036c..0f26394 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -11084,24 +11084,77 @@ out: } /* + * Only delete backref if REFERENCER_MISSING now + * + * Returns <0 the extent was deleted + * Returns >0 the backref was deleted but extent is still existed, + * returned value means err after repair + * Returns 0 nothing happened + */ +static int repair_extent_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, + u64 owner, u64 offset, int err) +{ + struct btrfs_key old_key; + int freed = 0; + int ret; + + btrfs_item_key_to_cpu(path->nodes[0], &old_key, path->slots[0]); + + if (err & (REFERENCER_MISSING | REFERENCER_MISMATCH)) { + /* delete the backref */ + ret = btrfs_free_extent(trans, root->fs_info->fs_root, bytenr, + num_bytes, parent, root_objectid, owner, offset); + if (!ret) { + freed = 1; + err &= ~REFERENCER_MISSING; + printf("Delete backref in extent [%llu %llu]\n", + bytenr, num_bytes); + } else { + error("fail to delete backref in extent [%llu %llu]\n", + bytenr, num_bytes); + } + } + + /* btrfs_free_extent may delete the extent */ + btrfs_release_path(path); + ret = btrfs_search_slot(NULL, root, &old_key, path, 0, 0); + + if (ret) + ret = -ENOENT; + else if (freed) + ret = err; + return ret; +} + +/* * This function will check a given extent item, including its backref and * itself (like crossing stripe boundary and type) * * Since we don't use extent_record anymore, introduce new error bit */ -static int check_extent_item(struct btrfs_fs_info *fs_info, - struct extent_buffer *eb, int slot) +static int check_extent_item(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + struct btrfs_path *path) { struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *iref; struct btrfs_extent_data_ref *dref; + struct extent_buffer *eb = path->nodes[0]; unsigned long end; unsigned long ptr; + int slot = path->slots[0]; int type; u32 nodesize = btrfs_super_nodesize(fs_info->super_copy); u32 item_size = btrfs_item_size_nr(eb, slot); u64 flags; u64 offset; + u64 parent; + u64 num_bytes; + u64 root_objectid; + u64 owner; + u64 owner_offset; int metadata = 0; int level; struct btrfs_key key; @@ -11109,10 +11162,13 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, int err = 0; btrfs_item_key_to_cpu(eb, &key, slot); - if (key.type == BTRFS_EXTENT_ITEM_KEY) + if (key.type == BTRFS_EXTENT_ITEM_KEY) { bytes_used += key.offset; - else + num_bytes = key.offset; + } else { bytes_used += nodesize; + num_bytes = nodesize; + } if (item_size < sizeof(*ei)) { /* @@ -11150,7 +11206,6 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, level = key.offset; } end = (unsigned long)ei + item_size; - next: /* Reached extent item end normally */ if (ptr == end) @@ -11164,42 +11219,63 @@ next: goto out; } + parent = 0; + root_objectid = 0; + owner = 0; + owner_offset = 0; /* Now check every backref in this extent item */ iref = (struct btrfs_extent_inline_ref *)ptr; type = btrfs_extent_inline_ref_type(eb, iref); offset = btrfs_extent_inline_ref_offset(eb, iref); switch (type) { case BTRFS_TREE_BLOCK_REF_KEY: + root_objectid = offset; + owner = level; ret = check_tree_block_backref(fs_info, offset, key.objectid, level); err |= ret; break; case BTRFS_SHARED_BLOCK_REF_KEY: + parent = offset; ret = check_shared_block_backref(fs_info, offset, key.objectid, level); err |= ret; break; case BTRFS_EXTENT_DATA_REF_KEY: dref = (struct btrfs_extent_data_ref *)(&iref->offset); - ret = check_extent_data_backref(fs_info, - btrfs_extent_data_ref_root(eb, dref), - btrfs_extent_data_ref_objectid(eb, dref), - btrfs_extent_data_ref_offset(eb, dref), - key.objectid, key.offset, - btrfs_extent_data_ref_count(eb, dref)); + root_objectid = btrfs_extent_data_ref_root(eb, dref); + owner = btrfs_extent_data_ref_objectid(eb, dref); + owner_offset = btrfs_extent_data_ref_offset(eb, dref); + ret = check_extent_data_backref(fs_info, root_objectid, owner, + owner_offset, key.objectid, key.offset, + btrfs_extent_data_ref_count(eb, dref)); err |= ret; break; case BTRFS_SHARED_DATA_REF_KEY: + parent = offset; ret = check_shared_data_backref(fs_info, offset, key.objectid); err |= ret; break; default: error("extent[%llu %d %llu] has unknown ref type: %d", key.objectid, key.type, key.offset, type); - err |= UNKNOWN_TYPE; + ret = UNKNOWN_TYPE; + err |= ret; goto out; } + if (err && repair) { + ret = repair_extent_item(trans, fs_info->extent_root, path, + key.objectid, num_bytes, parent, root_objectid, + owner, owner_offset, ret); + if (ret < 0) + goto out; + if (ret) { + goto next; + err = ret; + } + } + ptr += btrfs_extent_inline_ref_size(type); goto next; @@ -11571,6 +11647,39 @@ out: return err; } +static int delete_extent_tree_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path) +{ + struct btrfs_key key; + int ret = 0; + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + btrfs_release_path(path); + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); + if (ret) { + ret = -ENOENT; + goto out; + } + + ret = btrfs_del_item(trans, root, path); + if (ret) + goto out; + + if (path->slots[0] == 0) + btrfs_prev_leaf(root, path); + else + path->slots[0]--; +out: + if (ret) + error("failed to delete root %llu item[%llu, %u, %llu]\n", + root->objectid, key.objectid, key.type, key.offset); + else + printf("deleted root %llu item[%llu, %u, %llu]\n", + root->objectid, key.objectid, key.type, key.offset); + return ret; +} + /* * Main entry function to check known items and update related accounting info */ @@ -11609,6 +11718,9 @@ again: break; case BTRFS_BLOCK_GROUP_ITEM_KEY: ret = check_block_group_item(fs_info, eb, slot); + if (repair && + ret & REFERENCER_MISSING) + ret = delete_extent_tree_item(trans, root, path); err |= ret; break; case BTRFS_DEV_ITEM_KEY: @@ -11625,7 +11737,7 @@ again: break; case BTRFS_EXTENT_ITEM_KEY: case BTRFS_METADATA_ITEM_KEY: - ret = check_extent_item(fs_info, eb, slot); + ret = check_extent_item(trans, fs_info, path); err |= ret; break; case BTRFS_EXTENT_CSUM_KEY: @@ -11635,6 +11747,9 @@ again: case BTRFS_TREE_BLOCK_REF_KEY: ret = check_tree_block_backref(fs_info, key.offset, key.objectid, -1); + if (repair && + ret & (REFERENCER_MISMATCH | REFERENCER_MISSING)) + ret = delete_extent_tree_item(trans, root, path); err |= ret; break; case BTRFS_EXTENT_DATA_REF_KEY: @@ -11645,16 +11760,26 @@ again: btrfs_extent_data_ref_offset(eb, dref), key.objectid, 0, btrfs_extent_data_ref_count(eb, dref)); + if (repair && + ret & (REFERENCER_MISMATCH | REFERENCER_MISSING)) + ret = delete_extent_tree_item(trans, root, path); err |= ret; break; case BTRFS_SHARED_BLOCK_REF_KEY: ret = check_shared_block_backref(fs_info, key.offset, key.objectid, -1); + if (repair && + ret & (REFERENCER_MISMATCH | REFERENCER_MISSING)) + ret = delete_extent_tree_item(trans, root, path); err |= ret; break; case BTRFS_SHARED_DATA_REF_KEY: ret = check_shared_data_backref(fs_info, key.offset, key.objectid); + if (repair && + ret & (REFERENCER_MISMATCH | REFERENCER_MISSING)) + ret = delete_extent_tree_item(trans, root, path); + err |= ret; break; default: -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html