public inbox for linux-btrfs@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] btrfs-progs: check: properly fix missing INODE_REF cases
@ 2025-12-17  8:08 Qu Wenruo
  2025-12-17  8:08 ` [PATCH 1/3] btrfs-progs: check/original: add dedicated missing INODE_REF repair Qu Wenruo
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Qu Wenruo @ 2025-12-17  8:08 UTC (permalink / raw)
  To: linux-btrfs

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

 check/main.c                                  |  51 ++++++++++++++++++
 check/mode-lowmem.c                           |  11 ++++
 .../070-missing-inode-ref/.lowmem_repairable  |   0
 .../070-missing-inode-ref/default.img.xz      | Bin 0 -> 2092 bytes
 4 files changed, 62 insertions(+)
 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

--
2.52.0


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

* [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
zcmexsUKJ6=z`*kC+7>sK126d-6cic77$9IylIX<;{Qub>#xl6h>{}kWZ}Y*EQkChu
z9ovi-ygg^E)qeg`zA)RKQymX>Y|xC~d-&exw<Su6KX~p5ehvKka*nex_ia9#46T&>
z7jN!5R>zfhzc^HO*RHGl!m}sodkuR_j6`2CSQ_u&D|kG1@7@3Xa^6DUTRh8_J1=u9
zl1T4dwm**J&9w>1H5^g_GFb(hvt}pE5Bkfuc#XcoojI8^o?H-ey?yd-);*`0GQ0G`
zYHr)jl3gkko@?`{^_D2dIWI-aP4f=!T$<9U#Qk|@>UW8YLFu=a&#joC{%P~C_p6N6
z3InY(yf!;qnfr*%O%_ZqUy{)#_%h^{agS0MbCXmh>&spBe#ewPavz`W|E;czcb(lL
zM$W>hhc5zSIcIZcw!e&$`N4n0FTJ~IW)H*pDKj6+nJ>urD?2S~-CJq)wP)m0l7$}q
zy>VafVd?XFq4-;OU$uo8)gGO8IIZf0{^CWSbZ+xG?9yFrQc&ixI_kCGQ>)SoOVcx$
zJ)-)On{-3{i<lIX--nyB`LjIT{k4g6RqBgxY-VY5T-Z4Oxb(a_`mz7?ug;5K9z3^c
z3b>_}<~4u8iMm_oU)gdWxv^kc8q>L(Uy_%6_<HbK#~-itTkdIgNFLX^d&!@vPF$A#
z&5q4$|A@bDee-(t58H%8R{Ljku6@GRej>po-fqQBbM`Gf0l&U-F><G9tk|UbqT{N_
zbm`fq;!dAG%RKIw-FEH!msz1!H3HLoCI)Solo_<XSl92{E|YcIY5Ln!ZCC#m+L2Y8
znew;g$-V#VQ_cyxSvuytV{ty(Q+I3i$|(*px0rr=D}I&ed$h_&Te?H+mU;SvqjRSJ
zyya-NDT04%(cAa+liKW6XZhs6c9?qOa--mhf6J?{?LYj%MSQX1@A}J|zja#l+WozM
z{dUy!ncI`sf4uZ>hq^@B`I+8(c0UzAdc^0^hr2Gnv-VYo@h6?$?$`X$qk5-dYS`lo
zACB#RF|E)r@OzPG=YPAhLo-D_e!AYg`s6W|Ys>Y#3a;JVDN<b0!P{K1Q9p+7*J4BS
z37OnV?3{5GJ2vcpkbnDn+)R@-H3epuLd7J7r$wIHtWgo4V4-n9Ws}I|BTF2QC@u|-
zf6Swmwxrle^^oE61<Pk8DZD%zYPV)b!>S+EpO+p~`{+GOWX*oQ>l2bwPsW`!vsD*6
zAGLSWm$YfxX)_isEt@s{sY7){l)}M4<ym?Mq_r!U<sV$A44R&@VTtv{6<-z~y05+`
z{_wvkLj24pHhWF*m*`!+L~N4XLgUirpe3CBljrK2J#yY+e=@r*Mo8%4)UDj<zYYmn
z?b~V9YhP^sQDxQZ*p*tWeJ;NHw7lj%>Ty1v9mqT{v@GT3)BP{}v=jb@+w83LY!vK1
zWVr3YnlsVG%Ia@=q$1Ae`q@tnxYX?uTCTS*knJjqe-_Iv(aGNziu0X47-5oOuxb7Z
z0nHPuCVzivFv<UOSj0WgA9cMGR*Kv^@bcF#X35hh^%|b5UHbXqnG_FurT(JopZ`rx
zeOSEq>a9BhqKqQzH*LQoxN7T;+_@4@ZPwb_zl?8LI%nO78Rw@pE;JE}ywATuc9p?~
zCDTk2UMXbXl|At4TKF&VbBn5(doF+1F|s;R^e?7BP`zL5htIU_tGAn<+nZZc_I}Oo
z=l&TrUbdUdpQNtKdvnqG>6=v6^QA8PD{UMjG#iq>@&s+)d)_>{KD^mgx~9GI{L06l
zSJq7XczatO6W?X^yz2{eZ^tAQCtH12*ZOk9Qp+#X>uSGQV!^WBhMI}B-7|!Lnf#4k
z9;c%<k>gq30$w)my8CVCPi0Ccq~yp3ryai`RBp&2Ho0ut!ywa?lJG}vi?6Pk^iOev
z`MV^uSd+$+E3;)Mxa~T4SdvMqHe%)vJ}zNz|J?TqTOOYkk8om5DKZGZ5xv6qZckUZ
z&5zaEZ!To6KH3-3T<~K-V-x=`FRz;a8z&2X|Det#Q&;p&JnqQ%_Gq7Sofp@SJ?yz%
z6Cd9AP}6Gf?U(yMwfsI{RchvWvitQ%iJ;FPxwUvDczxe2+pKaSW2v~f^NR{r0rPF!
zi+nBIZ#tWp|5ML@oOI;8PE}vr^GC1s1pV7PX8f6CVx1IoFr-(^DR`T|_0Kt?GqQJc
zZ;f0(U%_PV0{;yu3GKpD_ZE73x12Z`FEPhv=Ts|)om`bGbB`atP{fj*{(j=NH~lTM
zwq38^aBjcM#;b{|A`4R|Om?Wf|I2#k$(1iSe?KvDI9XUXFLc(~IZrAVo_bgLf<HI#
z-R{||Zy4xqyr6z2>C}uq8<XkY@fBihjC|AQE?0TcdN@HZdd2dhCaDk0n`-j4V!7ur
z)&%T%B(Zl==p+6^_ZaqG+}M>XvNQb1;#I%;?4RT(h$$3ak|+?cjho}gc6X=shD(RF
zr^?-DUDD2SL2!@%li72A{Cq0%|IYG<dO^>VH2GINdUfO1-(Y^j><Dk8$4RB8k&N-F
zseVSUqJ8!Dsm$9vx7F}llLaIH-eX&qzP}*W8vScm!nyF3YMFOe8!bp!xb0B5&XF!2
z-^mH<t{h79n$9&>@7*f3&@)l@l%9SRo$*5KiivxcjQ*bWoBt$U%#Yrz5iIkvPJX4P
zYVX9AzOri+<Buhk>X#dIPd9qwd3gGi-Om%Q);~;bsNWLDIlGa=)>XduwyDR$aG4(>
zYI_d%ij<rGyk91NH}cn7&cIJ|Ze?9w%CI;8zW!dN;%#e~w4--@IXF9A`BKnfi-h-=
z1!AwppEu{9wOm2Na`&@sHkTZBEL>2MpE>`QM6aIwj(=U?3P1D06hn_kYaAFDTLl^v
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

end of thread, other threads:[~2026-01-24  1:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox