diff options
Diffstat (limited to 'fs/jbd2/recovery.c')
-rw-r--r-- | fs/jbd2/recovery.c | 126 |
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 | ||
177 | static 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 | ||
378 | static 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 | |||
396 | static 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 | |||
356 | static int do_one_pass(journal_t *journal, | 413 | static 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 | ||
805 | static 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 | ||