aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@us.ibm.com>2012-04-29 18:29:10 -0400
committerTheodore Ts'o <tytso@mit.edu>2012-04-29 18:29:10 -0400
commita9c4731780544d52b243bf46e4dd635c67fa9f84 (patch)
treedc735f2f59fa2107e8fed8e3d26f259557e34008
parent0441984a3398970ab4820410b9cf4ff85bf3a6b0 (diff)
ext4: calculate and verify superblock checksum
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>
-rw-r--r--fs/ext4/ext4.h10
-rw-r--r--fs/ext4/ext4_jbd2.c9
-rw-r--r--fs/ext4/ext4_jbd2.h7
-rw-r--r--fs/ext4/inode.c2
-rw-r--r--fs/ext4/namei.c4
-rw-r--r--fs/ext4/resize.c4
-rw-r--r--fs/ext4/super.c47
7 files changed, 76 insertions, 7 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f19192bde094..5adbce519b66 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1280,6 +1280,9 @@ struct ext4_sb_info {
1280 1280
1281 /* Reference to checksum algorithm driver via cryptoapi */ 1281 /* Reference to checksum algorithm driver via cryptoapi */
1282 struct crypto_shash *s_chksum_driver; 1282 struct crypto_shash *s_chksum_driver;
1283
1284 /* Precomputed FS UUID checksum for seeding other checksums */
1285 __u32 s_csum_seed;
1283}; 1286};
1284 1287
1285static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) 1288static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@ -2004,6 +2007,10 @@ extern int ext4_group_extend(struct super_block *sb,
2004extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count); 2007extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
2005 2008
2006/* super.c */ 2009/* super.c */
2010extern int ext4_superblock_csum_verify(struct super_block *sb,
2011 struct ext4_super_block *es);
2012extern void ext4_superblock_csum_set(struct super_block *sb,
2013 struct ext4_super_block *es);
2007extern void *ext4_kvmalloc(size_t size, gfp_t flags); 2014extern void *ext4_kvmalloc(size_t size, gfp_t flags);
2008extern void *ext4_kvzalloc(size_t size, gfp_t flags); 2015extern void *ext4_kvzalloc(size_t size, gfp_t flags);
2009extern void ext4_kvfree(void *ptr); 2016extern void ext4_kvfree(void *ptr);
@@ -2279,6 +2286,9 @@ static inline void ext4_unlock_group(struct super_block *sb,
2279 2286
2280static inline void ext4_mark_super_dirty(struct super_block *sb) 2287static inline void ext4_mark_super_dirty(struct super_block *sb)
2281{ 2288{
2289 struct ext4_super_block *es = EXT4_SB(sb)->s_es;
2290
2291 ext4_superblock_csum_set(sb, es);
2282 if (EXT4_SB(sb)->s_journal == NULL) 2292 if (EXT4_SB(sb)->s_journal == NULL)
2283 sb->s_dirt =1; 2293 sb->s_dirt =1;
2284} 2294}
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index aca179017582..90f7c2e84db1 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -138,16 +138,23 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
138} 138}
139 139
140int __ext4_handle_dirty_super(const char *where, unsigned int line, 140int __ext4_handle_dirty_super(const char *where, unsigned int line,
141 handle_t *handle, struct super_block *sb) 141 handle_t *handle, struct super_block *sb,
142 int now)
142{ 143{
143 struct buffer_head *bh = EXT4_SB(sb)->s_sbh; 144 struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
144 int err = 0; 145 int err = 0;
145 146
146 if (ext4_handle_valid(handle)) { 147 if (ext4_handle_valid(handle)) {
148 ext4_superblock_csum_set(sb,
149 (struct ext4_super_block *)bh->b_data);
147 err = jbd2_journal_dirty_metadata(handle, bh); 150 err = jbd2_journal_dirty_metadata(handle, bh);
148 if (err) 151 if (err)
149 ext4_journal_abort_handle(where, line, __func__, 152 ext4_journal_abort_handle(where, line, __func__,
150 bh, handle, err); 153 bh, handle, err);
154 } else if (now) {
155 ext4_superblock_csum_set(sb,
156 (struct ext4_super_block *)bh->b_data);
157 mark_buffer_dirty(bh);
151 } else 158 } else
152 sb->s_dirt = 1; 159 sb->s_dirt = 1;
153 return err; 160 return err;
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index 83b20fcf9400..f440e8f1841f 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -213,7 +213,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
213 struct buffer_head *bh); 213 struct buffer_head *bh);
214 214
215int __ext4_handle_dirty_super(const char *where, unsigned int line, 215int __ext4_handle_dirty_super(const char *where, unsigned int line,
216 handle_t *handle, struct super_block *sb); 216 handle_t *handle, struct super_block *sb,
217 int now);
217 218
218#define ext4_journal_get_write_access(handle, bh) \ 219#define ext4_journal_get_write_access(handle, bh) \
219 __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh)) 220 __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
@@ -225,8 +226,10 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
225#define ext4_handle_dirty_metadata(handle, inode, bh) \ 226#define ext4_handle_dirty_metadata(handle, inode, bh) \
226 __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \ 227 __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
227 (bh)) 228 (bh))
229#define ext4_handle_dirty_super_now(handle, sb) \
230 __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 1)
228#define ext4_handle_dirty_super(handle, sb) \ 231#define ext4_handle_dirty_super(handle, sb) \
229 __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb)) 232 __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 0)
230 233
231handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); 234handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
232int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle); 235int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index c77b0bd2c711..8bc21ecc1df5 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3936,7 +3936,7 @@ static int ext4_do_update_inode(handle_t *handle,
3936 EXT4_SET_RO_COMPAT_FEATURE(sb, 3936 EXT4_SET_RO_COMPAT_FEATURE(sb,
3937 EXT4_FEATURE_RO_COMPAT_LARGE_FILE); 3937 EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
3938 ext4_handle_sync(handle); 3938 ext4_handle_sync(handle);
3939 err = ext4_handle_dirty_super(handle, sb); 3939 err = ext4_handle_dirty_super_now(handle, sb);
3940 } 3940 }
3941 } 3941 }
3942 raw_inode->i_generation = cpu_to_le32(inode->i_generation); 3942 raw_inode->i_generation = cpu_to_le32(inode->i_generation);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index b58bd5c8ffe7..625125172d05 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2021,7 +2021,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
2021 /* Insert this inode at the head of the on-disk orphan list... */ 2021 /* Insert this inode at the head of the on-disk orphan list... */
2022 NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan); 2022 NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan);
2023 EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino); 2023 EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
2024 err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); 2024 err = ext4_handle_dirty_super_now(handle, sb);
2025 rc = ext4_mark_iloc_dirty(handle, inode, &iloc); 2025 rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
2026 if (!err) 2026 if (!err)
2027 err = rc; 2027 err = rc;
@@ -2094,7 +2094,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
2094 if (err) 2094 if (err)
2095 goto out_brelse; 2095 goto out_brelse;
2096 sbi->s_es->s_last_orphan = cpu_to_le32(ino_next); 2096 sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
2097 err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh); 2097 err = ext4_handle_dirty_super_now(handle, inode->i_sb);
2098 } else { 2098 } else {
2099 struct ext4_iloc iloc2; 2099 struct ext4_iloc iloc2;
2100 struct inode *i_prev = 2100 struct inode *i_prev =
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 59fa0be27251..e0374757a94b 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -796,7 +796,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
796 ext4_kvfree(o_group_desc); 796 ext4_kvfree(o_group_desc);
797 797
798 le16_add_cpu(&es->s_reserved_gdt_blocks, -1); 798 le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
799 err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); 799 err = ext4_handle_dirty_super_now(handle, sb);
800 if (err) 800 if (err)
801 ext4_std_error(sb, err); 801 ext4_std_error(sb, err);
802 802
@@ -968,6 +968,8 @@ static void update_backups(struct super_block *sb,
968 goto exit_err; 968 goto exit_err;
969 } 969 }
970 970
971 ext4_superblock_csum_set(sb, (struct ext4_super_block *)data);
972
971 while ((group = ext4_list_backups(sb, &three, &five, &seven)) < last) { 973 while ((group = ext4_list_backups(sb, &three, &five, &seven)) < last) {
972 struct buffer_head *bh; 974 struct buffer_head *bh;
973 975
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 11a0a7078da7..f80c7e612829 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -122,6 +122,38 @@ static int ext4_verify_csum_type(struct super_block *sb,
122 return es->s_checksum_type == EXT4_CRC32C_CHKSUM; 122 return es->s_checksum_type == EXT4_CRC32C_CHKSUM;
123} 123}
124 124
125static __le32 ext4_superblock_csum(struct super_block *sb,
126 struct ext4_super_block *es)
127{
128 struct ext4_sb_info *sbi = EXT4_SB(sb);
129 int offset = offsetof(struct ext4_super_block, s_checksum);
130 __u32 csum;
131
132 csum = ext4_chksum(sbi, ~0, (char *)es, offset);
133
134 return cpu_to_le32(csum);
135}
136
137int ext4_superblock_csum_verify(struct super_block *sb,
138 struct ext4_super_block *es)
139{
140 if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
141 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
142 return 1;
143
144 return es->s_checksum == ext4_superblock_csum(sb, es);
145}
146
147void ext4_superblock_csum_set(struct super_block *sb,
148 struct ext4_super_block *es)
149{
150 if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
151 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
152 return;
153
154 es->s_checksum = ext4_superblock_csum(sb, es);
155}
156
125void *ext4_kvmalloc(size_t size, gfp_t flags) 157void *ext4_kvmalloc(size_t size, gfp_t flags)
126{ 158{
127 void *ret; 159 void *ret;
@@ -3057,6 +3089,20 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
3057 } 3089 }
3058 } 3090 }
3059 3091
3092 /* Check superblock checksum */
3093 if (!ext4_superblock_csum_verify(sb, es)) {
3094 ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with "
3095 "invalid superblock checksum. Run e2fsck?");
3096 silent = 1;
3097 goto cantfind_ext4;
3098 }
3099
3100 /* Precompute checksum seed for all metadata */
3101 if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
3102 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
3103 sbi->s_csum_seed = ext4_chksum(sbi, ~0, es->s_uuid,
3104 sizeof(es->s_uuid));
3105
3060 /* Set defaults before we parse the mount options */ 3106 /* Set defaults before we parse the mount options */
3061 def_mount_opts = le32_to_cpu(es->s_default_mount_opts); 3107 def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
3062 set_opt(sb, INIT_INODE_TABLE); 3108 set_opt(sb, INIT_INODE_TABLE);
@@ -4059,6 +4105,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)
4059 &EXT4_SB(sb)->s_freeinodes_counter)); 4105 &EXT4_SB(sb)->s_freeinodes_counter));
4060 sb->s_dirt = 0; 4106 sb->s_dirt = 0;
4061 BUFFER_TRACE(sbh, "marking dirty"); 4107 BUFFER_TRACE(sbh, "marking dirty");
4108 ext4_superblock_csum_set(sb, es);
4062 mark_buffer_dirty(sbh); 4109 mark_buffer_dirty(sbh);
4063 if (sync) { 4110 if (sync) {
4064 error = sync_dirty_buffer(sbh); 4111 error = sync_dirty_buffer(sbh);