Original can't handle mismatched well.
Since it only records inode mode and one filetype of dir_item or
dir_index. If backref has mismatched filetype, the one filetype
is untrusted. The only trusted thing is inode mode.
So, if we found mismatched filetype in a backref, just delete
dir_index and dir_item. Then next round of repair will add new
dir_index and dir_item whose filetype bases on the inode mode.
Transform delete_dir_index() to __delete_dir_item() for code reuse.
Signed-off-by: Su Yue <suy.fnst@xxxxxxxxxxxxxx>
---
cmds-check.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 57 insertions(+), 9 deletions(-)
diff --git a/cmds-check.c b/cmds-check.c
index 6091c7ef3442..156b4063bf40 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -3059,27 +3059,41 @@ static int add_missing_dir_index(struct btrfs_root *root,
return 0;
}
-static int delete_dir_index(struct btrfs_root *root,
- struct inode_backref *backref)
+static int __delete_dir_item(struct btrfs_root *root,
+ struct inode_backref *backref, bool is_index)
{
struct btrfs_trans_handle *trans;
struct btrfs_dir_item *di;
struct btrfs_path path;
int ret = 0;
+ u64 offset;
+ u8 type;
+
+ if (is_index) {
+ type = BTRFS_DIR_INDEX_KEY;
+ offset = backref->index;
+ } else {
+ type = BTRFS_DIR_ITEM_KEY;
+ offset = btrfs_name_hash(backref->name, backref->namelen);
+ }
trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans))
return PTR_ERR(trans);
- fprintf(stderr, "Deleting bad dir index [%llu,%u,%llu] root %llu\n",
- (unsigned long long)backref->dir,
- BTRFS_DIR_INDEX_KEY, (unsigned long long)backref->index,
- (unsigned long long)root->objectid);
+ fprintf(stderr, "Deleting bad dir %s [%llu,%u,%llu] root %llu\n",
+ is_index ? "index" : "item", (unsigned long long)backref->dir,
+ type, offset, (unsigned long long)root->objectid);
btrfs_init_path(&path);
- di = btrfs_lookup_dir_index(trans, root, &path, backref->dir,
- backref->name, backref->namelen,
- backref->index, -1);
+ if (is_index)
+ di = btrfs_lookup_dir_index(trans, root, &path, backref->dir,
+ backref->name, backref->namelen,
+ backref->index, -1);
+ else
+ di = btrfs_lookup_dir_item(trans, root, &path, backref->dir,
+ backref->name, backref->namelen,
+ -1);
if (IS_ERR(di)) {
ret = PTR_ERR(di);
btrfs_release_path(&path);
@@ -3099,6 +3113,18 @@ static int delete_dir_index(struct btrfs_root *root,
return ret;
}
+static int delete_dir_index(struct btrfs_root *root,
+ struct inode_backref *backref)
+{
+ return __delete_dir_item(root, backref, true);
+}
+
+static int delete_dir_item(struct btrfs_root *root,
+ struct inode_backref *backref)
+{
+ return __delete_dir_item(root, backref, false);
+}
+
static int __create_inode_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 ino, u64 size,
u64 nbytes, u64 nlink, u32 mode)
@@ -3228,6 +3254,22 @@ static int repair_inode_backrefs(struct btrfs_root *root,
continue;
}
+ if (delete &&
+ (backref->errors & REF_ERR_FILETYPE_UNMATCH)) {
+ if (backref->found_dir_index)
+ ret = delete_dir_index(root, backref);
+ if (ret)
+ break;
+ if (backref->found_dir_item)
+ ret = delete_dir_item(root, backref);
+ if (ret)
+ break;
+ backref->found_dir_index = 0;
+ backref->found_dir_item = 0;
+ repaired++;
+ continue;
+ }
+
if (!delete && !backref->found_dir_index &&
backref->found_dir_item && backref->found_inode_ref) {
ret = add_missing_dir_index(root, inode_cache, rec,
@@ -3285,6 +3327,12 @@ static int repair_inode_backrefs(struct btrfs_root *root,
btrfs_commit_transaction(trans, root);
backref->found_dir_index = 1;
backref->found_dir_item = 1;
+ /*
+ * We can't judge backre->filetype is right or not.
+ * Just rely on the inode mode.
+ */
+ if (rec->found_inode_item)
+ backref->errors &= ~REF_ERR_FILETYPE_UNMATCH;
repaired++;
}
--
2.16.1
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html