Commit fc750a3b authored by changfengnan's avatar changfengnan Committed by Theodore Ts'o

jbd2: avoid transaction reuse after reformatting

When ext4 is formatted with lazy_journal_init=1 and transactions from
the previous filesystem are still on disk, it is possible that they are
considered during a recovery after a crash. Because the checksum seed
has changed, the CRC check will fail, and the journal recovery fails
with checksum error although the journal is otherwise perfectly valid.
Fix the problem by checking commit block time stamps to determine
whether the data in the journal block is just stale or whether it is
indeed corrupt.
Reported-by: default avatarkernel test robot <lkp@intel.com>
Reviewed-by: default avatarAndreas Dilger <adilger@dilger.ca>
Signed-off-by: default avatarFengnan Chang <changfengnan@hikvision.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20201012164900.20197-1-jack@suse.czSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent d3e7d20b
...@@ -428,6 +428,8 @@ static int do_one_pass(journal_t *journal, ...@@ -428,6 +428,8 @@ static int do_one_pass(journal_t *journal,
__u32 crc32_sum = ~0; /* Transactional Checksums */ __u32 crc32_sum = ~0; /* Transactional Checksums */
int descr_csum_size = 0; int descr_csum_size = 0;
int block_error = 0; int block_error = 0;
bool need_check_commit_time = false;
__u64 last_trans_commit_time = 0, commit_time;
/* /*
* First thing is to establish what we expect to find in the log * First thing is to establish what we expect to find in the log
...@@ -520,12 +522,21 @@ static int do_one_pass(journal_t *journal, ...@@ -520,12 +522,21 @@ static int do_one_pass(journal_t *journal,
if (descr_csum_size > 0 && if (descr_csum_size > 0 &&
!jbd2_descriptor_block_csum_verify(journal, !jbd2_descriptor_block_csum_verify(journal,
bh->b_data)) { bh->b_data)) {
printk(KERN_ERR "JBD2: Invalid checksum " /*
"recovering block %lu in log\n", * PASS_SCAN can see stale blocks due to lazy
next_log_block); * journal init. Don't error out on those yet.
err = -EFSBADCRC; */
brelse(bh); if (pass != PASS_SCAN) {
goto failed; pr_err("JBD2: Invalid checksum recovering block %lu in log\n",
next_log_block);
err = -EFSBADCRC;
brelse(bh);
goto failed;
}
need_check_commit_time = true;
jbd_debug(1,
"invalid descriptor block found in %lu\n",
next_log_block);
} }
/* If it is a valid descriptor block, replay it /* If it is a valid descriptor block, replay it
...@@ -535,6 +546,7 @@ static int do_one_pass(journal_t *journal, ...@@ -535,6 +546,7 @@ static int do_one_pass(journal_t *journal,
if (pass != PASS_REPLAY) { if (pass != PASS_REPLAY) {
if (pass == PASS_SCAN && if (pass == PASS_SCAN &&
jbd2_has_feature_checksum(journal) && jbd2_has_feature_checksum(journal) &&
!need_check_commit_time &&
!info->end_transaction) { !info->end_transaction) {
if (calc_chksums(journal, bh, if (calc_chksums(journal, bh,
&next_log_block, &next_log_block,
...@@ -683,11 +695,41 @@ static int do_one_pass(journal_t *journal, ...@@ -683,11 +695,41 @@ static int do_one_pass(journal_t *journal,
* mentioned conditions. Hence assume * mentioned conditions. Hence assume
* "Interrupted Commit".) * "Interrupted Commit".)
*/ */
commit_time = be64_to_cpu(
((struct commit_header *)bh->b_data)->h_commit_sec);
/*
* If need_check_commit_time is set, it means we are in
* PASS_SCAN and csum verify failed before. If
* commit_time is increasing, it's the same journal,
* otherwise it is stale journal block, just end this
* recovery.
*/
if (need_check_commit_time) {
if (commit_time >= last_trans_commit_time) {
pr_err("JBD2: Invalid checksum found in transaction %u\n",
next_commit_ID);
err = -EFSBADCRC;
brelse(bh);
goto failed;
}
ignore_crc_mismatch:
/*
* It likely does not belong to same journal,
* just end this recovery with success.
*/
jbd_debug(1, "JBD2: Invalid checksum ignored in transaction %u, likely stale data\n",
next_commit_ID);
err = 0;
brelse(bh);
goto done;
}
/* Found an expected commit block: if checksums /*
* are present verify them in PASS_SCAN; else not * Found an expected commit block: if checksums
* are present, verify them in PASS_SCAN; else not
* much to do other than move on to the next sequence * much to do other than move on to the next sequence
* number. */ * number.
*/
if (pass == PASS_SCAN && if (pass == PASS_SCAN &&
jbd2_has_feature_checksum(journal)) { jbd2_has_feature_checksum(journal)) {
struct commit_header *cbh = struct commit_header *cbh =
...@@ -719,6 +761,8 @@ static int do_one_pass(journal_t *journal, ...@@ -719,6 +761,8 @@ static int do_one_pass(journal_t *journal,
!jbd2_commit_block_csum_verify(journal, !jbd2_commit_block_csum_verify(journal,
bh->b_data)) { bh->b_data)) {
chksum_error: chksum_error:
if (commit_time < last_trans_commit_time)
goto ignore_crc_mismatch;
info->end_transaction = next_commit_ID; info->end_transaction = next_commit_ID;
if (!jbd2_has_feature_async_commit(journal)) { if (!jbd2_has_feature_async_commit(journal)) {
...@@ -728,11 +772,24 @@ static int do_one_pass(journal_t *journal, ...@@ -728,11 +772,24 @@ static int do_one_pass(journal_t *journal,
break; break;
} }
} }
if (pass == PASS_SCAN)
last_trans_commit_time = commit_time;
brelse(bh); brelse(bh);
next_commit_ID++; next_commit_ID++;
continue; continue;
case JBD2_REVOKE_BLOCK: case JBD2_REVOKE_BLOCK:
/*
* Check revoke block crc in pass_scan, if csum verify
* failed, check commit block time later.
*/
if (pass == PASS_SCAN &&
!jbd2_descriptor_block_csum_verify(journal,
bh->b_data)) {
jbd_debug(1, "JBD2: invalid revoke block found in %lu\n",
next_log_block);
need_check_commit_time = true;
}
/* If we aren't in the REVOKE pass, then we can /* If we aren't in the REVOKE pass, then we can
* just skip over this block. */ * just skip over this block. */
if (pass != PASS_REVOKE) { if (pass != PASS_REVOKE) {
...@@ -800,9 +857,6 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh, ...@@ -800,9 +857,6 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
offset = sizeof(jbd2_journal_revoke_header_t); offset = sizeof(jbd2_journal_revoke_header_t);
rcount = be32_to_cpu(header->r_count); rcount = be32_to_cpu(header->r_count);
if (!jbd2_descriptor_block_csum_verify(journal, header))
return -EFSBADCRC;
if (jbd2_journal_has_csum_v2or3(journal)) if (jbd2_journal_has_csum_v2or3(journal))
csum_size = sizeof(struct jbd2_journal_block_tail); csum_size = sizeof(struct jbd2_journal_block_tail);
if (rcount > journal->j_blocksize - csum_size) if (rcount > journal->j_blocksize - csum_size)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment