diff options
-rw-r--r-- | fs/ext4/ext4.h | 10 | ||||
-rw-r--r-- | fs/ext4/ext4_jbd2.c | 9 | ||||
-rw-r--r-- | fs/ext4/ext4_jbd2.h | 7 | ||||
-rw-r--r-- | fs/ext4/inode.c | 2 | ||||
-rw-r--r-- | fs/ext4/namei.c | 4 | ||||
-rw-r--r-- | fs/ext4/resize.c | 4 | ||||
-rw-r--r-- | fs/ext4/super.c | 47 |
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 | ||
1285 | static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) | 1288 | static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) |
@@ -2004,6 +2007,10 @@ extern int ext4_group_extend(struct super_block *sb, | |||
2004 | extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count); | 2007 | extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count); |
2005 | 2008 | ||
2006 | /* super.c */ | 2009 | /* super.c */ |
2010 | extern int ext4_superblock_csum_verify(struct super_block *sb, | ||
2011 | struct ext4_super_block *es); | ||
2012 | extern void ext4_superblock_csum_set(struct super_block *sb, | ||
2013 | struct ext4_super_block *es); | ||
2007 | extern void *ext4_kvmalloc(size_t size, gfp_t flags); | 2014 | extern void *ext4_kvmalloc(size_t size, gfp_t flags); |
2008 | extern void *ext4_kvzalloc(size_t size, gfp_t flags); | 2015 | extern void *ext4_kvzalloc(size_t size, gfp_t flags); |
2009 | extern void ext4_kvfree(void *ptr); | 2016 | extern void ext4_kvfree(void *ptr); |
@@ -2279,6 +2286,9 @@ static inline void ext4_unlock_group(struct super_block *sb, | |||
2279 | 2286 | ||
2280 | static inline void ext4_mark_super_dirty(struct super_block *sb) | 2287 | static 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 | ||
140 | int __ext4_handle_dirty_super(const char *where, unsigned int line, | 140 | int __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 | ||
215 | int __ext4_handle_dirty_super(const char *where, unsigned int line, | 215 | int __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 | ||
231 | handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); | 234 | handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); |
232 | int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle); | 235 | int __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 | ||
125 | static __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 | |||
137 | int 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 | |||
147 | void 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 | |||
125 | void *ext4_kvmalloc(size_t size, gfp_t flags) | 157 | void *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); |