From 846f838797c7ade4effd3868eb16d54c553299a1 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 25 Oct 2018 14:09:25 +0800 Subject: [PATCH] btrfs-progs: check: orig: Use mismatch_dir_hash_record to record bad dir items This changes reporting from current in-place, like: ERROR: DIR_ITEM[256 751495445] name foor.WvG1c1TdU namelen 13 filetype 1 mismatch with its hash, wanted 751495445 have 2870353892 root 5 root dir 256 error To new summary report at the end of the pass: root 5 root dir 256 error root 5 inode 256 errors 40000 Dir items with mismatch hash: name: foor.WvG1c1Td namelen: 13 wanted 0xab161fe4 has 0x2ccae915 Also, with mismatch_dir_hash_record structure, it provides the base for later original mode repair. Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- check/main.c | 76 ++++++++++++++++++++++++++++++++++++++++--- check/mode-original.h | 14 ++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/check/main.c b/check/main.c index 6ffab157..7634ec28 100644 --- a/check/main.c +++ b/check/main.c @@ -462,6 +462,8 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) struct inode_backref *tmp; struct orphan_data_extent *src_orphan; struct orphan_data_extent *dst_orphan; + struct mismatch_dir_hash_record *hash_record; + struct mismatch_dir_hash_record *new_record; struct rb_node *rb; size_t size; int ret; @@ -473,6 +475,7 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) rec->refs = 1; INIT_LIST_HEAD(&rec->backrefs); INIT_LIST_HEAD(&rec->orphan_extents); + INIT_LIST_HEAD(&rec->mismatch_dir_hash); rec->holes = RB_ROOT; list_for_each_entry(orig, &orig_rec->backrefs, list) { @@ -494,6 +497,16 @@ static struct inode_record *clone_inode_rec(struct inode_record *orig_rec) memcpy(dst_orphan, src_orphan, sizeof(*src_orphan)); list_add_tail(&dst_orphan->list, &rec->orphan_extents); } + list_for_each_entry(hash_record, &orig_rec->mismatch_dir_hash, list) { + size = sizeof(*hash_record) + hash_record->namelen; + new_record = malloc(size); + if (!new_record) { + ret = -ENOMEM; + goto cleanup; + } + memcpy(&new_record, hash_record, size); + list_add_tail(&new_record->list, &rec->mismatch_dir_hash); + } ret = copy_file_extent_holes(&rec->holes, &orig_rec->holes); if (ret < 0) goto cleanup_rb; @@ -522,6 +535,13 @@ cleanup: list_del(&orig->list); free(orig); } + if (!list_empty(&rec->mismatch_dir_hash)) { + list_for_each_entry_safe(hash_record, new_record, + &rec->mismatch_dir_hash, list) { + list_del(&hash_record->list); + free(hash_record); + } + } free(rec); @@ -621,6 +641,25 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec) round_up(rec->isize, root->fs_info->sectorsize)); } + + /* Print dir item with mismatch hash */ + if (errors & I_ERR_MISMATCH_DIR_HASH) { + struct mismatch_dir_hash_record *hash_record; + + fprintf(stderr, "Dir items with mismatch hash:\n"); + list_for_each_entry(hash_record, &rec->mismatch_dir_hash, + list) { + char *namebuf = (char *)(hash_record + 1); + u32 crc; + + crc = btrfs_name_hash(namebuf, hash_record->namelen); + fprintf(stderr, + "\tname: %.*s namelen: %u wanted 0x%08x has 0x%08llx\n", + hash_record->namelen, namebuf, + hash_record->namelen, crc, + hash_record->key.offset); + } + } } static void print_ref_error(int errors) @@ -682,6 +721,7 @@ static struct inode_record *get_inode_rec(struct cache_tree *inode_cache, rec->refs = 1; INIT_LIST_HEAD(&rec->backrefs); INIT_LIST_HEAD(&rec->orphan_extents); + INIT_LIST_HEAD(&rec->mismatch_dir_hash); rec->holes = RB_ROOT; node = malloc(sizeof(*node)); @@ -718,6 +758,8 @@ static void free_orphan_data_extents(struct list_head *orphan_extents) static void free_inode_rec(struct inode_record *rec) { struct inode_backref *backref; + struct mismatch_dir_hash_record *hash; + struct mismatch_dir_hash_record *next; if (--rec->refs > 0) return; @@ -727,6 +769,8 @@ static void free_inode_rec(struct inode_record *rec) list_del(&backref->list); free(backref); } + list_for_each_entry_safe(hash, next, &rec->mismatch_dir_hash, list) + free(hash); free_orphan_data_extents(&rec->orphan_extents); free_file_extent_holes(&rec->holes); free(rec); @@ -1273,6 +1317,25 @@ out: return has_parent ? 0 : 2; } +static int add_mismatch_dir_hash(struct inode_record *dir_rec, + struct btrfs_key *key, const char *namebuf, + int namelen) +{ + struct mismatch_dir_hash_record *hash_record; + + hash_record = malloc(sizeof(*hash_record) + namelen); + if (!hash_record) { + error("failed to allocate memory for mismatch dir hash rec"); + return -ENOMEM; + } + memcpy(&hash_record->key, key, sizeof(*key)); + memcpy(hash_record + 1, namebuf, namelen); + hash_record->namelen = namelen; + + list_add(&hash_record->list, &dir_rec->mismatch_dir_hash); + return 0; +} + static int process_dir_item(struct extent_buffer *eb, int slot, struct btrfs_key *key, struct shared_node *active_node) @@ -1300,6 +1363,8 @@ static int process_dir_item(struct extent_buffer *eb, di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item); total = btrfs_item_size_nr(eb, slot); while (cur < total) { + int ret; + nritems++; btrfs_dir_item_key_to_cpu(eb, di, &location); name_len = btrfs_dir_name_len(eb, di); @@ -1324,10 +1389,12 @@ static int process_dir_item(struct extent_buffer *eb, if (key->type == BTRFS_DIR_ITEM_KEY && key->offset != btrfs_name_hash(namebuf, len)) { - rec->errors |= I_ERR_ODD_DIR_ITEM; - error("DIR_ITEM[%llu %llu] name %s namelen %u filetype %u mismatch with its hash, wanted %llu have %llu", - key->objectid, key->offset, namebuf, len, filetype, - key->offset, btrfs_name_hash(namebuf, len)); + rec->errors |= I_ERR_MISMATCH_DIR_HASH; + ret = add_mismatch_dir_hash(rec, key, namebuf, len); + /* Fatal error, ENOMEM */ + if (ret < 0) + return ret; + goto next; } if (location.type == BTRFS_INODE_ITEM_KEY) { @@ -1348,6 +1415,7 @@ static int process_dir_item(struct extent_buffer *eb, len, filetype, key->type, error); } +next: len = sizeof(*di) + name_len + data_len; di = (struct btrfs_dir_item *)((char *)di + len); cur += len; diff --git a/check/mode-original.h b/check/mode-original.h index ec2842e0..25ca2741 100644 --- a/check/mode-original.h +++ b/check/mode-original.h @@ -188,6 +188,7 @@ struct file_extent_hole { #define I_ERR_FILE_EXTENT_TOO_LARGE (1 << 15) #define I_ERR_ODD_INODE_FLAGS (1 << 16) #define I_ERR_INLINE_RAM_BYTES_WRONG (1 << 17) +#define I_ERR_MISMATCH_DIR_HASH (1 << 18) struct inode_record { struct list_head backrefs; @@ -213,10 +214,23 @@ struct inode_record { u64 extent_end; struct rb_root holes; struct list_head orphan_extents; + struct list_head mismatch_dir_hash; u32 refs; }; +/* + * To record one dir_item with mismatch hash. + * + * Since the hash is incorrect, we must record the hash (key). + */ +struct mismatch_dir_hash_record { + struct list_head list; + struct btrfs_key key; + int namelen; + /* namebuf follows here */ +}; + struct root_backref { struct list_head list; unsigned int found_dir_item:1;