From: Su Yue <suy.f...@cn.fujitsu.com> Previously, @err are assigned immediately after check but before repair. repair_extent_item()'s return value also confuses the caller. If error has been repaired and returns 0, check_extent_item() will try to continue check the nonexistent and cause flase alerts.
Here make repair_extent_item()'s return codes only represents status of the extent item, error bits are handled in caller of the repair function. Change of @err after repair. Signed-off-by: Su Yue <suy.f...@cn.fujitsu.com> Reviewed-by: Qu Wenruo <w...@suse.com> [Solve conflicts with DIR_ITEM hash mismatch patchset] Signed-off-by: Qu Wenruo <w...@suse.com> --- check/mode-lowmem.c | 118 +++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index 44708cf4b02a..7bcb833a567e 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c @@ -3850,62 +3850,70 @@ out: } /* - * Only delete backref if REFERENCER_MISSING now + * Only delete backref if REFERENCER_MISSING or REFERENCER_MISMATCH. * - * Returns <0 the extent was deleted - * Returns >0 the backref was deleted but extent still exists, returned value - * means error after repair - * Returns 0 nothing happened + * Returns <0 error + * Returns >0 the backref was deleted but extent still exists + * Returns =0 the whole extent item was deleted */ static int repair_extent_item(struct btrfs_root *root, struct btrfs_path *path, u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, - u64 owner, u64 offset, int err) + u64 owner, u64 offset) { struct btrfs_trans_handle *trans; struct btrfs_root *extent_root = root->fs_info->extent_root; 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)) == 0) - return err; - ret = avoid_extents_overwrite(root->fs_info); if (ret) - return err; + return ret; trans = btrfs_start_transaction(extent_root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); errno = -ret; error("fail to start transaction: %m"); - /* nothing happened */ - ret = 0; goto out; } /* 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; + if (!ret) printf("Delete backref in extent [%llu %llu]\n", bytenr, num_bytes); - } else { + else { error("fail to delete backref in extent [%llu %llu]", bytenr, num_bytes); + btrfs_abort_transaction(trans, ret); + goto out; } btrfs_commit_transaction(trans, extent_root); - /* 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; + if (ret > 0) { + /* odd, there must be one block group before at least */ + if (path->slots[0] == 0) { + ret = -EUCLEAN; + goto out; + } + /* + * btrfs_free_extent() has deleted the extent item, + * let path point to last checked item. + */ + if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) + path->slots[0] = btrfs_header_nritems(path->nodes[0]) - 1; + else + path->slots[0]--; + + ret = 0; + } else if (ret == 0) { + ret = 1; + } + out: return ret; } @@ -3923,7 +3931,6 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, 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; @@ -3941,6 +3948,8 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, struct btrfs_key key; int ret; int err = 0; + int tmp_err; + u32 ptr_offset; btrfs_item_key_to_cpu(eb, &key, slot); if (key.type == BTRFS_EXTENT_ITEM_KEY) { @@ -3986,21 +3995,22 @@ static int check_extent_item(struct btrfs_fs_info *fs_info, /* New METADATA_ITEM */ level = key.offset; } - end = (unsigned long)ei + item_size; + ptr_offset = ptr - (unsigned long)ei; next: /* Reached extent item end normally */ - if (ptr == end) + if (ptr_offset == item_size) goto out; /* Beyond extent item end, wrong item size */ - if (ptr > end) { + if (ptr_offset > item_size) { err |= ITEM_SIZE_MISMATCH; error("extent item at bytenr %llu slot %d has wrong size", eb->start, slot); goto out; } + ptr = (unsigned long)ei + ptr_offset; parent = 0; root_objectid = 0; owner = 0; @@ -4013,52 +4023,68 @@ next: case BTRFS_TREE_BLOCK_REF_KEY: root_objectid = offset; owner = level; - ret = check_tree_block_backref(fs_info, offset, key.objectid, - level); - err |= ret; + tmp_err = check_tree_block_backref(fs_info, offset, + key.objectid, level); break; case BTRFS_SHARED_BLOCK_REF_KEY: parent = offset; - ret = check_shared_block_backref(fs_info, offset, key.objectid, - level); - err |= ret; + tmp_err = check_shared_block_backref(fs_info, offset, + key.objectid, level); break; case BTRFS_EXTENT_DATA_REF_KEY: dref = (struct btrfs_extent_data_ref *)(&iref->offset); 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; + tmp_err = check_extent_data_backref(fs_info, root_objectid, + owner, owner_offset, key.objectid, key.offset, + btrfs_extent_data_ref_count(eb, dref)); break; case BTRFS_SHARED_DATA_REF_KEY: parent = offset; - ret = check_shared_data_backref(fs_info, offset, key.objectid); - err |= ret; + tmp_err = check_shared_data_backref(fs_info, offset, + key.objectid); break; default: error("extent[%llu %d %llu] has unknown ref type: %d", key.objectid, key.type, key.offset, type); - ret = UNKNOWN_TYPE; - err |= ret; + err |= UNKNOWN_TYPE; + goto out; } - if (err && repair) { + if ((tmp_err & (REFERENCER_MISSING | REFERENCER_MISMATCH)) + && repair) { ret = repair_extent_item(fs_info->extent_root, path, key.objectid, num_bytes, parent, root_objectid, - owner, owner_offset, ret); - if (ret < 0) + owner, owner_offset); + if (ret < 0) { + err |= tmp_err; + err |= FATAL_ERROR; goto out; - if (ret) { + } else if (ret == 0) { + err = 0; + goto out; + } else if (ret > 0) { + /* + * The error has been repaired which means the + * extent item is still existed with other backrefs, + * go to check next. + */ + tmp_err &= ~REFERENCER_MISSING; + tmp_err &= ~REFERENCER_MISMATCH; + err |= tmp_err; + eb = path->nodes[0]; + slot = path->slots[0]; + ei = btrfs_item_ptr(eb, slot, struct btrfs_extent_item); + item_size = btrfs_item_size_nr(eb, slot); goto next; - err = ret; } + } else { + err |= tmp_err; } - ptr += btrfs_extent_inline_ref_size(type); + ptr_offset += btrfs_extent_inline_ref_size(type); goto next; out: -- 2.21.0