From: Aditya Prakash Srivastava The "data_err=abort" mount option in ext4 is designed to abort the journal and force the filesystem into read-only mode if a file data writeback failure is detected (to prevent silent data loss and stale data exposure). However, in standard data=ordered mode, file data writeback is executed and waited for during transaction commit in journal_finish_inode_data_buffers(). When filemap_fdatawait_range_keep_errors() detects and returns a data writeback error (e.g. -EIO), JBD2 merely prints a warning message and then discards the error. This results in the transaction committing successfully, exposing stale/corrupted data and defeating the purpose of the data_err=abort option. Fix this by: 1. Defining a new JBD2 configuration flag, JBD2_ABORT_ON_DATA_ERR. 2. In JBD2, if JBD2_ABORT_ON_DATA_ERR is set, abort the transaction commit and the journal thread via jbd2_journal_abort() if writeback fails. 3. In ext4, configure JBD2_ABORT_ON_DATA_ERR on the journal based on the ext4 "data_err=abort" mount option in ext4_init_journal_params(). Reported-by: Anthony Rebello Closes: https://bugzilla.kernel.org/show_bug.cgi?id=207729 Suggested-by: Jan Kara Signed-off-by: Aditya Prakash Srivastava --- fs/ext4/super.c | 4 ++++ fs/jbd2/commit.c | 2 ++ include/linux/jbd2.h | 1 + 3 files changed, 7 insertions(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 7283108d7609..de34490a5b68 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -5875,6 +5875,10 @@ static void ext4_init_journal_params(struct super_block *sb, journal_t *journal) journal->j_flags |= JBD2_BARRIER; else journal->j_flags &= ~JBD2_BARRIER; + if (test_opt(sb, DATA_ERR_ABORT)) + journal->j_flags |= JBD2_ABORT_ON_DATA_ERR; + else + journal->j_flags &= ~JBD2_ABORT_ON_DATA_ERR; /* * Always enable journal cycle record option, letting the journal * records log transactions continuously between each mount. diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index d8577725a2fb..49acc9d0809e 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -768,6 +768,8 @@ void jbd2_journal_commit_transaction(journal_t *journal) printk(KERN_WARNING "JBD2: Detected IO errors %d while flushing file data on %s\n", err, journal->j_devname); + if (journal->j_flags & JBD2_ABORT_ON_DATA_ERR) + jbd2_journal_abort(journal, err); err = 0; } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 7e785aa6d35d..e39679656de6 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1410,6 +1410,7 @@ JBD2_FEATURE_INCOMPAT_FUNCS(fast_commit, FAST_COMMIT) #define JBD2_FLUSHED 0x008 /* The journal superblock has been flushed */ #define JBD2_LOADED 0x010 /* The journal superblock has been loaded */ #define JBD2_BARRIER 0x020 /* Use IDE barriers */ +#define JBD2_ABORT_ON_DATA_ERR 0x040 /* Abort the journal on file data write errors */ #define JBD2_CYCLE_RECORD 0x080 /* Journal cycled record log on * clean and empty filesystem * logging area */ -- 2.47.3