Skip to content

Commit

Permalink
ext4: calculate and verify superblock checksum
Browse files Browse the repository at this point in the history
Calculate and verify the superblock checksum.  Since the UUID and
block group number are embedded in each copy of the superblock, we
need only checksum the entire block.  Refactor some of the code to
eliminate open-coding of the checksum update call.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
  • Loading branch information
Darrick J. Wong authored and Theodore Ts'o committed Apr 29, 2012
1 parent 0441984 commit a9c4731
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 7 deletions.
10 changes: 10 additions & 0 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,9 @@ struct ext4_sb_info {

/* Reference to checksum algorithm driver via cryptoapi */
struct crypto_shash *s_chksum_driver;

/* Precomputed FS UUID checksum for seeding other checksums */
__u32 s_csum_seed;
};

static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
Expand Down Expand Up @@ -2004,6 +2007,10 @@ extern int ext4_group_extend(struct super_block *sb,
extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);

/* super.c */
extern int ext4_superblock_csum_verify(struct super_block *sb,
struct ext4_super_block *es);
extern void ext4_superblock_csum_set(struct super_block *sb,
struct ext4_super_block *es);
extern void *ext4_kvmalloc(size_t size, gfp_t flags);
extern void *ext4_kvzalloc(size_t size, gfp_t flags);
extern void ext4_kvfree(void *ptr);
Expand Down Expand Up @@ -2279,6 +2286,9 @@ static inline void ext4_unlock_group(struct super_block *sb,

static inline void ext4_mark_super_dirty(struct super_block *sb)
{
struct ext4_super_block *es = EXT4_SB(sb)->s_es;

ext4_superblock_csum_set(sb, es);
if (EXT4_SB(sb)->s_journal == NULL)
sb->s_dirt =1;
}
Expand Down
9 changes: 8 additions & 1 deletion fs/ext4/ext4_jbd2.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,23 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
}

int __ext4_handle_dirty_super(const char *where, unsigned int line,
handle_t *handle, struct super_block *sb)
handle_t *handle, struct super_block *sb,
int now)
{
struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
int err = 0;

if (ext4_handle_valid(handle)) {
ext4_superblock_csum_set(sb,
(struct ext4_super_block *)bh->b_data);
err = jbd2_journal_dirty_metadata(handle, bh);
if (err)
ext4_journal_abort_handle(where, line, __func__,
bh, handle, err);
} else if (now) {
ext4_superblock_csum_set(sb,
(struct ext4_super_block *)bh->b_data);
mark_buffer_dirty(bh);
} else
sb->s_dirt = 1;
return err;
Expand Down
7 changes: 5 additions & 2 deletions fs/ext4/ext4_jbd2.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
struct buffer_head *bh);

int __ext4_handle_dirty_super(const char *where, unsigned int line,
handle_t *handle, struct super_block *sb);
handle_t *handle, struct super_block *sb,
int now);

#define ext4_journal_get_write_access(handle, bh) \
__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
Expand All @@ -225,8 +226,10 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
#define ext4_handle_dirty_metadata(handle, inode, bh) \
__ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
(bh))
#define ext4_handle_dirty_super_now(handle, sb) \
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 1)
#define ext4_handle_dirty_super(handle, sb) \
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 0)

handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
Expand Down
2 changes: 1 addition & 1 deletion fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -3936,7 +3936,7 @@ static int ext4_do_update_inode(handle_t *handle,
EXT4_SET_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
ext4_handle_sync(handle);
err = ext4_handle_dirty_super(handle, sb);
err = ext4_handle_dirty_super_now(handle, sb);
}
}
raw_inode->i_generation = cpu_to_le32(inode->i_generation);
Expand Down
4 changes: 2 additions & 2 deletions fs/ext4/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -2021,7 +2021,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
/* Insert this inode at the head of the on-disk orphan list... */
NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan);
EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
err = ext4_handle_dirty_super_now(handle, sb);
rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
if (!err)
err = rc;
Expand Down Expand Up @@ -2094,7 +2094,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
if (err)
goto out_brelse;
sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
err = ext4_handle_dirty_super_now(handle, inode->i_sb);
} else {
struct ext4_iloc iloc2;
struct inode *i_prev =
Expand Down
4 changes: 3 additions & 1 deletion fs/ext4/resize.c
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
ext4_kvfree(o_group_desc);

le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
err = ext4_handle_dirty_super_now(handle, sb);
if (err)
ext4_std_error(sb, err);

Expand Down Expand Up @@ -968,6 +968,8 @@ static void update_backups(struct super_block *sb,
goto exit_err;
}

ext4_superblock_csum_set(sb, (struct ext4_super_block *)data);

while ((group = ext4_list_backups(sb, &three, &five, &seven)) < last) {
struct buffer_head *bh;

Expand Down
47 changes: 47 additions & 0 deletions fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit a9c4731

Please sign in to comment.