linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash
@ 2018-10-25  7:44 Qu Wenruo
  2018-10-25  7:44 ` [PATCH 1/4] btrfs-progs: lowmem check: Add ability to repair dir item with mismatch hash Qu Wenruo
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Qu Wenruo @ 2018-10-25  7:44 UTC (permalink / raw)
  To: linux-btrfs

This patchset can be fetched from github:
https://github.com/adam900710/btrfs-progs/tree/repair_bad_dir_item_hash

This is still based on the latest stable v4.17.1 branch (which is already a
little old now)

We have report from suse bugzilla where user report latest kernel refuse
to mount the fs.

The problem turns out to be that some old kernel (around Jan 2017) has a
bug that could leads to corrupted dir item with mismatch hash.

This patchset will allow btrfs check --repair to repair such problem.

Qu Wenruo (4):
  btrfs-progs: lowmem check: Add ability to repair dir item with
    mismatch hash
  btrfs-progs: original check: Use mismatch_dir_hash_record to record
    bad dir items
  btrfs-progs: original check: Add ability to repair dir item with
    invalid hash
  btrfs-progs: fsck-tests: Make 026-bad-dir-item-name test case to
    verify if btrfs-check can also repair it

 check/main.c                                  | 121 +++++++++++++++++-
 check/mode-common.c                           |  51 ++++++++
 check/mode-common.h                           |   5 +-
 check/mode-lowmem.c                           |  46 ++++++-
 check/mode-lowmem.h                           |   1 +
 check/mode-original.h                         |  14 ++
 ctree.h                                       |   3 +
 dir-item.c                                    |   6 +-
 .../026-bad-dir-item-name/description.txt     |  41 ++++++
 .../fsck-tests/026-bad-dir-item-name/test.sh  |  13 --
 10 files changed, 272 insertions(+), 29 deletions(-)
 create mode 100644 tests/fsck-tests/026-bad-dir-item-name/description.txt
 delete mode 100755 tests/fsck-tests/026-bad-dir-item-name/test.sh

-- 
2.19.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/4] btrfs-progs: lowmem check: Add ability to repair dir item with mismatch hash
  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
  2018-10-25  7:44 ` [PATCH 2/4] btrfs-progs: original check: Use mismatch_dir_hash_record to record bad dir items Qu Wenruo
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Qu Wenruo @ 2018-10-25  7:44 UTC (permalink / raw)
  To: linux-btrfs

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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/4] btrfs-progs: original check: Use mismatch_dir_hash_record to record bad dir items
  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 ` [PATCH 1/4] btrfs-progs: lowmem check: Add ability to repair dir item with mismatch hash Qu Wenruo
@ 2018-10-25  7:44 ` 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
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Qu Wenruo @ 2018-10-25  7:44 UTC (permalink / raw)
  To: linux-btrfs

This allow us to report the error better, from old in-place report 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 centralized report:
  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 basis for
later original mode repair.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 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 bc2ee22f7943..4a4f2a7c9cdb 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 ec2842e0b3be..25ca274118a7 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;
-- 
2.19.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/4] btrfs-progs: original check: Add ability to repair dir item with invalid hash
  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 ` [PATCH 1/4] btrfs-progs: lowmem check: Add ability to repair dir item with mismatch hash Qu Wenruo
  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 ` 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
  4 siblings, 0 replies; 7+ messages in thread
From: Qu Wenruo @ 2018-10-25  7:44 UTC (permalink / raw)
  To: linux-btrfs

The repair function is reusing delete_corrupted_dir_item().

Since the error can happen for root dir inode, also call
try_repair_inode() on root dir inode.

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 --repair.

Link: https://bugzilla.opensuse.org/show_bug.cgi?id=1111991
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/main.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 44 insertions(+), 1 deletion(-)

diff --git a/check/main.c b/check/main.c
index 4a4f2a7c9cdb..1abf6c994710 100644
--- a/check/main.c
+++ b/check/main.c
@@ -2658,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;
@@ -2671,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;
 
 	/*
@@ -2686,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)
@@ -2780,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",
-- 
2.19.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/4] btrfs-progs: fsck-tests: Make 026-bad-dir-item-name test case to verify if btrfs-check can also repair it
  2018-10-25  7:44 [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash Qu Wenruo
                   ` (2 preceding siblings ...)
  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 ` Qu Wenruo
  2019-01-07  6:51 ` [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash Qu Wenruo
  4 siblings, 0 replies; 7+ messages in thread
From: Qu Wenruo @ 2018-10-25  7:44 UTC (permalink / raw)
  To: linux-btrfs

Just remove the customized 'test.sh', then generic fsck test will do the
check-repair-check.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 .../026-bad-dir-item-name/description.txt     | 41 +++++++++++++++++++
 .../fsck-tests/026-bad-dir-item-name/test.sh  | 13 ------
 2 files changed, 41 insertions(+), 13 deletions(-)
 create mode 100644 tests/fsck-tests/026-bad-dir-item-name/description.txt
 delete mode 100755 tests/fsck-tests/026-bad-dir-item-name/test.sh

diff --git a/tests/fsck-tests/026-bad-dir-item-name/description.txt b/tests/fsck-tests/026-bad-dir-item-name/description.txt
new file mode 100644
index 000000000000..2bdb0f8179b0
--- /dev/null
+++ b/tests/fsck-tests/026-bad-dir-item-name/description.txt
@@ -0,0 +1,41 @@
+"default_case.img.xz" contains the fs with the following tree dump of fs tree:
+
+  [snip]
+	item 2 key (256 DIR_ITEM 751495445) itemoff 16019 itemsize 92
+		location key (259 INODE_ITEM 0) type FILE
+		transid 9 data_len 0 name_len 13
+		name: foor.WvG1c1Td
+		      ^^^^^^^^^^^^^ Hash doesn't match with key
+		location key (260 INODE_ITEM 0) type FILE
+		transid 12 data_len 0 name_len 19
+		name: user.J3__T_Km3dVsW_
+	item 3 key (256 DIR_INDEX 4) itemoff 15976 itemsize 43
+		location key (259 INODE_ITEM 0) type FILE
+		transid 9 data_len 0 name_len 13
+		name: foor.WvG1c1Td
+	item 4 key (256 DIR_INDEX 5) itemoff 15927 itemsize 49
+		location key (260 INODE_ITEM 0) type FILE
+		transid 12 data_len 0 name_len 19
+		name: user.J3__T_Km3dVsW_
+	item 5 key (259 INODE_ITEM 0) itemoff 15767 itemsize 160
+		generation 9 transid 9 size 0 nbytes 0
+		block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
+		sequence 1 flags 0x0(none)
+		atime 1499844359.341125147 (2017-07-12 15:25:59)
+		ctime 1499844359.341125147 (2017-07-12 15:25:59)
+		mtime 1499844359.341125147 (2017-07-12 15:25:59)
+		otime 1499844359.341125147 (2017-07-12 15:25:59)
+	item 6 key (259 INODE_REF 256) itemoff 15744 itemsize 23
+		index 4 namelen 13 name: foor.WvG1c1Td
+	item 7 key (260 INODE_ITEM 0) itemoff 15584 itemsize 160
+		generation 12 transid 12 size 0 nbytes 0
+		block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
+		sequence 1 flags 0x0(none)
+		atime 1499844544.931130070 (2017-07-12 15:29:04)
+		ctime 1499844544.931130070 (2017-07-12 15:29:04)
+		mtime 1499844544.931130070 (2017-07-12 15:29:04)
+		otime 1499844544.931130070 (2017-07-12 15:29:04)
+	item 8 key (260 INODE_REF 256) itemoff 15555 itemsize 29
+		index 5 namelen 19 name: user.J3__T_Km3dVsW_
+
+Test case is going to check if btrfs check can detect and repair it.
diff --git a/tests/fsck-tests/026-bad-dir-item-name/test.sh b/tests/fsck-tests/026-bad-dir-item-name/test.sh
deleted file mode 100755
index a38bf045ae82..000000000000
--- a/tests/fsck-tests/026-bad-dir-item-name/test.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-#
-# confirm whether check detects name and hash mismatch in dir_item
-
-source "$TEST_TOP/common"
-
-check_prereq btrfs
-
-image=$(extract_image "./default_case.img.xz")
-
-run_mustfail "dir_item hash mismatch not found" "$TOP/btrfs" check "$image"
-
-rm -f "$image"
-- 
2.19.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash
  2018-10-25  7:44 [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash Qu Wenruo
                   ` (3 preceding siblings ...)
  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 ` Qu Wenruo
  2019-01-07  6:53   ` Qu Wenruo
  4 siblings, 1 reply; 7+ messages in thread
From: Qu Wenruo @ 2019-01-07  6:51 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs, David Sterba


[-- Attachment #1.1: Type: text/plain, Size: 2070 bytes --]

Hi David,

Would you please consider merging this patchset?

There are at least 2 user reporting latest kernel tree checker refuse to
mount their fs, and this patch is needed to allow btrfs-progs to fix the fs.

Thanks,
Qu

On 2018/10/25 下午3:44, Qu Wenruo wrote:
> This patchset can be fetched from github:
> https://github.com/adam900710/btrfs-progs/tree/repair_bad_dir_item_hash
> 
> This is still based on the latest stable v4.17.1 branch (which is already a
> little old now)
> 
> We have report from suse bugzilla where user report latest kernel refuse
> to mount the fs.
> 
> The problem turns out to be that some old kernel (around Jan 2017) has a
> bug that could leads to corrupted dir item with mismatch hash.
> 
> This patchset will allow btrfs check --repair to repair such problem.
> 
> Qu Wenruo (4):
>   btrfs-progs: lowmem check: Add ability to repair dir item with
>     mismatch hash
>   btrfs-progs: original check: Use mismatch_dir_hash_record to record
>     bad dir items
>   btrfs-progs: original check: Add ability to repair dir item with
>     invalid hash
>   btrfs-progs: fsck-tests: Make 026-bad-dir-item-name test case to
>     verify if btrfs-check can also repair it
> 
>  check/main.c                                  | 121 +++++++++++++++++-
>  check/mode-common.c                           |  51 ++++++++
>  check/mode-common.h                           |   5 +-
>  check/mode-lowmem.c                           |  46 ++++++-
>  check/mode-lowmem.h                           |   1 +
>  check/mode-original.h                         |  14 ++
>  ctree.h                                       |   3 +
>  dir-item.c                                    |   6 +-
>  .../026-bad-dir-item-name/description.txt     |  41 ++++++
>  .../fsck-tests/026-bad-dir-item-name/test.sh  |  13 --
>  10 files changed, 272 insertions(+), 29 deletions(-)
>  create mode 100644 tests/fsck-tests/026-bad-dir-item-name/description.txt
>  delete mode 100755 tests/fsck-tests/026-bad-dir-item-name/test.sh
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/4] btrfs-progs: check: Add repair support for mismatch dir item hash
  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
  0 siblings, 0 replies; 7+ messages in thread
From: Qu Wenruo @ 2019-01-07  6:53 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs, David Sterba


[-- Attachment #1.1: Type: text/plain, Size: 2228 bytes --]



On 2019/1/7 下午2:51, Qu Wenruo wrote:
> Hi David,
> 
> Would you please consider merging this patchset?

My fault, already in devel branch.

Thanks,
Qu

> 
> There are at least 2 user reporting latest kernel tree checker refuse to
> mount their fs, and this patch is needed to allow btrfs-progs to fix the fs.
> 
> Thanks,
> Qu
> 
> On 2018/10/25 下午3:44, Qu Wenruo wrote:
>> This patchset can be fetched from github:
>> https://github.com/adam900710/btrfs-progs/tree/repair_bad_dir_item_hash
>>
>> This is still based on the latest stable v4.17.1 branch (which is already a
>> little old now)
>>
>> We have report from suse bugzilla where user report latest kernel refuse
>> to mount the fs.
>>
>> The problem turns out to be that some old kernel (around Jan 2017) has a
>> bug that could leads to corrupted dir item with mismatch hash.
>>
>> This patchset will allow btrfs check --repair to repair such problem.
>>
>> Qu Wenruo (4):
>>   btrfs-progs: lowmem check: Add ability to repair dir item with
>>     mismatch hash
>>   btrfs-progs: original check: Use mismatch_dir_hash_record to record
>>     bad dir items
>>   btrfs-progs: original check: Add ability to repair dir item with
>>     invalid hash
>>   btrfs-progs: fsck-tests: Make 026-bad-dir-item-name test case to
>>     verify if btrfs-check can also repair it
>>
>>  check/main.c                                  | 121 +++++++++++++++++-
>>  check/mode-common.c                           |  51 ++++++++
>>  check/mode-common.h                           |   5 +-
>>  check/mode-lowmem.c                           |  46 ++++++-
>>  check/mode-lowmem.h                           |   1 +
>>  check/mode-original.h                         |  14 ++
>>  ctree.h                                       |   3 +
>>  dir-item.c                                    |   6 +-
>>  .../026-bad-dir-item-name/description.txt     |  41 ++++++
>>  .../fsck-tests/026-bad-dir-item-name/test.sh  |  13 --
>>  10 files changed, 272 insertions(+), 29 deletions(-)
>>  create mode 100644 tests/fsck-tests/026-bad-dir-item-name/description.txt
>>  delete mode 100755 tests/fsck-tests/026-bad-dir-item-name/test.sh
>>
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-01-07  6:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH 1/4] btrfs-progs: lowmem check: Add ability to repair dir item with mismatch hash Qu Wenruo
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).