Linux Btrfs filesystem development
 help / color / mirror / Atom feed
From: Qu Wenruo <wqu@suse.com>
To: linux-btrfs@vger.kernel.org
Subject: [PATCH 1/4] btrfs-progs: lowmem check: Add ability to repair dir item with mismatch hash
Date: Thu, 25 Oct 2018 15:44:46 +0800	[thread overview]
Message-ID: <20181025074449.32469-2-wqu@suse.com> (raw)
In-Reply-To: <20181025074449.32469-1-wqu@suse.com>

For DIR_ITEM with mismatch hash, we could just remove the offending dir
item.

Lowmem mode will handle the rest part (either re-create the correct
dir_item or move the orphan inode to lost+found).

This is especially important for old btrfs, since later kernel
introduces restrict tree-checker, which could detect such hash mismatch
and refuse to read the corrupted leaf.

With this repair ability, user could repair with btrfs check
--mode=lowmem --repair.

Link: https://bugzilla.opensuse.org/show_bug.cgi?id=1111991
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/mode-common.c | 51 +++++++++++++++++++++++++++++++++++++++++++++
 check/mode-common.h |  5 ++++-
 check/mode-lowmem.c | 46 +++++++++++++++++++++++++++++++++++-----
 check/mode-lowmem.h |  1 +
 ctree.h             |  3 +++
 dir-item.c          |  6 +-----
 6 files changed, 101 insertions(+), 11 deletions(-)

diff --git a/check/mode-common.c b/check/mode-common.c
index 15e2bbd1f30f..8660f43f6b20 100644
--- a/check/mode-common.c
+++ b/check/mode-common.c
@@ -742,3 +742,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 relie 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 6b05f8baf474..fda319267b04 100644
--- a/check/mode-common.h
+++ b/check/mode-common.h
@@ -121,5 +121,8 @@ 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 1bce44f5658a..fe3d5940faf0 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -1464,17 +1464,52 @@ out:
 	return ret;
 }
 
+/*
+ * 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)
@@ -1622,11 +1657,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);
@@ -1674,7 +1710,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);
diff --git a/check/mode-lowmem.h b/check/mode-lowmem.h
index 91f7b6b1db53..afd376ad7d5d 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/ctree.h b/ctree.h
index 4719962df67d..be0dacbe351d 100644
--- a/ctree.h
+++ b/ctree.h
@@ -2691,6 +2691,9 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
 			    struct btrfs_root *root, const char *name,
 			    u16 name_len, const void *data, u16 data_len,
 			    u64 dir);
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
+			      struct btrfs_path *path,
+			      const char *name, int name_len);
 /* inode-map.c */
 int btrfs_find_free_objectid(struct btrfs_trans_handle *trans,
 			     struct btrfs_root *fs_root,
diff --git a/dir-item.c b/dir-item.c
index d64f71e306e0..fa69dd1de185 100644
--- a/dir-item.c
+++ b/dir-item.c
@@ -22,10 +22,6 @@
 #include "hash.h"
 #include "transaction.h"
 
-static struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
-			      struct btrfs_path *path,
-			      const char *name, int name_len);
-
 static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
 						   *trans,
 						   struct btrfs_root *root,
@@ -323,7 +319,7 @@ static int verify_dir_item(struct btrfs_root *root,
 	return 0;
 }
 
-static struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
+struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
 			      struct btrfs_path *path,
 			      const char *name, int name_len)
 {
-- 
2.19.1


  reply	other threads:[~2018-10-25  7:44 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-25  7:44 [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash Qu Wenruo
2018-10-25  7:44 ` Qu Wenruo [this message]
2018-10-25  7:44 ` [PATCH 2/4] btrfs-progs: original check: Use mismatch_dir_hash_record to record bad dir items Qu Wenruo
2018-10-25  7:44 ` [PATCH 3/4] btrfs-progs: original check: Add ability to repair dir item with invalid hash Qu Wenruo
2018-10-25  7:44 ` [PATCH 4/4] btrfs-progs: fsck-tests: Make 026-bad-dir-item-name test case to verify if btrfs-check can also repair it Qu Wenruo
2019-01-07  6:51 ` [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash Qu Wenruo
2019-01-07  6:53   ` Qu Wenruo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181025074449.32469-2-wqu@suse.com \
    --to=wqu@suse.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox