aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jbd2/recovery.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/jbd2/recovery.c')
-rw-r--r--fs/jbd2/recovery.c126
1 files changed, 122 insertions, 4 deletions
diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c
index c1a03354a22..0131e436253 100644
--- a/fs/jbd2/recovery.c
+++ b/fs/jbd2/recovery.c
@@ -174,6 +174,25 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
174 return 0; 174 return 0;
175} 175}
176 176
177static int jbd2_descr_block_csum_verify(journal_t *j,
178 void *buf)
179{
180 struct jbd2_journal_block_tail *tail;
181 __u32 provided, calculated;
182
183 if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
184 return 1;
185
186 tail = (struct jbd2_journal_block_tail *)(buf + j->j_blocksize -
187 sizeof(struct jbd2_journal_block_tail));
188 provided = tail->t_checksum;
189 tail->t_checksum = 0;
190 calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
191 tail->t_checksum = provided;
192
193 provided = be32_to_cpu(provided);
194 return provided == calculated;
195}
177 196
178/* 197/*
179 * Count the number of in-use tags in a journal descriptor block. 198 * Count the number of in-use tags in a journal descriptor block.
@@ -186,6 +205,9 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
186 int nr = 0, size = journal->j_blocksize; 205 int nr = 0, size = journal->j_blocksize;
187 int tag_bytes = journal_tag_bytes(journal); 206 int tag_bytes = journal_tag_bytes(journal);
188 207
208 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
209 size -= sizeof(struct jbd2_journal_block_tail);
210
189 tagp = &bh->b_data[sizeof(journal_header_t)]; 211 tagp = &bh->b_data[sizeof(journal_header_t)];
190 212
191 while ((tagp - bh->b_data + tag_bytes) <= size) { 213 while ((tagp - bh->b_data + tag_bytes) <= size) {
@@ -193,10 +215,10 @@ static int count_tags(journal_t *journal, struct buffer_head *bh)
193 215
194 nr++; 216 nr++;
195 tagp += tag_bytes; 217 tagp += tag_bytes;
196 if (!(tag->t_flags & cpu_to_be32(JBD2_FLAG_SAME_UUID))) 218 if (!(tag->t_flags & cpu_to_be16(JBD2_FLAG_SAME_UUID)))
197 tagp += 16; 219 tagp += 16;
198 220
199 if (tag->t_flags & cpu_to_be32(JBD2_FLAG_LAST_TAG)) 221 if (tag->t_flags & cpu_to_be16(JBD2_FLAG_LAST_TAG))
200 break; 222 break;
201 } 223 }
202 224
@@ -353,6 +375,41 @@ static int calc_chksums(journal_t *journal, struct buffer_head *bh,
353 return 0; 375 return 0;
354} 376}
355 377
378static int jbd2_commit_block_csum_verify(journal_t *j, void *buf)
379{
380 struct commit_header *h;
381 __u32 provided, calculated;
382
383 if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
384 return 1;
385
386 h = buf;
387 provided = h->h_chksum[0];
388 h->h_chksum[0] = 0;
389 calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
390 h->h_chksum[0] = provided;
391
392 provided = be32_to_cpu(provided);
393 return provided == calculated;
394}
395
396static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
397 void *buf, __u32 sequence)
398{
399 __u32 provided, calculated;
400
401 if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
402 return 1;
403
404 sequence = cpu_to_be32(sequence);
405 calculated = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&sequence,
406 sizeof(sequence));
407 calculated = jbd2_chksum(j, calculated, buf, j->j_blocksize);
408 provided = be32_to_cpu(tag->t_checksum);
409
410 return provided == cpu_to_be32(calculated);
411}
412
356static int do_one_pass(journal_t *journal, 413static int do_one_pass(journal_t *journal,
357 struct recovery_info *info, enum passtype pass) 414 struct recovery_info *info, enum passtype pass)
358{ 415{
@@ -366,6 +423,7 @@ static int do_one_pass(journal_t *journal,
366 int blocktype; 423 int blocktype;
367 int tag_bytes = journal_tag_bytes(journal); 424 int tag_bytes = journal_tag_bytes(journal);
368 __u32 crc32_sum = ~0; /* Transactional Checksums */ 425 __u32 crc32_sum = ~0; /* Transactional Checksums */
426 int descr_csum_size = 0;
369 427
370 /* 428 /*
371 * First thing is to establish what we expect to find in the log 429 * First thing is to establish what we expect to find in the log
@@ -451,6 +509,18 @@ static int do_one_pass(journal_t *journal,
451 509
452 switch(blocktype) { 510 switch(blocktype) {
453 case JBD2_DESCRIPTOR_BLOCK: 511 case JBD2_DESCRIPTOR_BLOCK:
512 /* Verify checksum first */
513 if (JBD2_HAS_INCOMPAT_FEATURE(journal,
514 JBD2_FEATURE_INCOMPAT_CSUM_V2))
515 descr_csum_size =
516 sizeof(struct jbd2_journal_block_tail);
517 if (descr_csum_size > 0 &&
518 !jbd2_descr_block_csum_verify(journal,
519 bh->b_data)) {
520 err = -EIO;
521 goto failed;
522 }
523
454 /* If it is a valid descriptor block, replay it 524 /* If it is a valid descriptor block, replay it
455 * in pass REPLAY; if journal_checksums enabled, then 525 * in pass REPLAY; if journal_checksums enabled, then
456 * calculate checksums in PASS_SCAN, otherwise, 526 * calculate checksums in PASS_SCAN, otherwise,
@@ -481,11 +551,11 @@ static int do_one_pass(journal_t *journal,
481 551
482 tagp = &bh->b_data[sizeof(journal_header_t)]; 552 tagp = &bh->b_data[sizeof(journal_header_t)];
483 while ((tagp - bh->b_data + tag_bytes) 553 while ((tagp - bh->b_data + tag_bytes)
484 <= journal->j_blocksize) { 554 <= journal->j_blocksize - descr_csum_size) {
485 unsigned long io_block; 555 unsigned long io_block;
486 556
487 tag = (journal_block_tag_t *) tagp; 557 tag = (journal_block_tag_t *) tagp;
488 flags = be32_to_cpu(tag->t_flags); 558 flags = be16_to_cpu(tag->t_flags);
489 559
490 io_block = next_log_block++; 560 io_block = next_log_block++;
491 wrap(journal, next_log_block); 561 wrap(journal, next_log_block);
@@ -516,6 +586,19 @@ static int do_one_pass(journal_t *journal,
516 goto skip_write; 586 goto skip_write;
517 } 587 }
518 588
589 /* Look for block corruption */
590 if (!jbd2_block_tag_csum_verify(
591 journal, tag, obh->b_data,
592 be32_to_cpu(tmp->h_sequence))) {
593 brelse(obh);
594 success = -EIO;
595 printk(KERN_ERR "JBD: Invalid "
596 "checksum recovering "
597 "block %llu in log\n",
598 blocknr);
599 continue;
600 }
601
519 /* Find a buffer for the new 602 /* Find a buffer for the new
520 * data being restored */ 603 * data being restored */
521 nbh = __getblk(journal->j_fs_dev, 604 nbh = __getblk(journal->j_fs_dev,
@@ -650,6 +733,19 @@ static int do_one_pass(journal_t *journal,
650 } 733 }
651 crc32_sum = ~0; 734 crc32_sum = ~0;
652 } 735 }
736 if (pass == PASS_SCAN &&
737 !jbd2_commit_block_csum_verify(journal,
738 bh->b_data)) {
739 info->end_transaction = next_commit_ID;
740
741 if (!JBD2_HAS_INCOMPAT_FEATURE(journal,
742 JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
743 journal->j_failed_commit =
744 next_commit_ID;
745 brelse(bh);
746 break;
747 }
748 }
653 brelse(bh); 749 brelse(bh);
654 next_commit_ID++; 750 next_commit_ID++;
655 continue; 751 continue;
@@ -706,6 +802,25 @@ static int do_one_pass(journal_t *journal,
706 return err; 802 return err;
707} 803}
708 804
805static int jbd2_revoke_block_csum_verify(journal_t *j,
806 void *buf)
807{
808 struct jbd2_journal_revoke_tail *tail;
809 __u32 provided, calculated;
810
811 if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
812 return 1;
813
814 tail = (struct jbd2_journal_revoke_tail *)(buf + j->j_blocksize -
815 sizeof(struct jbd2_journal_revoke_tail));
816 provided = tail->r_checksum;
817 tail->r_checksum = 0;
818 calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize);
819 tail->r_checksum = provided;
820
821 provided = be32_to_cpu(provided);
822 return provided == calculated;
823}
709 824
710/* Scan a revoke record, marking all blocks mentioned as revoked. */ 825/* Scan a revoke record, marking all blocks mentioned as revoked. */
711 826
@@ -720,6 +835,9 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
720 offset = sizeof(jbd2_journal_revoke_header_t); 835 offset = sizeof(jbd2_journal_revoke_header_t);
721 max = be32_to_cpu(header->r_count); 836 max = be32_to_cpu(header->r_count);
722 837
838 if (!jbd2_revoke_block_csum_verify(journal, header))
839 return -EINVAL;
840
723 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) 841 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
724 record_len = 8; 842 record_len = 8;
725 843