ext4: make block group checksums use metadata_csum algorithm
metadata_csum supersedes uninit_bg. Convert the ROCOMPAT uninit_bg flag check to a helper function that covers both, and make the checksum calculation algorithm use either crc16 or the metadata_csum chosen algorithm depending on which flag is set. Print a warning if we try to mount a filesystem with both feature flags set. Signed-off-by: Darrick J. Wong <djwong@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
This commit is contained in:
parent
cc8e94fd12
commit
feb0ab32a5
|
@ -168,7 +168,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
|
|||
|
||||
/* If checksum is bad mark all blocks used to prevent allocation
|
||||
* essentially implementing a per-group read-only flag. */
|
||||
if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) {
|
||||
if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
|
||||
ext4_error(sb, "Checksum bad for group %u", block_group);
|
||||
ext4_free_group_clusters_set(sb, gdp, 0);
|
||||
ext4_free_inodes_set(sb, gdp, 0);
|
||||
|
@ -214,7 +214,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
|
|||
sb->s_blocksize * 8, bh->b_data);
|
||||
ext4_block_bitmap_csum_set(sb, block_group, gdp, bh,
|
||||
EXT4_BLOCKS_PER_GROUP(sb) / 8);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp);
|
||||
ext4_group_desc_csum_set(sb, block_group, gdp);
|
||||
}
|
||||
|
||||
/* Return the number of free blocks in a block group. It is used when
|
||||
|
|
|
@ -2114,10 +2114,10 @@ extern void ext4_used_dirs_set(struct super_block *sb,
|
|||
struct ext4_group_desc *bg, __u32 count);
|
||||
extern void ext4_itable_unused_set(struct super_block *sb,
|
||||
struct ext4_group_desc *bg, __u32 count);
|
||||
extern __le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 group,
|
||||
struct ext4_group_desc *gdp);
|
||||
extern int ext4_group_desc_csum_verify(struct ext4_sb_info *sbi, __u32 group,
|
||||
extern int ext4_group_desc_csum_verify(struct super_block *sb, __u32 group,
|
||||
struct ext4_group_desc *gdp);
|
||||
extern void ext4_group_desc_csum_set(struct super_block *sb, __u32 group,
|
||||
struct ext4_group_desc *gdp);
|
||||
|
||||
static inline int ext4_has_group_desc_csum(struct super_block *sb)
|
||||
{
|
||||
|
|
|
@ -70,13 +70,11 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,
|
|||
ext4_group_t block_group,
|
||||
struct ext4_group_desc *gdp)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
|
||||
J_ASSERT_BH(bh, buffer_locked(bh));
|
||||
|
||||
/* If checksum is bad mark all blocks and inodes use to prevent
|
||||
* allocation, essentially implementing a per-group read-only flag. */
|
||||
if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) {
|
||||
if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {
|
||||
ext4_error(sb, "Checksum bad for group %u", block_group);
|
||||
ext4_free_group_clusters_set(sb, gdp, 0);
|
||||
ext4_free_inodes_set(sb, gdp, 0);
|
||||
|
@ -92,7 +90,7 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,
|
|||
bh->b_data);
|
||||
ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh,
|
||||
EXT4_INODES_PER_GROUP(sb) / 8);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp);
|
||||
ext4_group_desc_csum_set(sb, block_group, gdp);
|
||||
|
||||
return EXT4_INODES_PER_GROUP(sb);
|
||||
}
|
||||
|
@ -298,7 +296,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
|
|||
}
|
||||
ext4_inode_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
|
||||
EXT4_INODES_PER_GROUP(sb) / 8);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp);
|
||||
ext4_group_desc_csum_set(sb, block_group, gdp);
|
||||
ext4_unlock_group(sb, block_group);
|
||||
|
||||
percpu_counter_inc(&sbi->s_freeinodes_counter);
|
||||
|
@ -731,7 +729,7 @@ repeat_in_this_group:
|
|||
|
||||
got:
|
||||
/* We may have to initialize the block bitmap if it isn't already */
|
||||
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
|
||||
if (ext4_has_group_desc_csum(sb) &&
|
||||
gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
|
||||
struct buffer_head *block_bitmap_bh;
|
||||
|
||||
|
@ -757,8 +755,7 @@ got:
|
|||
block_bitmap_bh,
|
||||
EXT4_BLOCKS_PER_GROUP(sb) /
|
||||
8);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, group,
|
||||
gdp);
|
||||
ext4_group_desc_csum_set(sb, group, gdp);
|
||||
}
|
||||
ext4_unlock_group(sb, group);
|
||||
|
||||
|
@ -811,7 +808,7 @@ got:
|
|||
if (ext4_has_group_desc_csum(sb)) {
|
||||
ext4_inode_bitmap_csum_set(sb, group, gdp, inode_bitmap_bh,
|
||||
EXT4_INODES_PER_GROUP(sb) / 8);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp);
|
||||
ext4_group_desc_csum_set(sb, group, gdp);
|
||||
ext4_unlock_group(sb, group);
|
||||
}
|
||||
|
||||
|
@ -1181,7 +1178,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group,
|
|||
skip_zeroout:
|
||||
ext4_lock_group(sb, group);
|
||||
gdp->bg_flags |= cpu_to_le16(EXT4_BG_INODE_ZEROED);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp);
|
||||
ext4_group_desc_csum_set(sb, group, gdp);
|
||||
ext4_unlock_group(sb, group);
|
||||
|
||||
BUFFER_TRACE(group_desc_bh,
|
||||
|
|
|
@ -3584,8 +3584,7 @@ make_io:
|
|||
b = table;
|
||||
end = b + EXT4_SB(sb)->s_inode_readahead_blks;
|
||||
num = EXT4_INODES_PER_GROUP(sb);
|
||||
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
||||
EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
|
||||
if (ext4_has_group_desc_csum(sb))
|
||||
num -= ext4_itable_unused_count(sb, gdp);
|
||||
table += num / inodes_per_block;
|
||||
if (end > table)
|
||||
|
|
|
@ -2799,7 +2799,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
|
|||
ext4_free_group_clusters_set(sb, gdp, len);
|
||||
ext4_block_bitmap_csum_set(sb, ac->ac_b_ex.fe_group, gdp, bitmap_bh,
|
||||
EXT4_BLOCKS_PER_GROUP(sb) / 8);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp);
|
||||
ext4_group_desc_csum_set(sb, ac->ac_b_ex.fe_group, gdp);
|
||||
|
||||
ext4_unlock_group(sb, ac->ac_b_ex.fe_group);
|
||||
percpu_counter_sub(&sbi->s_freeclusters_counter, ac->ac_b_ex.fe_len);
|
||||
|
@ -4663,7 +4663,7 @@ do_more:
|
|||
ext4_free_group_clusters_set(sb, gdp, ret);
|
||||
ext4_block_bitmap_csum_set(sb, block_group, gdp, bitmap_bh,
|
||||
EXT4_BLOCKS_PER_GROUP(sb) / 8);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp);
|
||||
ext4_group_desc_csum_set(sb, block_group, gdp);
|
||||
ext4_unlock_group(sb, block_group);
|
||||
percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters);
|
||||
|
||||
|
@ -4809,7 +4809,7 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
|
|||
ext4_free_group_clusters_set(sb, desc, blk_free_count);
|
||||
ext4_block_bitmap_csum_set(sb, block_group, desc, bitmap_bh,
|
||||
EXT4_BLOCKS_PER_GROUP(sb) / 8);
|
||||
desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc);
|
||||
ext4_group_desc_csum_set(sb, block_group, desc);
|
||||
ext4_unlock_group(sb, block_group);
|
||||
percpu_counter_add(&sbi->s_freeclusters_counter,
|
||||
EXT4_B2C(sbi, blocks_freed));
|
||||
|
|
|
@ -1160,7 +1160,7 @@ static int ext4_setup_new_descs(handle_t *handle, struct super_block *sb,
|
|||
EXT4_B2C(sbi, group_data->free_blocks_count));
|
||||
ext4_free_inodes_set(sb, gdp, EXT4_INODES_PER_GROUP(sb));
|
||||
gdp->bg_flags = cpu_to_le16(*bg_flags);
|
||||
gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp);
|
||||
ext4_group_desc_csum_set(sb, group, gdp);
|
||||
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh);
|
||||
if (unlikely(err)) {
|
||||
|
@ -1399,17 +1399,14 @@ static int ext4_setup_next_flex_gd(struct super_block *sb,
|
|||
(1 + ext4_bg_num_gdb(sb, group + i) +
|
||||
le16_to_cpu(es->s_reserved_gdt_blocks)) : 0;
|
||||
group_data[i].free_blocks_count = blocks_per_group - overhead;
|
||||
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
||||
EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
|
||||
if (ext4_has_group_desc_csum(sb))
|
||||
flex_gd->bg_flags[i] = EXT4_BG_BLOCK_UNINIT |
|
||||
EXT4_BG_INODE_UNINIT;
|
||||
else
|
||||
flex_gd->bg_flags[i] = EXT4_BG_INODE_ZEROED;
|
||||
}
|
||||
|
||||
if (last_group == n_group &&
|
||||
EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
||||
EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
|
||||
if (last_group == n_group && ext4_has_group_desc_csum(sb))
|
||||
/* We need to initialize block bitmap of last group. */
|
||||
flex_gd->bg_flags[i - 1] &= ~EXT4_BG_BLOCK_UNINIT;
|
||||
|
||||
|
|
|
@ -1952,43 +1952,69 @@ failed:
|
|||
return 0;
|
||||
}
|
||||
|
||||
__le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 block_group,
|
||||
struct ext4_group_desc *gdp)
|
||||
static __le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 block_group,
|
||||
struct ext4_group_desc *gdp)
|
||||
{
|
||||
int offset;
|
||||
__u16 crc = 0;
|
||||
__le32 le_group = cpu_to_le32(block_group);
|
||||
|
||||
if (sbi->s_es->s_feature_ro_compat &
|
||||
cpu_to_le32(EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
|
||||
int offset = offsetof(struct ext4_group_desc, bg_checksum);
|
||||
__le32 le_group = cpu_to_le32(block_group);
|
||||
if ((sbi->s_es->s_feature_ro_compat &
|
||||
cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))) {
|
||||
/* Use new metadata_csum algorithm */
|
||||
__u16 old_csum;
|
||||
__u32 csum32;
|
||||
|
||||
crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid));
|
||||
crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group));
|
||||
crc = crc16(crc, (__u8 *)gdp, offset);
|
||||
offset += sizeof(gdp->bg_checksum); /* skip checksum */
|
||||
/* for checksum of struct ext4_group_desc do the rest...*/
|
||||
if ((sbi->s_es->s_feature_incompat &
|
||||
cpu_to_le32(EXT4_FEATURE_INCOMPAT_64BIT)) &&
|
||||
offset < le16_to_cpu(sbi->s_es->s_desc_size))
|
||||
crc = crc16(crc, (__u8 *)gdp + offset,
|
||||
le16_to_cpu(sbi->s_es->s_desc_size) -
|
||||
offset);
|
||||
old_csum = gdp->bg_checksum;
|
||||
gdp->bg_checksum = 0;
|
||||
csum32 = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&le_group,
|
||||
sizeof(le_group));
|
||||
csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp,
|
||||
sbi->s_desc_size);
|
||||
gdp->bg_checksum = old_csum;
|
||||
|
||||
crc = csum32 & 0xFFFF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* old crc16 code */
|
||||
offset = offsetof(struct ext4_group_desc, bg_checksum);
|
||||
|
||||
crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid));
|
||||
crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group));
|
||||
crc = crc16(crc, (__u8 *)gdp, offset);
|
||||
offset += sizeof(gdp->bg_checksum); /* skip checksum */
|
||||
/* for checksum of struct ext4_group_desc do the rest...*/
|
||||
if ((sbi->s_es->s_feature_incompat &
|
||||
cpu_to_le32(EXT4_FEATURE_INCOMPAT_64BIT)) &&
|
||||
offset < le16_to_cpu(sbi->s_es->s_desc_size))
|
||||
crc = crc16(crc, (__u8 *)gdp + offset,
|
||||
le16_to_cpu(sbi->s_es->s_desc_size) -
|
||||
offset);
|
||||
|
||||
out:
|
||||
return cpu_to_le16(crc);
|
||||
}
|
||||
|
||||
int ext4_group_desc_csum_verify(struct ext4_sb_info *sbi, __u32 block_group,
|
||||
int ext4_group_desc_csum_verify(struct super_block *sb, __u32 block_group,
|
||||
struct ext4_group_desc *gdp)
|
||||
{
|
||||
if ((sbi->s_es->s_feature_ro_compat &
|
||||
cpu_to_le32(EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) &&
|
||||
(gdp->bg_checksum != ext4_group_desc_csum(sbi, block_group, gdp)))
|
||||
if (ext4_has_group_desc_csum(sb) &&
|
||||
(gdp->bg_checksum != ext4_group_desc_csum(EXT4_SB(sb),
|
||||
block_group, gdp)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ext4_group_desc_csum_set(struct super_block *sb, __u32 block_group,
|
||||
struct ext4_group_desc *gdp)
|
||||
{
|
||||
if (!ext4_has_group_desc_csum(sb))
|
||||
return;
|
||||
gdp->bg_checksum = ext4_group_desc_csum(EXT4_SB(sb), block_group, gdp);
|
||||
}
|
||||
|
||||
/* Called at mount-time, super-block is locked */
|
||||
static int ext4_check_descriptors(struct super_block *sb,
|
||||
ext4_group_t *first_not_zeroed)
|
||||
|
@ -2043,7 +2069,7 @@ static int ext4_check_descriptors(struct super_block *sb,
|
|||
return 0;
|
||||
}
|
||||
ext4_lock_group(sb, i);
|
||||
if (!ext4_group_desc_csum_verify(sbi, i, gdp)) {
|
||||
if (!ext4_group_desc_csum_verify(sb, i, gdp)) {
|
||||
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
||||
"Checksum for group %u failed (%u!=%u)",
|
||||
i, le16_to_cpu(ext4_group_desc_csum(sbi, i,
|
||||
|
@ -3069,6 +3095,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|||
goto cantfind_ext4;
|
||||
sbi->s_kbytes_written = le64_to_cpu(es->s_kbytes_written);
|
||||
|
||||
/* Warn if metadata_csum and gdt_csum are both set. */
|
||||
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
||||
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
|
||||
EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
|
||||
ext4_warning(sb, KERN_INFO "metadata_csum and uninit_bg are "
|
||||
"redundant flags; please run fsck.");
|
||||
|
||||
/* Check for a known checksum algorithm */
|
||||
if (!ext4_verify_csum_type(sb, es)) {
|
||||
ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with "
|
||||
|
@ -4400,7 +4433,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
|
|||
struct ext4_group_desc *gdp =
|
||||
ext4_get_group_desc(sb, g, NULL);
|
||||
|
||||
if (!ext4_group_desc_csum_verify(sbi, g, gdp)) {
|
||||
if (!ext4_group_desc_csum_verify(sb, g, gdp)) {
|
||||
ext4_msg(sb, KERN_ERR,
|
||||
"ext4_remount: Checksum for group %u failed (%u!=%u)",
|
||||
g, le16_to_cpu(ext4_group_desc_csum(sbi, g, gdp)),
|
||||
|
|
Loading…
Reference in New Issue