* [PATCH 1/3] btrfs-progs: check/original: add dedicated missing INODE_REF repair
2025-12-17 8:08 [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases Qu Wenruo
@ 2025-12-17 8:08 ` Qu Wenruo
2025-12-17 8:08 ` [PATCH 2/3] btrfs-progs: check/lowmem: fix " Qu Wenruo
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Qu Wenruo @ 2025-12-17 8:08 UTC (permalink / raw)
To: linux-btrfs
Currently if the original mode of btrfs-check hits a missing INODE_REF,
but with valid DIR_INDEX/DIR_ITEM, then it will repair it by deleting
the valid DIR_INDEX, which will just make things worse.
Add a dedicated repair for missing INODE_REF which will just add back
the missing INODE_REF, properly fixing the problem other than making it
worse.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
check/main.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/check/main.c b/check/main.c
index 96c7ef7d8261..880827e11ee7 100644
--- a/check/main.c
+++ b/check/main.c
@@ -2351,6 +2351,42 @@ static int create_inode_item(struct btrfs_root *root,
return 0;
}
+static int create_inode_ref(struct btrfs_root *root,
+ struct inode_record *i_rec,
+ struct inode_backref *backref)
+{
+ struct btrfs_trans_handle *trans;
+ int ret;
+
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ errno = -ret;
+ error_msg(ERROR_MSG_START_TRANS, "%m");
+ return ret;
+ }
+
+ ret = btrfs_insert_inode_ref(trans, root, backref->name, backref->namelen,
+ i_rec->ino, backref->dir, backref->index);
+ if (ret < 0) {
+ btrfs_commit_transaction(trans, root);
+ return ret;
+ }
+ ret = btrfs_commit_transaction(trans, root);
+ if (ret < 0) {
+ ret = PTR_ERR(trans);
+ errno = -ret;
+ error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
+ return ret;
+ }
+ backref->found_inode_ref = 1;
+ backref->errors &= ~REF_ERR_NO_INODE_REF;
+ printf("Added INODE_REF for root %lld ino %llu parent %llu index %llu name %.*s\n",
+ btrfs_root_id(root), i_rec->ino, backref->dir, backref->index,
+ backref->namelen, backref->name);
+ return 0;
+}
+
static int repair_inode_backrefs(struct btrfs_root *root,
struct inode_record *rec,
struct cache_tree *inode_cache,
@@ -2375,6 +2411,21 @@ static int repair_inode_backrefs(struct btrfs_root *root,
if (rec->ino == root_dirid && backref->index == 0)
continue;
+ /*
+ * Have DIR_INDEX, DIR_ITEM and INODE_ITEM, and even nlinks
+ * matches with only missing INODE_REF.
+ */
+ if (!backref->found_inode_ref && backref->found_dir_item &&
+ backref->found_dir_index && rec->found_inode_item &&
+ rec->found_link == rec->nlink) {
+ ret = create_inode_ref(root, rec, backref);
+ if (ret)
+ break;
+ repaired++;
+ list_del(&backref->list);
+ free(backref);
+ continue;
+ }
if (delete &&
((backref->found_dir_index && !backref->found_inode_ref) ||
(backref->found_dir_index && backref->found_inode_ref &&
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/3] btrfs-progs: check/lowmem: fix INODE_REF repair
2025-12-17 8:08 [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases Qu Wenruo
2025-12-17 8:08 ` [PATCH 1/3] btrfs-progs: check/original: add dedicated missing INODE_REF repair Qu Wenruo
@ 2025-12-17 8:08 ` Qu Wenruo
2025-12-17 8:08 ` [PATCH 3/3] btrfs-progs: fsck-tests: add a test case for repairing missing INODE_REF Qu Wenruo
2026-01-24 1:00 ` [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases David Sterba
3 siblings, 0 replies; 5+ messages in thread
From: Qu Wenruo @ 2025-12-17 8:08 UTC (permalink / raw)
To: linux-btrfs
[BUG]
Although lowmem has the logical to repair one missing
INODE_REF/DIR_INDEX/DIR_ITEM, there is a catch in missing INODE_REF.
If we're checking an DIR_ITEM (which we normally hits first), and failed
to find the INODE_REF, we are in a situation where there is no @index to
delete the original DIR_INDEX/DIR_ITEM pair.
This can cause further damage to the fs, without really improving
anything.
[CAUSE]
There is a minimal example where there is missing INODE_ITEM:
item 0 key (256 INODE_ITEM 0) itemoff 16123 itemsize 160
generation 3 transid 9 size 12 nbytes 16384
block group 0 mode 40755 links 1 uid 0 gid 0 rdev 0
sequence 1 flags 0x0(none)
item 1 key (256 INODE_REF 256) itemoff 16111 itemsize 12
index 0 namelen 2 name: ..
item 2 key (256 DIR_ITEM 496027801) itemoff 16075 itemsize 36
location key (257 INODE_ITEM 0) type FILE
transid 9 data_len 0 name_len 6
name: foobar
item 3 key (256 DIR_INDEX 2) itemoff 16039 itemsize 36
location key (257 INODE_ITEM 0) type FILE
transid 9 data_len 0 name_len 6
name: foobar
item 4 key (257 INODE_ITEM 0) itemoff 15879 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)
For above case, if we're check the DIR_ITEM, we can find the INODE_ITEM
but not the INODE_REF.
So we need to repair it, but at this stage we haven't checked DIR_INDEX,
this means we have no idea which index we should delete, leaving the
default @index as (-1).
If we call btrfs_unlink() with that (-1) as index, it will not delete
the (256 DIR_INDEX 2) one, then re-add a link using -1 as index, causing
more damage.
[FIX]
Before calling btrfs_unlink(), do an extra check on if we're called from
DIR_ITEM checks (aka, @index is -1) and we only detected a missing
INODE_REF yet.
If so, do find_dir_index() call to determine the DIR_INDEX, if that
failed it means we have missing both DIR_INDEX and INODE_REF, thus
should remove the lone DIR_ITEM instead.
With this enhancement, lowmem mode can properly fix the missing
INODE_REF by adding it back.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
check/mode-lowmem.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index 8a4c5bc1e10d..dcec6cc06510 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -1015,6 +1015,17 @@ static int repair_ternary_lowmem(struct btrfs_root *root, u64 dir_ino, u64 ino,
int stage = 0;
int ret = 0;
+ /*
+ * We miss an INODE_REF, and we're checking DIR_ITEM and hasn't yet
+ * find the DIR_INDEX, thus there is no reliable index.
+ * Try to locate one, this can be slow as we need to locate the DIR_INDEX
+ * item from the directory.
+ */
+ if (index == (u64)-1 && (err & INODE_REF_MISSING)) {
+ ret = find_dir_index(root, dir_ino, ino, &index, name, name_len, filetype);
+ if (ret < 0)
+ err |= DIR_INDEX_MISSING;
+ }
/*
* stage shall be one of following valild values:
* 0: Fine, nothing to do.
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 3/3] btrfs-progs: fsck-tests: add a test case for repairing missing INODE_REF
2025-12-17 8:08 [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases Qu Wenruo
2025-12-17 8:08 ` [PATCH 1/3] btrfs-progs: check/original: add dedicated missing INODE_REF repair Qu Wenruo
2025-12-17 8:08 ` [PATCH 2/3] btrfs-progs: check/lowmem: fix " Qu Wenruo
@ 2025-12-17 8:08 ` Qu Wenruo
2026-01-24 1:00 ` [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases David Sterba
3 siblings, 0 replies; 5+ messages in thread
From: Qu Wenruo @ 2025-12-17 8:08 UTC (permalink / raw)
To: linux-btrfs
The image is created by removing the INODE_REF for inode 257.
Now both original and lowmem mode should be able to repair it.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
.../070-missing-inode-ref/.lowmem_repairable | 0
.../070-missing-inode-ref/default.img.xz | Bin 0 -> 2092 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 tests/fsck-tests/070-missing-inode-ref/.lowmem_repairable
create mode 100644 tests/fsck-tests/070-missing-inode-ref/default.img.xz
diff --git a/tests/fsck-tests/070-missing-inode-ref/.lowmem_repairable b/tests/fsck-tests/070-missing-inode-ref/.lowmem_repairable
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/tests/fsck-tests/070-missing-inode-ref/default.img.xz b/tests/fsck-tests/070-missing-inode-ref/default.img.xz
new file mode 100644
index 0000000000000000000000000000000000000000..15d95eed391c14630d93be2bf3e665186f816259
GIT binary patch
literal 2092
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
V7#QB0-LcqsB>guNNR}ls3IGh@4rTxV
literal 0
HcmV?d00001
--
2.52.0
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases
2025-12-17 8:08 [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases Qu Wenruo
` (2 preceding siblings ...)
2025-12-17 8:08 ` [PATCH 3/3] btrfs-progs: fsck-tests: add a test case for repairing missing INODE_REF Qu Wenruo
@ 2026-01-24 1:00 ` David Sterba
3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2026-01-24 1:00 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs
On Wed, Dec 17, 2025 at 06:38:12PM +1030, Qu Wenruo wrote:
> This is inspired by a recent corruption report that an INODE_REF key hits a
> bitflip and becomes UNKNOWN.8 key.
>
> That cause missing INODE_REF, and unfortunately neither original nor
> lowmem mode can properly handle it.
>
> For the original mode it just completely lacks the handling for such
> case, thus fallback to delete the good DIR_INDEX, making things
> worse.
>
> For the lowmem mode, although it is trying to keep the good
> DIR_INDEX/DIR_ITEM, it doesn't handle index number search, thus using
> the default (-1) as index, causing incorrect new link to be added.
>
> Fix both modes, and add a new test case for it.
>
> Qu Wenruo (3):
> btrfs-progs: check/original: add dedicated missing INODE_REF repair
> btrfs-progs: check/lowmem: fix INODE_REF repair
> btrfs-progs: fsck-tests: add a test case for repairing missing
> INODE_REF
Added to devel, thanks.
^ permalink raw reply [flat|nested] 5+ messages in thread