diff options
Diffstat (limited to 'fs/jbd2')
-rw-r--r-- | fs/jbd2/Kconfig | 2 | ||||
-rw-r--r-- | fs/jbd2/commit.c | 70 | ||||
-rw-r--r-- | fs/jbd2/journal.c | 132 | ||||
-rw-r--r-- | fs/jbd2/recovery.c | 126 | ||||
-rw-r--r-- | fs/jbd2/revoke.c | 27 | ||||
-rw-r--r-- | fs/jbd2/transaction.c | 4 |
6 files changed, 349 insertions, 12 deletions
diff --git a/fs/jbd2/Kconfig b/fs/jbd2/Kconfig index f32f346f4b0a..69a48c2944da 100644 --- a/fs/jbd2/Kconfig +++ b/fs/jbd2/Kconfig | |||
@@ -1,6 +1,8 @@ | |||
1 | config JBD2 | 1 | config JBD2 |
2 | tristate | 2 | tristate |
3 | select CRC32 | 3 | select CRC32 |
4 | select CRYPTO | ||
5 | select CRYPTO_CRC32C | ||
4 | help | 6 | help |
5 | This is a generic journaling layer for block devices that support | 7 | This is a generic journaling layer for block devices that support |
6 | both 32-bit and 64-bit block numbers. It is currently used by | 8 | both 32-bit and 64-bit block numbers. It is currently used by |
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 840f70f50792..216f4299f65e 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c | |||
@@ -85,6 +85,24 @@ nope: | |||
85 | __brelse(bh); | 85 | __brelse(bh); |
86 | } | 86 | } |
87 | 87 | ||
88 | static void jbd2_commit_block_csum_set(journal_t *j, | ||
89 | struct journal_head *descriptor) | ||
90 | { | ||
91 | struct commit_header *h; | ||
92 | __u32 csum; | ||
93 | |||
94 | if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
95 | return; | ||
96 | |||
97 | h = (struct commit_header *)(jh2bh(descriptor)->b_data); | ||
98 | h->h_chksum_type = 0; | ||
99 | h->h_chksum_size = 0; | ||
100 | h->h_chksum[0] = 0; | ||
101 | csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data, | ||
102 | j->j_blocksize); | ||
103 | h->h_chksum[0] = cpu_to_be32(csum); | ||
104 | } | ||
105 | |||
88 | /* | 106 | /* |
89 | * Done it all: now submit the commit record. We should have | 107 | * Done it all: now submit the commit record. We should have |
90 | * cleaned up our previous buffers by now, so if we are in abort | 108 | * cleaned up our previous buffers by now, so if we are in abort |
@@ -128,6 +146,7 @@ static int journal_submit_commit_record(journal_t *journal, | |||
128 | tmp->h_chksum_size = JBD2_CRC32_CHKSUM_SIZE; | 146 | tmp->h_chksum_size = JBD2_CRC32_CHKSUM_SIZE; |
129 | tmp->h_chksum[0] = cpu_to_be32(crc32_sum); | 147 | tmp->h_chksum[0] = cpu_to_be32(crc32_sum); |
130 | } | 148 | } |
149 | jbd2_commit_block_csum_set(journal, descriptor); | ||
131 | 150 | ||
132 | JBUFFER_TRACE(descriptor, "submit commit block"); | 151 | JBUFFER_TRACE(descriptor, "submit commit block"); |
133 | lock_buffer(bh); | 152 | lock_buffer(bh); |
@@ -301,6 +320,44 @@ static void write_tag_block(int tag_bytes, journal_block_tag_t *tag, | |||
301 | tag->t_blocknr_high = cpu_to_be32((block >> 31) >> 1); | 320 | tag->t_blocknr_high = cpu_to_be32((block >> 31) >> 1); |
302 | } | 321 | } |
303 | 322 | ||
323 | static void jbd2_descr_block_csum_set(journal_t *j, | ||
324 | struct journal_head *descriptor) | ||
325 | { | ||
326 | struct jbd2_journal_block_tail *tail; | ||
327 | __u32 csum; | ||
328 | |||
329 | if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
330 | return; | ||
331 | |||
332 | tail = (struct jbd2_journal_block_tail *) | ||
333 | (jh2bh(descriptor)->b_data + j->j_blocksize - | ||
334 | sizeof(struct jbd2_journal_block_tail)); | ||
335 | tail->t_checksum = 0; | ||
336 | csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data, | ||
337 | j->j_blocksize); | ||
338 | tail->t_checksum = cpu_to_be32(csum); | ||
339 | } | ||
340 | |||
341 | static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, | ||
342 | struct buffer_head *bh, __u32 sequence) | ||
343 | { | ||
344 | struct page *page = bh->b_page; | ||
345 | __u8 *addr; | ||
346 | __u32 csum; | ||
347 | |||
348 | if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
349 | return; | ||
350 | |||
351 | sequence = cpu_to_be32(sequence); | ||
352 | addr = kmap_atomic(page, KM_USER0); | ||
353 | csum = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&sequence, | ||
354 | sizeof(sequence)); | ||
355 | csum = jbd2_chksum(j, csum, addr + offset_in_page(bh->b_data), | ||
356 | bh->b_size); | ||
357 | kunmap_atomic(addr, KM_USER0); | ||
358 | |||
359 | tag->t_checksum = cpu_to_be32(csum); | ||
360 | } | ||
304 | /* | 361 | /* |
305 | * jbd2_journal_commit_transaction | 362 | * jbd2_journal_commit_transaction |
306 | * | 363 | * |
@@ -334,6 +391,10 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
334 | unsigned long first_block; | 391 | unsigned long first_block; |
335 | tid_t first_tid; | 392 | tid_t first_tid; |
336 | int update_tail; | 393 | int update_tail; |
394 | int csum_size = 0; | ||
395 | |||
396 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
397 | csum_size = sizeof(struct jbd2_journal_block_tail); | ||
337 | 398 | ||
338 | /* | 399 | /* |
339 | * First job: lock down the current transaction and wait for | 400 | * First job: lock down the current transaction and wait for |
@@ -627,7 +688,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
627 | 688 | ||
628 | tag = (journal_block_tag_t *) tagp; | 689 | tag = (journal_block_tag_t *) tagp; |
629 | write_tag_block(tag_bytes, tag, jh2bh(jh)->b_blocknr); | 690 | write_tag_block(tag_bytes, tag, jh2bh(jh)->b_blocknr); |
630 | tag->t_flags = cpu_to_be32(tag_flag); | 691 | tag->t_flags = cpu_to_be16(tag_flag); |
692 | jbd2_block_tag_csum_set(journal, tag, jh2bh(new_jh), | ||
693 | commit_transaction->t_tid); | ||
631 | tagp += tag_bytes; | 694 | tagp += tag_bytes; |
632 | space_left -= tag_bytes; | 695 | space_left -= tag_bytes; |
633 | 696 | ||
@@ -643,7 +706,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
643 | 706 | ||
644 | if (bufs == journal->j_wbufsize || | 707 | if (bufs == journal->j_wbufsize || |
645 | commit_transaction->t_buffers == NULL || | 708 | commit_transaction->t_buffers == NULL || |
646 | space_left < tag_bytes + 16) { | 709 | space_left < tag_bytes + 16 + csum_size) { |
647 | 710 | ||
648 | jbd_debug(4, "JBD2: Submit %d IOs\n", bufs); | 711 | jbd_debug(4, "JBD2: Submit %d IOs\n", bufs); |
649 | 712 | ||
@@ -651,8 +714,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) | |||
651 | submitting the IOs. "tag" still points to | 714 | submitting the IOs. "tag" still points to |
652 | the last tag we set up. */ | 715 | the last tag we set up. */ |
653 | 716 | ||
654 | tag->t_flags |= cpu_to_be32(JBD2_FLAG_LAST_TAG); | 717 | tag->t_flags |= cpu_to_be16(JBD2_FLAG_LAST_TAG); |
655 | 718 | ||
719 | jbd2_descr_block_csum_set(journal, descriptor); | ||
656 | start_journal_io: | 720 | start_journal_io: |
657 | for (i = 0; i < bufs; i++) { | 721 | for (i = 0; i < bufs; i++) { |
658 | struct buffer_head *bh = wbuf[i]; | 722 | struct buffer_head *bh = wbuf[i]; |
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 1afb701622b0..e9a3c4c85594 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c | |||
@@ -97,6 +97,43 @@ EXPORT_SYMBOL(jbd2_inode_cache); | |||
97 | static void __journal_abort_soft (journal_t *journal, int errno); | 97 | static void __journal_abort_soft (journal_t *journal, int errno); |
98 | static int jbd2_journal_create_slab(size_t slab_size); | 98 | static int jbd2_journal_create_slab(size_t slab_size); |
99 | 99 | ||
100 | /* Checksumming functions */ | ||
101 | int jbd2_verify_csum_type(journal_t *j, journal_superblock_t *sb) | ||
102 | { | ||
103 | if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
104 | return 1; | ||
105 | |||
106 | return sb->s_checksum_type == JBD2_CRC32C_CHKSUM; | ||
107 | } | ||
108 | |||
109 | static __u32 jbd2_superblock_csum(journal_t *j, journal_superblock_t *sb) | ||
110 | { | ||
111 | __u32 csum, old_csum; | ||
112 | |||
113 | old_csum = sb->s_checksum; | ||
114 | sb->s_checksum = 0; | ||
115 | csum = jbd2_chksum(j, ~0, (char *)sb, sizeof(journal_superblock_t)); | ||
116 | sb->s_checksum = old_csum; | ||
117 | |||
118 | return cpu_to_be32(csum); | ||
119 | } | ||
120 | |||
121 | int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb) | ||
122 | { | ||
123 | if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
124 | return 1; | ||
125 | |||
126 | return sb->s_checksum == jbd2_superblock_csum(j, sb); | ||
127 | } | ||
128 | |||
129 | void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb) | ||
130 | { | ||
131 | if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
132 | return; | ||
133 | |||
134 | sb->s_checksum = jbd2_superblock_csum(j, sb); | ||
135 | } | ||
136 | |||
100 | /* | 137 | /* |
101 | * Helper function used to manage commit timeouts | 138 | * Helper function used to manage commit timeouts |
102 | */ | 139 | */ |
@@ -1348,6 +1385,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal) | |||
1348 | jbd_debug(1, "JBD2: updating superblock error (errno %d)\n", | 1385 | jbd_debug(1, "JBD2: updating superblock error (errno %d)\n", |
1349 | journal->j_errno); | 1386 | journal->j_errno); |
1350 | sb->s_errno = cpu_to_be32(journal->j_errno); | 1387 | sb->s_errno = cpu_to_be32(journal->j_errno); |
1388 | jbd2_superblock_csum_set(journal, sb); | ||
1351 | read_unlock(&journal->j_state_lock); | 1389 | read_unlock(&journal->j_state_lock); |
1352 | 1390 | ||
1353 | jbd2_write_superblock(journal, WRITE_SYNC); | 1391 | jbd2_write_superblock(journal, WRITE_SYNC); |
@@ -1376,6 +1414,9 @@ static int journal_get_superblock(journal_t *journal) | |||
1376 | } | 1414 | } |
1377 | } | 1415 | } |
1378 | 1416 | ||
1417 | if (buffer_verified(bh)) | ||
1418 | return 0; | ||
1419 | |||
1379 | sb = journal->j_superblock; | 1420 | sb = journal->j_superblock; |
1380 | 1421 | ||
1381 | err = -EINVAL; | 1422 | err = -EINVAL; |
@@ -1413,6 +1454,43 @@ static int journal_get_superblock(journal_t *journal) | |||
1413 | goto out; | 1454 | goto out; |
1414 | } | 1455 | } |
1415 | 1456 | ||
1457 | if (JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM) && | ||
1458 | JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) { | ||
1459 | /* Can't have checksum v1 and v2 on at the same time! */ | ||
1460 | printk(KERN_ERR "JBD: Can't enable checksumming v1 and v2 " | ||
1461 | "at the same time!\n"); | ||
1462 | goto out; | ||
1463 | } | ||
1464 | |||
1465 | if (!jbd2_verify_csum_type(journal, sb)) { | ||
1466 | printk(KERN_ERR "JBD: Unknown checksum type\n"); | ||
1467 | goto out; | ||
1468 | } | ||
1469 | |||
1470 | /* Load the checksum driver */ | ||
1471 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) { | ||
1472 | journal->j_chksum_driver = crypto_alloc_shash("crc32c", 0, 0); | ||
1473 | if (IS_ERR(journal->j_chksum_driver)) { | ||
1474 | printk(KERN_ERR "JBD: Cannot load crc32c driver.\n"); | ||
1475 | err = PTR_ERR(journal->j_chksum_driver); | ||
1476 | journal->j_chksum_driver = NULL; | ||
1477 | goto out; | ||
1478 | } | ||
1479 | } | ||
1480 | |||
1481 | /* Check superblock checksum */ | ||
1482 | if (!jbd2_superblock_csum_verify(journal, sb)) { | ||
1483 | printk(KERN_ERR "JBD: journal checksum error\n"); | ||
1484 | goto out; | ||
1485 | } | ||
1486 | |||
1487 | /* Precompute checksum seed for all metadata */ | ||
1488 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
1489 | journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid, | ||
1490 | sizeof(sb->s_uuid)); | ||
1491 | |||
1492 | set_buffer_verified(bh); | ||
1493 | |||
1416 | return 0; | 1494 | return 0; |
1417 | 1495 | ||
1418 | out: | 1496 | out: |
@@ -1564,6 +1642,8 @@ int jbd2_journal_destroy(journal_t *journal) | |||
1564 | iput(journal->j_inode); | 1642 | iput(journal->j_inode); |
1565 | if (journal->j_revoke) | 1643 | if (journal->j_revoke) |
1566 | jbd2_journal_destroy_revoke(journal); | 1644 | jbd2_journal_destroy_revoke(journal); |
1645 | if (journal->j_chksum_driver) | ||
1646 | crypto_free_shash(journal->j_chksum_driver); | ||
1567 | kfree(journal->j_wbuf); | 1647 | kfree(journal->j_wbuf); |
1568 | kfree(journal); | 1648 | kfree(journal); |
1569 | 1649 | ||
@@ -1653,6 +1733,10 @@ int jbd2_journal_check_available_features (journal_t *journal, unsigned long com | |||
1653 | int jbd2_journal_set_features (journal_t *journal, unsigned long compat, | 1733 | int jbd2_journal_set_features (journal_t *journal, unsigned long compat, |
1654 | unsigned long ro, unsigned long incompat) | 1734 | unsigned long ro, unsigned long incompat) |
1655 | { | 1735 | { |
1736 | #define INCOMPAT_FEATURE_ON(f) \ | ||
1737 | ((incompat & (f)) && !(sb->s_feature_incompat & cpu_to_be32(f))) | ||
1738 | #define COMPAT_FEATURE_ON(f) \ | ||
1739 | ((compat & (f)) && !(sb->s_feature_compat & cpu_to_be32(f))) | ||
1656 | journal_superblock_t *sb; | 1740 | journal_superblock_t *sb; |
1657 | 1741 | ||
1658 | if (jbd2_journal_check_used_features(journal, compat, ro, incompat)) | 1742 | if (jbd2_journal_check_used_features(journal, compat, ro, incompat)) |
@@ -1661,16 +1745,54 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, | |||
1661 | if (!jbd2_journal_check_available_features(journal, compat, ro, incompat)) | 1745 | if (!jbd2_journal_check_available_features(journal, compat, ro, incompat)) |
1662 | return 0; | 1746 | return 0; |
1663 | 1747 | ||
1748 | /* Asking for checksumming v2 and v1? Only give them v2. */ | ||
1749 | if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V2 && | ||
1750 | compat & JBD2_FEATURE_COMPAT_CHECKSUM) | ||
1751 | compat &= ~JBD2_FEATURE_COMPAT_CHECKSUM; | ||
1752 | |||
1664 | jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n", | 1753 | jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n", |
1665 | compat, ro, incompat); | 1754 | compat, ro, incompat); |
1666 | 1755 | ||
1667 | sb = journal->j_superblock; | 1756 | sb = journal->j_superblock; |
1668 | 1757 | ||
1758 | /* If enabling v2 checksums, update superblock */ | ||
1759 | if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V2)) { | ||
1760 | sb->s_checksum_type = JBD2_CRC32C_CHKSUM; | ||
1761 | sb->s_feature_compat &= | ||
1762 | ~cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM); | ||
1763 | |||
1764 | /* Load the checksum driver */ | ||
1765 | if (journal->j_chksum_driver == NULL) { | ||
1766 | journal->j_chksum_driver = crypto_alloc_shash("crc32c", | ||
1767 | 0, 0); | ||
1768 | if (IS_ERR(journal->j_chksum_driver)) { | ||
1769 | printk(KERN_ERR "JBD: Cannot load crc32c " | ||
1770 | "driver.\n"); | ||
1771 | journal->j_chksum_driver = NULL; | ||
1772 | return 0; | ||
1773 | } | ||
1774 | } | ||
1775 | |||
1776 | /* Precompute checksum seed for all metadata */ | ||
1777 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, | ||
1778 | JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
1779 | journal->j_csum_seed = jbd2_chksum(journal, ~0, | ||
1780 | sb->s_uuid, | ||
1781 | sizeof(sb->s_uuid)); | ||
1782 | } | ||
1783 | |||
1784 | /* If enabling v1 checksums, downgrade superblock */ | ||
1785 | if (COMPAT_FEATURE_ON(JBD2_FEATURE_COMPAT_CHECKSUM)) | ||
1786 | sb->s_feature_incompat &= | ||
1787 | ~cpu_to_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2); | ||
1788 | |||
1669 | sb->s_feature_compat |= cpu_to_be32(compat); | 1789 | sb->s_feature_compat |= cpu_to_be32(compat); |
1670 | sb->s_feature_ro_compat |= cpu_to_be32(ro); | 1790 | sb->s_feature_ro_compat |= cpu_to_be32(ro); |
1671 | sb->s_feature_incompat |= cpu_to_be32(incompat); | 1791 | sb->s_feature_incompat |= cpu_to_be32(incompat); |
1672 | 1792 | ||
1673 | return 1; | 1793 | return 1; |
1794 | #undef COMPAT_FEATURE_ON | ||
1795 | #undef INCOMPAT_FEATURE_ON | ||
1674 | } | 1796 | } |
1675 | 1797 | ||
1676 | /* | 1798 | /* |
@@ -1975,10 +2097,16 @@ int jbd2_journal_blocks_per_page(struct inode *inode) | |||
1975 | */ | 2097 | */ |
1976 | size_t journal_tag_bytes(journal_t *journal) | 2098 | size_t journal_tag_bytes(journal_t *journal) |
1977 | { | 2099 | { |
2100 | journal_block_tag_t tag; | ||
2101 | size_t x = 0; | ||
2102 | |||
2103 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
2104 | x += sizeof(tag.t_checksum); | ||
2105 | |||
1978 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) | 2106 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) |
1979 | return JBD2_TAG_SIZE64; | 2107 | return x + JBD2_TAG_SIZE64; |
1980 | else | 2108 | else |
1981 | return JBD2_TAG_SIZE32; | 2109 | return x + JBD2_TAG_SIZE32; |
1982 | } | 2110 | } |
1983 | 2111 | ||
1984 | /* | 2112 | /* |
diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index c1a03354a22f..0131e4362534 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 | ||
diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index 6973705d6a3d..f30b80b4ce8b 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c | |||
@@ -578,6 +578,7 @@ static void write_one_revoke_record(journal_t *journal, | |||
578 | struct jbd2_revoke_record_s *record, | 578 | struct jbd2_revoke_record_s *record, |
579 | int write_op) | 579 | int write_op) |
580 | { | 580 | { |
581 | int csum_size = 0; | ||
581 | struct journal_head *descriptor; | 582 | struct journal_head *descriptor; |
582 | int offset; | 583 | int offset; |
583 | journal_header_t *header; | 584 | journal_header_t *header; |
@@ -592,9 +593,13 @@ static void write_one_revoke_record(journal_t *journal, | |||
592 | descriptor = *descriptorp; | 593 | descriptor = *descriptorp; |
593 | offset = *offsetp; | 594 | offset = *offsetp; |
594 | 595 | ||
596 | /* Do we need to leave space at the end for a checksum? */ | ||
597 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
598 | csum_size = sizeof(struct jbd2_journal_revoke_tail); | ||
599 | |||
595 | /* Make sure we have a descriptor with space left for the record */ | 600 | /* Make sure we have a descriptor with space left for the record */ |
596 | if (descriptor) { | 601 | if (descriptor) { |
597 | if (offset == journal->j_blocksize) { | 602 | if (offset >= journal->j_blocksize - csum_size) { |
598 | flush_descriptor(journal, descriptor, offset, write_op); | 603 | flush_descriptor(journal, descriptor, offset, write_op); |
599 | descriptor = NULL; | 604 | descriptor = NULL; |
600 | } | 605 | } |
@@ -631,6 +636,24 @@ static void write_one_revoke_record(journal_t *journal, | |||
631 | *offsetp = offset; | 636 | *offsetp = offset; |
632 | } | 637 | } |
633 | 638 | ||
639 | static void jbd2_revoke_csum_set(journal_t *j, | ||
640 | struct journal_head *descriptor) | ||
641 | { | ||
642 | struct jbd2_journal_revoke_tail *tail; | ||
643 | __u32 csum; | ||
644 | |||
645 | if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) | ||
646 | return; | ||
647 | |||
648 | tail = (struct jbd2_journal_revoke_tail *) | ||
649 | (jh2bh(descriptor)->b_data + j->j_blocksize - | ||
650 | sizeof(struct jbd2_journal_revoke_tail)); | ||
651 | tail->r_checksum = 0; | ||
652 | csum = jbd2_chksum(j, j->j_csum_seed, jh2bh(descriptor)->b_data, | ||
653 | j->j_blocksize); | ||
654 | tail->r_checksum = cpu_to_be32(csum); | ||
655 | } | ||
656 | |||
634 | /* | 657 | /* |
635 | * Flush a revoke descriptor out to the journal. If we are aborting, | 658 | * Flush a revoke descriptor out to the journal. If we are aborting, |
636 | * this is a noop; otherwise we are generating a buffer which needs to | 659 | * this is a noop; otherwise we are generating a buffer which needs to |
@@ -652,6 +675,8 @@ static void flush_descriptor(journal_t *journal, | |||
652 | 675 | ||
653 | header = (jbd2_journal_revoke_header_t *) jh2bh(descriptor)->b_data; | 676 | header = (jbd2_journal_revoke_header_t *) jh2bh(descriptor)->b_data; |
654 | header->r_count = cpu_to_be32(offset); | 677 | header->r_count = cpu_to_be32(offset); |
678 | jbd2_revoke_csum_set(journal, descriptor); | ||
679 | |||
655 | set_buffer_jwrite(bh); | 680 | set_buffer_jwrite(bh); |
656 | BUFFER_TRACE(bh, "write"); | 681 | BUFFER_TRACE(bh, "write"); |
657 | set_buffer_dirty(bh); | 682 | set_buffer_dirty(bh); |
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index ddcd3549c6c2..fb1ab9533b67 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c | |||
@@ -162,8 +162,8 @@ static int start_this_handle(journal_t *journal, handle_t *handle, | |||
162 | 162 | ||
163 | alloc_transaction: | 163 | alloc_transaction: |
164 | if (!journal->j_running_transaction) { | 164 | if (!journal->j_running_transaction) { |
165 | new_transaction = kmem_cache_alloc(transaction_cache, | 165 | new_transaction = kmem_cache_zalloc(transaction_cache, |
166 | gfp_mask | __GFP_ZERO); | 166 | gfp_mask); |
167 | if (!new_transaction) { | 167 | if (!new_transaction) { |
168 | /* | 168 | /* |
169 | * If __GFP_FS is not present, then we may be | 169 | * If __GFP_FS is not present, then we may be |