diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index a4967b27ffb6..631a42b50516 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -417,7 +417,7 @@ static int do_one_pass(journal_t *journal, struct recovery_info *info, enum passtype pass) { unsigned int first_commit_ID, next_commit_ID; - unsigned long next_log_block; + unsigned long next_log_block, ri_commit_block = 0; int err, success = 0; journal_superblock_t * sb; journal_header_t * tmp; @@ -428,6 +428,8 @@ static int do_one_pass(journal_t *journal, __u32 crc32_sum = ~0; /* Transactional Checksums */ int descr_csum_size = 0; int block_error = 0; + bool need_check_commit_time = false; + __be64 last_trans_commit_time = 0; /* * First thing is to establish what we expect to find in the log @@ -514,18 +516,17 @@ static int do_one_pass(journal_t *journal, switch(blocktype) { case JBD2_DESCRIPTOR_BLOCK: /* Verify checksum first */ + if (pass == PASS_SCAN) + ri_commit_block = 0; if (jbd2_journal_has_csum_v2or3(journal)) descr_csum_size = sizeof(struct jbd2_journal_block_tail); if (descr_csum_size > 0 && !jbd2_descriptor_block_csum_verify(journal, bh->b_data)) { - printk(KERN_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, continue recovery first.\n", + next_log_block); } /* If it is a valid descriptor block, replay it @@ -535,6 +536,7 @@ static int do_one_pass(journal_t *journal, if (pass != PASS_REPLAY) { if (pass == PASS_SCAN && jbd2_has_feature_checksum(journal) && + !need_check_commit_time && !info->end_transaction) { if (calc_chksums(journal, bh, &next_log_block, @@ -688,6 +690,47 @@ static int do_one_pass(journal_t *journal, * are present verify them in PASS_SCAN; else not * much to do other than move on to the next sequence * number. */ + if (pass == PASS_SCAN) { + struct commit_header *cbh = + (struct commit_header *)bh->b_data; + /* + * When need check commit time, it means csum + * verify failed before, if commit time is + * increasing, it's same journal, otherwise + * not same journal, just end this recovery. + */ + if (need_check_commit_time) { + __be64 commit_time = + be64_to_cpu(cbh->h_commit_sec); + + if (commit_time >= last_trans_commit_time) { + pr_err("JBD2: Invalid checksum found in log, %d\n", + next_commit_ID); + err = -EFSBADCRC; + brelse(bh); + goto failed; + } else { + /* + * it's not belong to same journal, just + * end this recovery with success. + */ + jbd_debug(1, "JBD2: Invalid checksum found in block in log, but not same journal %d\n", + next_commit_ID); + err = 0; + brelse(bh); + goto done; + } + } + if (ri_commit_block) { + jbd_debug(1, "invalid commit block found in %lu, stop here.\n", + next_log_block); + brelse(bh); + goto done; + } + ri_commit_block = next_log_block; + last_trans_commit_time = + be64_to_cpu(cbh->h_commit_sec); + } if (pass == PASS_SCAN && jbd2_has_feature_checksum(journal)) { int chksum_err, chksum_seen; @@ -755,6 +798,21 @@ static int do_one_pass(journal_t *journal, continue; 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_journal_revoke_header_t *header = + (jbd2_journal_revoke_header_t *)bh->b_data; + ri_commit_block = 0; + if (!jbd2_descriptor_block_csum_verify(journal, + header)) { + jbd_debug(1, "invalid revoke block found in %lu, continue recovery first.\n", + next_log_block); + need_check_commit_time = true; + } + } /* If we aren't in the REVOKE pass, then we can * just skip over this block. */ if (pass != PASS_REVOKE) { @@ -822,9 +880,6 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh, offset = sizeof(jbd2_journal_revoke_header_t); rcount = be32_to_cpu(header->r_count); - if (!jbd2_descriptor_block_csum_verify(journal, header)) - return -EFSBADCRC; - if (jbd2_journal_has_csum_v2or3(journal)) csum_size = sizeof(struct jbd2_journal_block_tail); if (rcount > journal->j_blocksize - csum_size)