summaryrefslogtreecommitdiff
path: root/check
diff options
context:
space:
mode:
Diffstat (limited to 'check')
-rw-r--r--check/main.c235
-rw-r--r--check/mode-common.c51
-rw-r--r--check/mode-common.h4
-rw-r--r--check/mode-lowmem.c89
-rw-r--r--check/mode-lowmem.h1
-rw-r--r--check/mode-original.h14
6 files changed, 379 insertions, 15 deletions
diff --git a/check/main.c b/check/main.c
index db18827..7547209 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;
@@ -2590,6 +2658,41 @@ out:
return ret;
}
+static int repair_mismatch_dir_hash(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct inode_record *rec)
+{
+ struct mismatch_dir_hash_record *hash;
+ int ret;
+
+ printf(
+ "Deleting bad dir items with invalid hash for root %llu ino %llu\n",
+ root->root_key.objectid, rec->ino);
+ while (!list_empty(&rec->mismatch_dir_hash)) {
+ char *namebuf;
+
+ hash = list_entry(rec->mismatch_dir_hash.next,
+ struct mismatch_dir_hash_record, list);
+ namebuf = (char *)(hash + 1);
+
+ ret = delete_corrupted_dir_item(trans, root, &hash->key,
+ namebuf, hash->namelen);
+ if (ret < 0)
+ break;
+
+ /* Also reduce dir isize */
+ rec->found_size -= hash->namelen;
+ list_del(&hash->list);
+ free(hash);
+ }
+ if (!ret) {
+ rec->errors &= ~I_ERR_MISMATCH_DIR_HASH;
+ /* We rely on later dir isize repair to reset dir isize */
+ rec->errors |= I_ERR_DIR_ISIZE_WRONG;
+ }
+ return ret;
+}
+
static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
{
struct btrfs_trans_handle *trans;
@@ -2603,7 +2706,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
I_ERR_FILE_EXTENT_ORPHAN |
I_ERR_FILE_EXTENT_DISCOUNT |
I_ERR_FILE_NBYTES_WRONG |
- I_ERR_INLINE_RAM_BYTES_WRONG)))
+ I_ERR_INLINE_RAM_BYTES_WRONG |
+ I_ERR_MISMATCH_DIR_HASH)))
return rec->errors;
/*
@@ -2618,6 +2722,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
return PTR_ERR(trans);
btrfs_init_path(&path);
+ if (!ret && rec->errors & I_ERR_MISMATCH_DIR_HASH)
+ ret = repair_mismatch_dir_hash(trans, root, rec);
if (rec->errors & I_ERR_NO_INODE_ITEM)
ret = repair_inode_no_item(trans, root, &path, rec);
if (!ret && rec->errors & I_ERR_FILE_EXTENT_ORPHAN)
@@ -2712,6 +2818,11 @@ static int check_inode_recs(struct btrfs_root *root,
rec = get_inode_rec(inode_cache, root_dirid, 0);
BUG_ON(IS_ERR(rec));
if (rec) {
+ if (repair) {
+ ret = try_repair_inode(root, rec);
+ if (ret < 0)
+ error++;
+ }
ret = check_root_dir(rec);
if (ret) {
fprintf(stderr, "root %llu root dir %llu error\n",
@@ -3362,9 +3473,10 @@ skip_walking:
printf("Try to repair the btree for root %llu\n",
root->root_key.objectid);
ret = repair_btree(root, &corrupt_blocks);
- if (ret < 0)
+ if (ret < 0) {
errno = -ret;
fprintf(stderr, "Failed to repair btree: %m\n");
+ }
if (!ret)
printf("Btree for root %llu is fixed\n",
root->root_key.objectid);
@@ -7944,6 +8056,13 @@ static int check_device_used(struct device_record *dev_rec,
struct device_extent_record *dev_extent_rec;
u64 total_byte = 0;
+ if (dev_rec->byte_used > dev_rec->total_byte) {
+ error(
+ "device %llu has incorrect used bytes %llu > total bytes %llu",
+ dev_rec->devid, dev_rec->byte_used, dev_rec->total_byte);
+ return -EUCLEAN;
+ }
+
cache = search_cache_extent2(&dext_cache->tree, dev_rec->devid, 0);
while (cache) {
dev_extent_rec = container_of(cache,
@@ -8230,6 +8349,102 @@ out:
return ret;
}
+/*
+ * Check if all dev extents are valid (not overlapping nor beyond device
+ * boundary).
+ *
+ * Dev extents <-> chunk cross checking is already done in check_chunks().
+ */
+static int check_dev_extents(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_path path;
+ struct btrfs_key key;
+ struct btrfs_root *dev_root = fs_info->dev_root;
+ int ret;
+ u64 prev_devid = 0;
+ u64 prev_dev_ext_end = 0;
+
+ btrfs_init_path(&path);
+
+ key.objectid = 1;
+ key.type = BTRFS_DEV_EXTENT_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to search device tree: %m");
+ goto out;
+ }
+ if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+ ret = btrfs_next_leaf(dev_root, &path);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to find next leaf: %m");
+ goto out;
+ }
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+ }
+
+ while (1) {
+ struct btrfs_dev_extent *dev_ext;
+ struct btrfs_device *dev;
+ u64 devid;
+ u64 physical_offset;
+ u64 physical_len;
+
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.type != BTRFS_DEV_EXTENT_KEY)
+ break;
+ dev_ext = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_dev_extent);
+ devid = key.objectid;
+ physical_offset = key.offset;
+ physical_len = btrfs_dev_extent_length(path.nodes[0], dev_ext);
+
+ dev = btrfs_find_device(fs_info, devid, NULL, NULL);
+ if (!dev) {
+ error("failed to find device with devid %llu", devid);
+ ret = -EUCLEAN;
+ goto out;
+ }
+ if (prev_devid == devid && prev_dev_ext_end > physical_offset) {
+ error(
+"dev extent devid %llu physical offset %llu overlap with previous dev extent end %llu",
+ devid, physical_offset, prev_dev_ext_end);
+ ret = -EUCLEAN;
+ goto out;
+ }
+ if (physical_offset + physical_len > dev->total_bytes) {
+ error(
+"dev extent devid %llu physical offset %llu len %llu is beyond device boudnary %llu",
+ devid, physical_offset, physical_len,
+ dev->total_bytes);
+ ret = -EUCLEAN;
+ goto out;
+ }
+ prev_devid = devid;
+ prev_dev_ext_end = physical_offset + physical_len;
+
+ ret = btrfs_next_item(dev_root, &path);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to find next leaf: %m");
+ goto out;
+ }
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ }
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
static int check_chunks_and_extents(struct btrfs_fs_info *fs_info)
{
struct rb_root dev_cache;
@@ -8324,6 +8539,12 @@ again:
goto out;
}
+ ret = check_dev_extents(fs_info);
+ if (ret < 0) {
+ err = ret;
+ goto out;
+ }
+
ret = check_chunks(&chunk_cache, &block_group_cache,
&dev_extent_cache, NULL, NULL, NULL, 0);
if (ret) {
@@ -8424,7 +8645,7 @@ static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(c, root->root_key.objectid);
- write_extent_buffer(c, root->fs_info->fsid,
+ write_extent_buffer(c, root->fs_info->fs_devices->metadata_uuid,
btrfs_header_fsid(), BTRFS_FSID_SIZE);
write_extent_buffer(c, root->fs_info->chunk_tree_uuid,
diff --git a/check/mode-common.c b/check/mode-common.c
index 2efa4dc..fed102b 100644
--- a/check/mode-common.c
+++ b/check/mode-common.c
@@ -744,3 +744,54 @@ void cleanup_excluded_extents(struct btrfs_fs_info *fs_info)
}
fs_info->excluded_extents = NULL;
}
+
+/*
+ * Delete one corrupted dir item whose hash doesn't match its name.
+ *
+ * Since its hash is incorrect, we can't use btrfs_name_hash() to calculate
+ * the search key, but rely on @di_key parameter to do the search.
+ */
+int delete_corrupted_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_key *di_key, char *namebuf,
+ u32 namelen)
+{
+ struct btrfs_dir_item *di_item;
+ struct btrfs_path path;
+ int ret;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(trans, root, di_key, &path, 0, 1);
+ if (ret > 0) {
+ error("key (%llu %u %llu) doesn't exist in root %llu",
+ di_key->objectid, di_key->type, di_key->offset,
+ root->root_key.objectid);
+ ret = -ENOENT;
+ goto out;
+ }
+ if (ret < 0) {
+ error("failed to search root %llu: %d",
+ root->root_key.objectid, ret);
+ goto out;
+ }
+
+ di_item = btrfs_match_dir_item_name(root, &path, namebuf, namelen);
+ if (!di_item) {
+ /*
+ * This is possible if the dir_item has incorrect namelen.
+ * But in that case, we shouldn't reach repair path here.
+ */
+ error("no dir item named '%s' found with key (%llu %u %llu)",
+ namebuf, di_key->objectid, di_key->type,
+ di_key->offset);
+ ret = -ENOENT;
+ goto out;
+ }
+ ret = btrfs_delete_one_dir_name(trans, root, &path, di_item);
+ if (ret < 0)
+ error("failed to delete one dir name: %d", ret);
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
diff --git a/check/mode-common.h b/check/mode-common.h
index 6b05f8b..1fb3cda 100644
--- a/check/mode-common.h
+++ b/check/mode-common.h
@@ -121,5 +121,9 @@ void reset_cached_block_groups(struct btrfs_fs_info *fs_info);
int pin_metadata_blocks(struct btrfs_fs_info *fs_info);
int exclude_metadata_blocks(struct btrfs_fs_info *fs_info);
void cleanup_excluded_extents(struct btrfs_fs_info *fs_info);
+int delete_corrupted_dir_item(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_key *di_key, char *namebuf,
+ u32 namelen);
#endif
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index 6fb397a..fc6228a 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -1474,16 +1474,52 @@ out:
}
/*
+ * A wrapper for delete_corrupted_dir_item(), with support part like
+ * start/commit transaction.
+ */
+static int lowmem_delete_corrupted_dir_item(struct btrfs_root *root,
+ struct btrfs_key *di_key,
+ char *namebuf, u32 name_len)
+{
+ struct btrfs_trans_handle *trans;
+ int ret;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("failed to start transaction: %d", ret);
+ return ret;
+ }
+
+ ret = delete_corrupted_dir_item(trans, root, di_key, namebuf, name_len);
+ if (ret < 0) {
+ btrfs_abort_transaction(trans, ret);
+ } else {
+ ret = btrfs_commit_transaction(trans, root);
+ if (ret < 0)
+ error("failed to commit transaction: %d", ret);
+ }
+ return ret;
+}
+
+/*
* Call repair_inode_item_missing and repair_ternary_lowmem to repair
*
* Returns error after repair
*/
-static int repair_dir_item(struct btrfs_root *root, u64 dirid, u64 ino,
- u64 index, u8 filetype, char *namebuf, u32 name_len,
- int err)
+static int repair_dir_item(struct btrfs_root *root, struct btrfs_key *di_key,
+ u64 ino, u64 index, u8 filetype, char *namebuf,
+ u32 name_len, int err)
{
+ u64 dirid = di_key->objectid;
int ret;
+ if (err & (DIR_ITEM_HASH_MISMATCH)) {
+ ret = lowmem_delete_corrupted_dir_item(root, di_key, namebuf,
+ name_len);
+ if (!ret)
+ err &= ~(DIR_ITEM_HASH_MISMATCH);
+ }
if (err & INODE_ITEM_MISSING) {
ret = repair_inode_item_missing(root, ino, filetype);
if (!ret)
@@ -1631,11 +1667,12 @@ begin:
if (di_key->type == BTRFS_DIR_ITEM_KEY &&
di_key->offset != btrfs_name_hash(namebuf, len)) {
- err |= -EIO;
error("root %llu DIR_ITEM[%llu %llu] name %s namelen %u filetype %u mismatch with its hash, wanted %llu have %llu",
root->objectid, di_key->objectid, di_key->offset,
namebuf, len, filetype, di_key->offset,
btrfs_name_hash(namebuf, len));
+ tmp_err |= DIR_ITEM_HASH_MISMATCH;
+ goto next;
}
btrfs_dir_item_key_to_cpu(node, di, &location);
@@ -1683,7 +1720,7 @@ begin:
next:
if (tmp_err && repair) {
- ret = repair_dir_item(root, di_key->objectid,
+ ret = repair_dir_item(root, di_key,
location.objectid, index,
imode_to_type(mode), namebuf,
name_len, tmp_err);
@@ -2142,7 +2179,7 @@ out:
error("failed to set nbytes in inode %llu root %llu",
ino, root->root_key.objectid);
else
- printf("Set nbytes in inode item %llu root %llu\n to %llu", ino,
+ printf("Set nbytes in inode item %llu root %llu to %llu\n", ino,
root->root_key.objectid, nbytes);
/* research path */
@@ -4095,6 +4132,8 @@ static int check_dev_item(struct btrfs_fs_info *fs_info,
u64 dev_id;
u64 used;
u64 total = 0;
+ u64 prev_devid = 0;
+ u64 prev_dev_ext_end = 0;
int ret;
dev_item = btrfs_item_ptr(eb, slot, struct btrfs_dev_item);
@@ -4102,6 +4141,12 @@ static int check_dev_item(struct btrfs_fs_info *fs_info,
used = btrfs_device_bytes_used(eb, dev_item);
total_bytes = btrfs_device_total_bytes(eb, dev_item);
+ if (used > total_bytes) {
+ error(
+ "device %llu has incorrect used bytes %llu > total bytes %llu",
+ dev_id, used, total_bytes);
+ return ACCOUNTING_MISMATCH;
+ }
key.objectid = dev_id;
key.type = BTRFS_DEV_EXTENT_KEY;
key.offset = 0;
@@ -4116,8 +4161,16 @@ static int check_dev_item(struct btrfs_fs_info *fs_info,
return REFERENCER_MISSING;
}
- /* Iterate dev_extents to calculate the used space of a device */
+ /*
+ * Iterate dev_extents to calculate the used space of a device
+ *
+ * Also make sure no dev extents overlap and end beyond device boundary
+ */
while (1) {
+ u64 devid;
+ u64 physical_offset;
+ u64 physical_len;
+
if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
goto next;
@@ -4129,7 +4182,27 @@ static int check_dev_item(struct btrfs_fs_info *fs_info,
ptr = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_dev_extent);
- total += btrfs_dev_extent_length(path.nodes[0], ptr);
+ devid = key.objectid;
+ physical_offset = key.offset;
+ physical_len = btrfs_dev_extent_length(path.nodes[0], ptr);
+
+ if (prev_devid == devid && physical_offset < prev_dev_ext_end) {
+ error(
+"dev extent devid %llu offset %llu len %llu overlap with previous dev extent end %llu",
+ devid, physical_offset, physical_len,
+ prev_dev_ext_end);
+ return ACCOUNTING_MISMATCH;
+ }
+ if (physical_offset + physical_len > total_bytes) {
+ error(
+"dev extent devid %llu offset %llu len %llu is beyond device boundary %llu",
+ devid, physical_offset, physical_len,
+ total_bytes);
+ return ACCOUNTING_MISMATCH;
+ }
+ prev_devid = devid;
+ prev_dev_ext_end = physical_offset + physical_len;
+ total += physical_len;
next:
ret = btrfs_next_item(dev_root, &path);
if (ret)
diff --git a/check/mode-lowmem.h b/check/mode-lowmem.h
index 0ad2a9e..46b9b19 100644
--- a/check/mode-lowmem.h
+++ b/check/mode-lowmem.h
@@ -45,6 +45,7 @@
#define BG_ACCOUNTING_ERROR (1<<21) /* Block group accounting error */
#define FATAL_ERROR (1<<22) /* Fatal bit for errno */
#define INODE_FLAGS_ERROR (1<<23) /* Invalid inode flags */
+#define DIR_ITEM_HASH_MISMATCH (1<<24) /* Dir item hash mismatch */
/*
* Error bit for low memory mode check.
diff --git a/check/mode-original.h b/check/mode-original.h
index ec2842e..25ca274 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;