[PATCH] ext4: propagate errors from fast commit block cleanup

From: lty

Date: Mon Sep 14 2026 - 09:00:58 EST


ext4_ext_clear_bb() stops scanning when ext4_map_blocks() fails, but it
returns success to its caller. It also ignores failures from extent lookup
and replay-region allocation. ext4_fc_replay_inode() ignores the helper
result and continues replay with stale block bitmap accounting.

Propagate all cleanup errors from ext4_ext_clear_bb() and abort inode
replay when cleanup fails.

Fixes: 8016e29f4362 ("ext4: fast commit recovery path")
Cc: stable@xxxxxxxxxxxxxxx

Testing: QEMU fast-commit replay with an injected -EIO at the
ext4_ext_clear_bb() mapping call. The unpatched kernel completed recovery
and e2fsck reported a free-block count mismatch; the patched kernel aborted
recovery with EIO. Also built the ext4 target objects with the expanded
error paths.

Signed-off-by: lty <781735889@xxxxxx>
---
fs/ext4/extents.c | 31 ++++++++++++++++++-------------
fs/ext4/fast_commit.c | 5 ++++-
2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 76038b6c3655..f65d254c0d5e 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -6363,29 +6363,34 @@ int ext4_ext_clear_bb(struct inode *inode)
map.m_len = end - cur;
ret = ext4_map_blocks(NULL, inode, &map, 0);
if (ret < 0)
- break;
+ goto out;
if (ret > 0) {
path = ext4_find_extent(inode, map.m_lblk, path, 0);
- if (!IS_ERR(path)) {
- for (j = 0; j < path->p_depth; j++) {
- ext4_mb_mark_bb(inode->i_sb,
- path[j].p_block, 1, false);
- ext4_fc_record_regions(inode->i_sb, inode->i_ino,
- 0, path[j].p_block, 1, 1);
- }
- } else {
- path = NULL;
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
+ goto out;
+ }
+ for (j = 0; j < path->p_depth; j++) {
+ ext4_mb_mark_bb(inode->i_sb,
+ path[j].p_block, 1, false);
+ ret = ext4_fc_record_regions(inode->i_sb,
+ inode->i_ino, 0,
+ path[j].p_block, 1, 1);
+ if (ret)
+ goto out;
}
ext4_mb_mark_bb(inode->i_sb, map.m_pblk, map.m_len, false);
- ext4_fc_record_regions(inode->i_sb, inode->i_ino,
- map.m_lblk, map.m_pblk, map.m_len, 1);
+ ret = ext4_fc_record_regions(inode->i_sb, inode->i_ino,
+ map.m_lblk, map.m_pblk, map.m_len, 1);
+ if (ret)
+ goto out;
}
cur = cur + map.m_len;
}

out:
ext4_free_ext_path(path);
- return 0;
+ return ret < 0 ? ret : 0;
}

#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
index 0cac890cf370..ab7263b4f9e4 100644
--- a/fs/ext4/fast_commit.c
+++ b/fs/ext4/fast_commit.c
@@ -1941,8 +1941,11 @@ static int ext4_fc_replay_inode(struct super_block *sb,

inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
if (!IS_ERR(inode)) {
- ext4_ext_clear_bb(inode);
+ ret = ext4_ext_clear_bb(inode);
iput(inode);
+ inode = NULL;
+ if (ret)
+ goto out;
}
inode = NULL;