mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Joseph Qi <joseph.qi@linux.alibaba.com>
To: Andrew Morton <akpm@linux-foundation.org>,
	Heming Zhao <heming.zhao@suse.com>
Cc: Mark Fasheh <mark@fasheh.com>, Joel Becker <jlbec@evilplan.org>,
	ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org,
	Joseph Qi <joseph.qi@linux.alibaba.com>
Subject: [PATCH v2 22/31] ocfs2: report unrepairable corruption from online filecheck fix
Date: Fri,  6 Nov 2026 19:14:49 +0800	[thread overview]
Message-ID: <20261106111458.1467464-23-joseph.qi@linux.alibaba.com> (raw)
In-Reply-To: <20261106111458.1467464-1-joseph.qi@linux.alibaba.com>

ocfs2_filecheck_repair_inode_block() proceeds when validation fails but
then returns 0 unconditionally, after repairing only i_blkno,
fs_generation and l_next_free_rec.  An inode failing any other check is
reported to userspace as SUCCESS while still corrupt.  Revalidate after
the repairs and return the residual code.
Documentation/filesystems/ocfs2-online-filecheck.rst already describes
this: "the inode must first be checked for errors before performing a
final fix".

Returning an error from the validate callback is not free, though.
ocfs2_read_blocks() clears BH_Uptodate on failure, and
ocfs2_read_locked_inode() bails out before upgrading the cluster lock to
exclusive and calling ocfs2_write_block().  Simply revalidating at the
end would therefore leave a dirty non-uptodate buffer in the block device
cache: the next OCFS2_BH_IGNORE_CACHE read of that block sees
buffer_dirty(), skips submitting the read, and then fails -EIO on
!buffer_uptodate(), so later filecheck requests report FAILED instead of
the real reason.  The half repaired dinode, stamped with a freshly
computed ecc, would also be flushed by generic writeback without the
exclusive cluster lock.

Snapshot the block instead and only mark it dirty once the repaired
dinode validates.  On residual failure restore the snapshot and return
the error with the buffer clean, so the next read goes to disk as usual.
The snapshot covers the whole block rather than just the three repaired
fields, because ocfs2_validate_meta_ecc() mutates it in place while
attempting a hamming fix.

The ecc is recomputed before revalidating, otherwise a bad ecc would
always fail the revalidation and roll back the ecc repair itself.  Whether
it needs recomputing is taken from the initial validation's error code
rather than a second ocfs2_validate_meta_ecc() call, since any field
repair invalidates the ecc anyway.

Also guard the l_next_free_rec repair with a valid l_count.  Copying a
zero or oversized l_count into l_next_free_rec spread the corruption to a
second field and stamped it with a good ecc.

Inodes failing an unrepairable check now report INVALIDINO instead of
SUCCESS.  No new error code is introduced, so ocfs2_filecheck_errs[] and
its userspace-visible names are unchanged.  A failed repair no longer
leaves any modification behind, and revalidation runs the ecc check a
second time, which increments osb_ecc_stats again and emits a second
ocfs2_filecheck_validate_inode_block trace event per fix request.

Fixes: d56a8f32e4c6 ("ocfs2: check/fix inode block for online file check")
Cc: <stable@vger.kernel.org>
Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
---
 fs/ocfs2/inode.c | 52 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 49 insertions(+), 3 deletions(-)

diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 9db147de1982..47b539edb764 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -2062,8 +2062,11 @@ static int ocfs2_filecheck_repair_inode_block(struct super_block *sb,
 {
 	int changed = 0;
 	struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data;
+	void *orig;
+	int rc;
 
-	if (!ocfs2_filecheck_validate_inode_block(sb, bh))
+	rc = ocfs2_filecheck_validate_inode_block(sb, bh);
+	if (!rc)
 		return 0;
 
 	trace_ocfs2_filecheck_repair_inode_block(
@@ -2097,6 +2100,16 @@ static int ocfs2_filecheck_repair_inode_block(struct super_block *sb,
 		return -OCFS2_FILECHECK_ERR_VALIDFLAG;
 	}
 
+	/*
+	 * Snapshot the whole block, not just the fields repaired below:
+	 * ocfs2_validate_meta_ecc() also mutates it in place while trying a
+	 * hamming fix, so a field by field undo would not restore it.
+	 */
+	orig = kmalloc(sb->s_blocksize, GFP_NOFS);
+	if (!orig)
+		return -OCFS2_FILECHECK_ERR_FAILED;
+	memcpy(orig, bh->b_data, sb->s_blocksize);
+
 	if (ocfs2_dinode_has_wrong_blkno(di, bh->b_blocknr)) {
 		di->i_blkno = cpu_to_le64(bh->b_blocknr);
 		changed = 1;
@@ -2115,7 +2128,14 @@ static int ocfs2_filecheck_repair_inode_block(struct super_block *sb,
 		     le32_to_cpu(di->i_fs_generation));
 	}
 
+	/*
+	 * Only copy l_count into l_next_free_rec when l_count itself is
+	 * sane, otherwise the repair would spread the corruption to a
+	 * second field and stamp it with a freshly computed ecc.
+	 */
 	if (ocfs2_dinode_has_extents(di) &&
+	    !ocfs2_dinode_has_zero_extent_count(di) &&
+	    !ocfs2_dinode_has_oversized_extent_count(sb, di) &&
 	    ocfs2_dinode_has_bad_extent_next_free(di)) {
 		di->id2.i_list.l_next_free_rec = di->id2.i_list.l_count;
 		changed = 1;
@@ -2125,14 +2145,40 @@ static int ocfs2_filecheck_repair_inode_block(struct super_block *sb,
 		     le16_to_cpu(di->id2.i_list.l_next_free_rec));
 	}
 
-	if (changed || ocfs2_validate_meta_ecc(sb, bh->b_data, &di->i_check)) {
+	/*
+	 * Recompute the ecc before revalidating, but do not mark the buffer
+	 * dirty yet.  The initial validation already told us whether the ecc
+	 * was bad, and any field repair above invalidates it anyway.
+	 */
+	if (changed || rc == -OCFS2_FILECHECK_ERR_BLOCKECC) {
 		ocfs2_compute_meta_ecc(sb, bh->b_data, &di->i_check);
-		mark_buffer_dirty(bh);
 		mlog(ML_ERROR,
 		     "Filecheck: reset dinode #%llu: compute meta ecc\n",
 		     (unsigned long long)bh->b_blocknr);
 	}
 
+	/*
+	 * Only i_blkno, fs_generation and l_next_free_rec are repairable, so
+	 * report whatever is still wrong instead of claiming success.
+	 *
+	 * Validate before dirtying.  ocfs2_read_blocks() clears BH_Uptodate
+	 * when the validate callback fails, and a dirty non-uptodate buffer
+	 * left in the block device cache makes every later
+	 * OCFS2_BH_IGNORE_CACHE read of this block skip the disk read and
+	 * fail with -EIO.  ocfs2_read_locked_inode() also bails out before
+	 * upgrading to an exclusive cluster lock and writing the block, so
+	 * the repair would be flushed later without that lock.  Roll back
+	 * instead, leaving the buffer clean.
+	 */
+	rc = ocfs2_filecheck_validate_inode_block(sb, bh);
+	if (rc) {
+		memcpy(bh->b_data, orig, sb->s_blocksize);
+		kfree(orig);
+		return rc;
+	}
+
+	mark_buffer_dirty(bh);
+	kfree(orig);
 	return 0;
 }
 
-- 
2.39.3


  parent reply	other threads:[~2026-11-06 11:15 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06 11:14 [PATCH v2 00/31] ocfs2: split dinode validation into shared predicates Joseph Qi
2026-11-06 11:14 ` [PATCH v2 01/31] ocfs2: exempt orphaned dinodes from the zero nlink check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 02/31] ocfs2: abstract the zero nlink or mode dinode check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 03/31] ocfs2: abstract the dinode i_blkno mismatch check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 04/31] ocfs2: abstract the dinode OCFS2_VALID_FL check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 05/31] ocfs2: abstract the dinode fs_generation mismatch check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 06/31] ocfs2: abstract the dinode suballoc slot checks Joseph Qi
2026-11-06 11:14 ` [PATCH v2 07/31] ocfs2: abstract the dinode suballoc bit check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 08/31] ocfs2: abstract the dinode orphaned slot check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 09/31] ocfs2: abstract the dinode DIO " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 10/31] ocfs2: abstract the inline data dinode guard and cluster check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 11/31] ocfs2: abstract the inline data id_count bound check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 12/31] ocfs2: abstract the inline data i_size " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 13/31] ocfs2: abstract the fast symlink dinode guard and size check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 14/31] ocfs2: abstract the fast symlink NUL-termination check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 15/31] ocfs2: abstract the chain list dinode guard and count check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 16/31] ocfs2: abstract the chain list next free record check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 17/31] ocfs2: abstract the chain list bits per cluster check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 18/31] ocfs2: abstract the dinode refcount location check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 19/31] ocfs2: abstract the zero extent list count check Joseph Qi
2026-11-06 11:14 ` [PATCH v2 20/31] ocfs2: abstract the oversized " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 21/31] ocfs2: abstract the extent list next free record check Joseph Qi
2026-11-06 11:14 ` Joseph Qi [this message]
2026-11-06 11:14 ` [PATCH v2 23/31] ocfs2: check dinode nlink and mode in online filecheck Joseph Qi
2026-11-06 11:14 ` [PATCH v2 24/31] ocfs2: check dinode suballoc slot " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 25/31] ocfs2: check dinode suballoc bit " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 26/31] ocfs2: check dinode orphaned slots " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 27/31] ocfs2: check inline data dinodes " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 28/31] ocfs2: check fast symlink " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 29/31] ocfs2: check chain list " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 30/31] ocfs2: check dinode refcount location " Joseph Qi
2026-11-06 11:14 ` [PATCH v2 31/31] ocfs2: check extent list dinodes " Joseph Qi

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=20261106111458.1467464-23-joseph.qi@linux.alibaba.com \
    --to=joseph.qi@linux.alibaba.com \
    --cc=akpm@linux-foundation.org \
    --cc=heming.zhao@suse.com \
    --cc=jlbec@evilplan.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark@fasheh.com \
    --cc=ocfs2-devel@lists.linux.dev \
    /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

all inboxes | Powered by JetHome®