|
@@ -122,6 +122,38 @@ static int ext4_verify_csum_type(struct super_block *sb,
|
|
|
return es->s_checksum_type == EXT4_CRC32C_CHKSUM;
|
|
|
}
|
|
|
|
|
|
+static __le32 ext4_superblock_csum(struct super_block *sb,
|
|
|
+ struct ext4_super_block *es)
|
|
|
+{
|
|
|
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
+ int offset = offsetof(struct ext4_super_block, s_checksum);
|
|
|
+ __u32 csum;
|
|
|
+
|
|
|
+ csum = ext4_chksum(sbi, ~0, (char *)es, offset);
|
|
|
+
|
|
|
+ return cpu_to_le32(csum);
|
|
|
+}
|
|
|
+
|
|
|
+int ext4_superblock_csum_verify(struct super_block *sb,
|
|
|
+ struct ext4_super_block *es)
|
|
|
+{
|
|
|
+ if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
|
|
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ return es->s_checksum == ext4_superblock_csum(sb, es);
|
|
|
+}
|
|
|
+
|
|
|
+void ext4_superblock_csum_set(struct super_block *sb,
|
|
|
+ struct ext4_super_block *es)
|
|
|
+{
|
|
|
+ if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
|
|
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
|
|
|
+ return;
|
|
|
+
|
|
|
+ es->s_checksum = ext4_superblock_csum(sb, es);
|
|
|
+}
|
|
|
+
|
|
|
void *ext4_kvmalloc(size_t size, gfp_t flags)
|
|
|
{
|
|
|
void *ret;
|
|
@@ -3057,6 +3089,20 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* Check superblock checksum */
|
|
|
+ if (!ext4_superblock_csum_verify(sb, es)) {
|
|
|
+ ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with "
|
|
|
+ "invalid superblock checksum. Run e2fsck?");
|
|
|
+ silent = 1;
|
|
|
+ goto cantfind_ext4;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Precompute checksum seed for all metadata */
|
|
|
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
|
|
|
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
|
|
|
+ sbi->s_csum_seed = ext4_chksum(sbi, ~0, es->s_uuid,
|
|
|
+ sizeof(es->s_uuid));
|
|
|
+
|
|
|
/* Set defaults before we parse the mount options */
|
|
|
def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
|
|
|
set_opt(sb, INIT_INODE_TABLE);
|
|
@@ -4059,6 +4105,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
|
|
&EXT4_SB(sb)->s_freeinodes_counter));
|
|
|
sb->s_dirt = 0;
|
|
|
BUFFER_TRACE(sbh, "marking dirty");
|
|
|
+ ext4_superblock_csum_set(sb, es);
|
|
|
mark_buffer_dirty(sbh);
|
|
|
if (sync) {
|
|
|
error = sync_dirty_buffer(sbh);
|