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 <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2018-10-25 14:09:25 +08:00 committed by David Sterba
parent 38aff3d1ed
commit 846f838797
2 changed files with 86 additions and 4 deletions

View File

@ -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;

View File

@ -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;