aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2009-05-17 15:38:01 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-05-17 15:38:01 -0400
commit6fd058f7791087648c683eb8572edf3be3c4c23c (patch)
tree0d80791532d2d022c91f20013003716eaf0afb40 /fs
parent2ac3b6e00acb46406c993d57921f86a594aafe08 (diff)
ext4: Add a comprehensive block validity check to ext4_get_blocks()
To catch filesystem bugs or corruption which could lead to the filesystem getting severly damaged, this patch adds a facility for tracking all of the filesystem metadata blocks by contiguous regions in a red-black tree. This allows quick searching of the tree to locate extents which might overlap with filesystem metadata blocks. This facility is also used by the multi-block allocator to assure that it is not allocating blocks out of the system zone, as well as by the routines used when reading indirect blocks and extents information from disk to make sure their contents are valid. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/Makefile4
-rw-r--r--fs/ext4/block_validity.c244
-rw-r--r--fs/ext4/ext4.h11
-rw-r--r--fs/ext4/extents.c22
-rw-r--r--fs/ext4/inode.c47
-rw-r--r--fs/ext4/mballoc.c11
-rw-r--r--fs/ext4/super.c32
7 files changed, 332 insertions, 39 deletions
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index a8ff003a00f7..8a34710ecf40 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -5,8 +5,8 @@
5obj-$(CONFIG_EXT4_FS) += ext4.o 5obj-$(CONFIG_EXT4_FS) += ext4.o
6 6
7ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ 7ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
8 ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \ 8 ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
9 ext4_jbd2.o migrate.o mballoc.o 9 ext4_jbd2.o migrate.o mballoc.o block_validity.o
10 10
11ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o 11ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
12ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o 12ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
new file mode 100644
index 000000000000..50784ef07563
--- /dev/null
+++ b/fs/ext4/block_validity.c
@@ -0,0 +1,244 @@
1/*
2 * linux/fs/ext4/block_validity.c
3 *
4 * Copyright (C) 2009
5 * Theodore Ts'o (tytso@mit.edu)
6 *
7 * Track which blocks in the filesystem are metadata blocks that
8 * should never be used as data blocks by files or directories.
9 */
10
11#include <linux/time.h>
12#include <linux/fs.h>
13#include <linux/namei.h>
14#include <linux/quotaops.h>
15#include <linux/buffer_head.h>
16#include <linux/module.h>
17#include <linux/swap.h>
18#include <linux/pagemap.h>
19#include <linux/version.h>
20#include <linux/blkdev.h>
21#include <linux/mutex.h>
22#include "ext4.h"
23
24struct ext4_system_zone {
25 struct rb_node node;
26 ext4_fsblk_t start_blk;
27 unsigned int count;
28};
29
30static struct kmem_cache *ext4_system_zone_cachep;
31
32int __init init_ext4_system_zone(void)
33{
34 ext4_system_zone_cachep = KMEM_CACHE(ext4_system_zone,
35 SLAB_RECLAIM_ACCOUNT);
36 if (ext4_system_zone_cachep == NULL)
37 return -ENOMEM;
38 return 0;
39}
40
41void exit_ext4_system_zone(void)
42{
43 kmem_cache_destroy(ext4_system_zone_cachep);
44}
45
46static inline int can_merge(struct ext4_system_zone *entry1,
47 struct ext4_system_zone *entry2)
48{
49 if ((entry1->start_blk + entry1->count) == entry2->start_blk)
50 return 1;
51 return 0;
52}
53
54/*
55 * Mark a range of blocks as belonging to the "system zone" --- that
56 * is, filesystem metadata blocks which should never be used by
57 * inodes.
58 */
59static int add_system_zone(struct ext4_sb_info *sbi,
60 ext4_fsblk_t start_blk,
61 unsigned int count)
62{
63 struct ext4_system_zone *new_entry = NULL, *entry;
64 struct rb_node **n = &sbi->system_blks.rb_node, *node;
65 struct rb_node *parent = NULL, *new_node = NULL;
66
67 while (*n) {
68 parent = *n;
69 entry = rb_entry(parent, struct ext4_system_zone, node);
70 if (start_blk < entry->start_blk)
71 n = &(*n)->rb_left;
72 else if (start_blk >= (entry->start_blk + entry->count))
73 n = &(*n)->rb_right;
74 else {
75 if (start_blk + count > (entry->start_blk +
76 entry->count))
77 entry->count = (start_blk + count -
78 entry->start_blk);
79 new_node = *n;
80 new_entry = rb_entry(new_node, struct ext4_system_zone,
81 node);
82 break;
83 }
84 }
85
86 if (!new_entry) {
87 new_entry = kmem_cache_alloc(ext4_system_zone_cachep,
88 GFP_KERNEL);
89 if (!new_entry)
90 return -ENOMEM;
91 new_entry->start_blk = start_blk;
92 new_entry->count = count;
93 new_node = &new_entry->node;
94
95 rb_link_node(new_node, parent, n);
96 rb_insert_color(new_node, &sbi->system_blks);
97 }
98
99 /* Can we merge to the left? */
100 node = rb_prev(new_node);
101 if (node) {
102 entry = rb_entry(node, struct ext4_system_zone, node);
103 if (can_merge(entry, new_entry)) {
104 new_entry->start_blk = entry->start_blk;
105 new_entry->count += entry->count;
106 rb_erase(node, &sbi->system_blks);
107 kmem_cache_free(ext4_system_zone_cachep, entry);
108 }
109 }
110
111 /* Can we merge to the right? */
112 node = rb_next(new_node);
113 if (node) {
114 entry = rb_entry(node, struct ext4_system_zone, node);
115 if (can_merge(new_entry, entry)) {
116 new_entry->count += entry->count;
117 rb_erase(node, &sbi->system_blks);
118 kmem_cache_free(ext4_system_zone_cachep, entry);
119 }
120 }
121 return 0;
122}
123
124static void debug_print_tree(struct ext4_sb_info *sbi)
125{
126 struct rb_node *node;
127 struct ext4_system_zone *entry;
128 int first = 1;
129
130 printk(KERN_INFO "System zones: ");
131 node = rb_first(&sbi->system_blks);
132 while (node) {
133 entry = rb_entry(node, struct ext4_system_zone, node);
134 printk("%s%llu-%llu", first ? "" : ", ",
135 entry->start_blk, entry->start_blk + entry->count - 1);
136 first = 0;
137 node = rb_next(node);
138 }
139 printk("\n");
140}
141
142int ext4_setup_system_zone(struct super_block *sb)
143{
144 ext4_group_t ngroups = ext4_get_groups_count(sb);
145 struct ext4_sb_info *sbi = EXT4_SB(sb);
146 struct ext4_group_desc *gdp;
147 ext4_group_t i;
148 int flex_size = ext4_flex_bg_size(sbi);
149 int ret;
150
151 if (!test_opt(sb, BLOCK_VALIDITY)) {
152 if (EXT4_SB(sb)->system_blks.rb_node)
153 ext4_release_system_zone(sb);
154 return 0;
155 }
156 if (EXT4_SB(sb)->system_blks.rb_node)
157 return 0;
158
159 for (i=0; i < ngroups; i++) {
160 if (ext4_bg_has_super(sb, i) &&
161 ((i < 5) || ((i % flex_size) == 0)))
162 add_system_zone(sbi, ext4_group_first_block_no(sb, i),
163 sbi->s_gdb_count + 1);
164 gdp = ext4_get_group_desc(sb, i, NULL);
165 ret = add_system_zone(sbi, ext4_block_bitmap(sb, gdp), 1);
166 if (ret)
167 return ret;
168 ret = add_system_zone(sbi, ext4_inode_bitmap(sb, gdp), 1);
169 if (ret)
170 return ret;
171 ret = add_system_zone(sbi, ext4_inode_table(sb, gdp),
172 sbi->s_itb_per_group);
173 if (ret)
174 return ret;
175 }
176
177 if (test_opt(sb, DEBUG))
178 debug_print_tree(EXT4_SB(sb));
179 return 0;
180}
181
182/* Called when the filesystem is unmounted */
183void ext4_release_system_zone(struct super_block *sb)
184{
185 struct rb_node *n = EXT4_SB(sb)->system_blks.rb_node;
186 struct rb_node *parent;
187 struct ext4_system_zone *entry;
188
189 while (n) {
190 /* Do the node's children first */
191 if (n->rb_left) {
192 n = n->rb_left;
193 continue;
194 }
195 if (n->rb_right) {
196 n = n->rb_right;
197 continue;
198 }
199 /*
200 * The node has no children; free it, and then zero
201 * out parent's link to it. Finally go to the
202 * beginning of the loop and try to free the parent
203 * node.
204 */
205 parent = rb_parent(n);
206 entry = rb_entry(n, struct ext4_system_zone, node);
207 kmem_cache_free(ext4_system_zone_cachep, entry);
208 if (!parent)
209 EXT4_SB(sb)->system_blks.rb_node = NULL;
210 else if (parent->rb_left == n)
211 parent->rb_left = NULL;
212 else if (parent->rb_right == n)
213 parent->rb_right = NULL;
214 n = parent;
215 }
216 EXT4_SB(sb)->system_blks.rb_node = NULL;
217}
218
219/*
220 * Returns 1 if the passed-in block region (start_blk,
221 * start_blk+count) is valid; 0 if some part of the block region
222 * overlaps with filesystem metadata blocks.
223 */
224int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
225 unsigned int count)
226{
227 struct ext4_system_zone *entry;
228 struct rb_node *n = sbi->system_blks.rb_node;
229
230 if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
231 (start_blk + count > ext4_blocks_count(sbi->s_es)))
232 return 0;
233 while (n) {
234 entry = rb_entry(n, struct ext4_system_zone, node);
235 if (start_blk + count - 1 < entry->start_blk)
236 n = n->rb_left;
237 else if (start_blk >= (entry->start_blk + entry->count))
238 n = n->rb_right;
239 else
240 return 0;
241 }
242 return 1;
243}
244
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index d164f1294e5f..4311cc85b534 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -696,6 +696,7 @@ struct ext4_inode_info {
696#define EXT4_MOUNT_I_VERSION 0x2000000 /* i_version support */ 696#define EXT4_MOUNT_I_VERSION 0x2000000 /* i_version support */
697#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */ 697#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
698#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ 698#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
699#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
699 700
700/* Compatibility, for having both ext2_fs.h and ext4_fs.h included at once */ 701/* Compatibility, for having both ext2_fs.h and ext4_fs.h included at once */
701#ifndef _LINUX_EXT2_FS_H 702#ifndef _LINUX_EXT2_FS_H
@@ -887,6 +888,7 @@ struct ext4_sb_info {
887 int s_jquota_fmt; /* Format of quota to use */ 888 int s_jquota_fmt; /* Format of quota to use */
888#endif 889#endif
889 unsigned int s_want_extra_isize; /* New inodes should reserve # bytes */ 890 unsigned int s_want_extra_isize; /* New inodes should reserve # bytes */
891 struct rb_root system_blks;
890 892
891#ifdef EXTENTS_STATS 893#ifdef EXTENTS_STATS
892 /* ext4 extents stats */ 894 /* ext4 extents stats */
@@ -1618,6 +1620,15 @@ extern struct dentry *ext4_get_parent(struct dentry *child);
1618extern const struct inode_operations ext4_symlink_inode_operations; 1620extern const struct inode_operations ext4_symlink_inode_operations;
1619extern const struct inode_operations ext4_fast_symlink_inode_operations; 1621extern const struct inode_operations ext4_fast_symlink_inode_operations;
1620 1622
1623/* block_validity */
1624extern void ext4_release_system_zone(struct super_block *sb);
1625extern int ext4_setup_system_zone(struct super_block *sb);
1626extern int __init init_ext4_system_zone(void);
1627extern void exit_ext4_system_zone(void);
1628extern int ext4_data_block_valid(struct ext4_sb_info *sbi,
1629 ext4_fsblk_t start_blk,
1630 unsigned int count);
1631
1621/* extents.c */ 1632/* extents.c */
1622extern int ext4_ext_tree_init(handle_t *handle, struct inode *); 1633extern int ext4_ext_tree_init(handle_t *handle, struct inode *);
1623extern int ext4_ext_writepage_trans_blocks(struct inode *, int); 1634extern int ext4_ext_writepage_trans_blocks(struct inode *, int);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 27c383c7b43c..d04b779b780e 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -326,32 +326,18 @@ ext4_ext_max_entries(struct inode *inode, int depth)
326 326
327static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext) 327static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
328{ 328{
329 ext4_fsblk_t block = ext_pblock(ext), valid_block; 329 ext4_fsblk_t block = ext_pblock(ext);
330 int len = ext4_ext_get_actual_len(ext); 330 int len = ext4_ext_get_actual_len(ext);
331 struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
332 331
333 valid_block = le32_to_cpu(es->s_first_data_block) + 332 return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
334 EXT4_SB(inode->i_sb)->s_gdb_count;
335 if (unlikely(block <= valid_block ||
336 ((block + len) > ext4_blocks_count(es))))
337 return 0;
338 else
339 return 1;
340} 333}
341 334
342static int ext4_valid_extent_idx(struct inode *inode, 335static int ext4_valid_extent_idx(struct inode *inode,
343 struct ext4_extent_idx *ext_idx) 336 struct ext4_extent_idx *ext_idx)
344{ 337{
345 ext4_fsblk_t block = idx_pblock(ext_idx), valid_block; 338 ext4_fsblk_t block = idx_pblock(ext_idx);
346 struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
347 339
348 valid_block = le32_to_cpu(es->s_first_data_block) + 340 return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, 1);
349 EXT4_SB(inode->i_sb)->s_gdb_count;
350 if (unlikely(block <= valid_block ||
351 (block >= ext4_blocks_count(es))))
352 return 0;
353 else
354 return 1;
355} 341}
356 342
357static int ext4_valid_extent_entries(struct inode *inode, 343static int ext4_valid_extent_entries(struct inode *inode,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index d7b7480682b9..dadd3f995db5 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -372,20 +372,21 @@ static int ext4_block_to_path(struct inode *inode,
372} 372}
373 373
374static int __ext4_check_blockref(const char *function, struct inode *inode, 374static int __ext4_check_blockref(const char *function, struct inode *inode,
375 __le32 *p, unsigned int max) { 375 __le32 *p, unsigned int max)
376 376{
377 unsigned int maxblocks = ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es);
378 __le32 *bref = p; 377 __le32 *bref = p;
378 unsigned int blk;
379
379 while (bref < p+max) { 380 while (bref < p+max) {
380 if (unlikely(le32_to_cpu(*bref) >= maxblocks)) { 381 blk = le32_to_cpu(*bref++);
382 if (blk &&
383 unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
384 blk, 1))) {
381 ext4_error(inode->i_sb, function, 385 ext4_error(inode->i_sb, function,
382 "block reference %u >= max (%u) " 386 "invalid block reference %u "
383 "in inode #%lu, offset=%d", 387 "in inode #%lu", blk, inode->i_ino);
384 le32_to_cpu(*bref), maxblocks,
385 inode->i_ino, (int)(bref-p));
386 return -EIO; 388 return -EIO;
387 } 389 }
388 bref++;
389 } 390 }
390 return 0; 391 return 0;
391} 392}
@@ -1125,6 +1126,21 @@ static void ext4_da_update_reserve_space(struct inode *inode, int used)
1125 ext4_discard_preallocations(inode); 1126 ext4_discard_preallocations(inode);
1126} 1127}
1127 1128
1129static int check_block_validity(struct inode *inode, sector_t logical,
1130 sector_t phys, int len)
1131{
1132 if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), phys, len)) {
1133 ext4_error(inode->i_sb, "check_block_validity",
1134 "inode #%lu logical block %llu mapped to %llu "
1135 "(size %d)", inode->i_ino,
1136 (unsigned long long) logical,
1137 (unsigned long long) phys, len);
1138 WARN_ON(1);
1139 return -EIO;
1140 }
1141 return 0;
1142}
1143
1128/* 1144/*
1129 * The ext4_get_blocks() function tries to look up the requested blocks, 1145 * The ext4_get_blocks() function tries to look up the requested blocks,
1130 * and returns if the blocks are already mapped. 1146 * and returns if the blocks are already mapped.
@@ -1170,6 +1186,13 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
1170 } 1186 }
1171 up_read((&EXT4_I(inode)->i_data_sem)); 1187 up_read((&EXT4_I(inode)->i_data_sem));
1172 1188
1189 if (retval > 0 && buffer_mapped(bh)) {
1190 int ret = check_block_validity(inode, block,
1191 bh->b_blocknr, retval);
1192 if (ret != 0)
1193 return ret;
1194 }
1195
1173 /* If it is only a block(s) look up */ 1196 /* If it is only a block(s) look up */
1174 if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) 1197 if ((flags & EXT4_GET_BLOCKS_CREATE) == 0)
1175 return retval; 1198 return retval;
@@ -1245,6 +1268,12 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
1245 ext4_da_update_reserve_space(inode, retval); 1268 ext4_da_update_reserve_space(inode, retval);
1246 1269
1247 up_write((&EXT4_I(inode)->i_data_sem)); 1270 up_write((&EXT4_I(inode)->i_data_sem));
1271 if (retval > 0 && buffer_mapped(bh)) {
1272 int ret = check_block_validity(inode, block,
1273 bh->b_blocknr, retval);
1274 if (ret != 0)
1275 return ret;
1276 }
1248 return retval; 1277 return retval;
1249} 1278}
1250 1279
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 541bd9adffa2..ed8482e22c0e 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -2961,15 +2961,10 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
2961 + le32_to_cpu(es->s_first_data_block); 2961 + le32_to_cpu(es->s_first_data_block);
2962 2962
2963 len = ac->ac_b_ex.fe_len; 2963 len = ac->ac_b_ex.fe_len;
2964 if (in_range(ext4_block_bitmap(sb, gdp), block, len) || 2964 if (!ext4_data_block_valid(sbi, block, len)) {
2965 in_range(ext4_inode_bitmap(sb, gdp), block, len) ||
2966 in_range(block, ext4_inode_table(sb, gdp),
2967 EXT4_SB(sb)->s_itb_per_group) ||
2968 in_range(block + len - 1, ext4_inode_table(sb, gdp),
2969 EXT4_SB(sb)->s_itb_per_group)) {
2970 ext4_error(sb, __func__, 2965 ext4_error(sb, __func__,
2971 "Allocating block %llu in system zone of %d group\n", 2966 "Allocating blocks %llu-%llu which overlap "
2972 block, ac->ac_b_ex.fe_group); 2967 "fs metadata\n", block, block+len);
2973 /* File system mounted not to panic on error 2968 /* File system mounted not to panic on error
2974 * Fix the bitmap and repeat the block allocation 2969 * Fix the bitmap and repeat the block allocation
2975 * We leak some of the blocks here. 2970 * We leak some of the blocks here.
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index dc34ed3d1327..600b7ad699b5 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -568,6 +568,7 @@ static void ext4_put_super(struct super_block *sb)
568 struct ext4_super_block *es = sbi->s_es; 568 struct ext4_super_block *es = sbi->s_es;
569 int i, err; 569 int i, err;
570 570
571 ext4_release_system_zone(sb);
571 ext4_mb_release(sb); 572 ext4_mb_release(sb);
572 ext4_ext_release(sb); 573 ext4_ext_release(sb);
573 ext4_xattr_put_super(sb); 574 ext4_xattr_put_super(sb);
@@ -1055,6 +1056,7 @@ enum {
1055 Opt_ignore, Opt_barrier, Opt_nobarrier, Opt_err, Opt_resize, 1056 Opt_ignore, Opt_barrier, Opt_nobarrier, Opt_err, Opt_resize,
1056 Opt_usrquota, Opt_grpquota, Opt_i_version, 1057 Opt_usrquota, Opt_grpquota, Opt_i_version,
1057 Opt_stripe, Opt_delalloc, Opt_nodelalloc, 1058 Opt_stripe, Opt_delalloc, Opt_nodelalloc,
1059 Opt_block_validity, Opt_noblock_validity,
1058 Opt_inode_readahead_blks, Opt_journal_ioprio 1060 Opt_inode_readahead_blks, Opt_journal_ioprio
1059}; 1061};
1060 1062
@@ -1114,6 +1116,8 @@ static const match_table_t tokens = {
1114 {Opt_resize, "resize"}, 1116 {Opt_resize, "resize"},
1115 {Opt_delalloc, "delalloc"}, 1117 {Opt_delalloc, "delalloc"},
1116 {Opt_nodelalloc, "nodelalloc"}, 1118 {Opt_nodelalloc, "nodelalloc"},
1119 {Opt_block_validity, "block_validity"},
1120 {Opt_noblock_validity, "noblock_validity"},
1117 {Opt_inode_readahead_blks, "inode_readahead_blks=%u"}, 1121 {Opt_inode_readahead_blks, "inode_readahead_blks=%u"},
1118 {Opt_journal_ioprio, "journal_ioprio=%u"}, 1122 {Opt_journal_ioprio, "journal_ioprio=%u"},
1119 {Opt_auto_da_alloc, "auto_da_alloc=%u"}, 1123 {Opt_auto_da_alloc, "auto_da_alloc=%u"},
@@ -1508,6 +1512,12 @@ set_qf_format:
1508 case Opt_delalloc: 1512 case Opt_delalloc:
1509 set_opt(sbi->s_mount_opt, DELALLOC); 1513 set_opt(sbi->s_mount_opt, DELALLOC);
1510 break; 1514 break;
1515 case Opt_block_validity:
1516 set_opt(sbi->s_mount_opt, BLOCK_VALIDITY);
1517 break;
1518 case Opt_noblock_validity:
1519 clear_opt(sbi->s_mount_opt, BLOCK_VALIDITY);
1520 break;
1511 case Opt_inode_readahead_blks: 1521 case Opt_inode_readahead_blks:
1512 if (match_int(&args[0], &option)) 1522 if (match_int(&args[0], &option))
1513 return 0; 1523 return 0;
@@ -2826,6 +2836,13 @@ no_journal:
2826 } else if (test_opt(sb, DELALLOC)) 2836 } else if (test_opt(sb, DELALLOC))
2827 printk(KERN_INFO "EXT4-fs: delayed allocation enabled\n"); 2837 printk(KERN_INFO "EXT4-fs: delayed allocation enabled\n");
2828 2838
2839 err = ext4_setup_system_zone(sb);
2840 if (err) {
2841 printk(KERN_ERR "EXT4-fs: failed to initialize system "
2842 "zone (%d)\n", err);
2843 goto failed_mount4;
2844 }
2845
2829 ext4_ext_init(sb); 2846 ext4_ext_init(sb);
2830 err = ext4_mb_init(sb, needs_recovery); 2847 err = ext4_mb_init(sb, needs_recovery);
2831 if (err) { 2848 if (err) {
@@ -2875,6 +2892,7 @@ cantfind_ext4:
2875 2892
2876failed_mount4: 2893failed_mount4:
2877 printk(KERN_ERR "EXT4-fs (device %s): mount failed\n", sb->s_id); 2894 printk(KERN_ERR "EXT4-fs (device %s): mount failed\n", sb->s_id);
2895 ext4_release_system_zone(sb);
2878 if (sbi->s_journal) { 2896 if (sbi->s_journal) {
2879 jbd2_journal_destroy(sbi->s_journal); 2897 jbd2_journal_destroy(sbi->s_journal);
2880 sbi->s_journal = NULL; 2898 sbi->s_journal = NULL;
@@ -3515,6 +3533,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
3515 sb->s_flags &= ~MS_RDONLY; 3533 sb->s_flags &= ~MS_RDONLY;
3516 } 3534 }
3517 } 3535 }
3536 ext4_setup_system_zone(sb);
3518 if (sbi->s_journal == NULL) 3537 if (sbi->s_journal == NULL)
3519 ext4_commit_super(sb, 1); 3538 ext4_commit_super(sb, 1);
3520 3539
@@ -3927,13 +3946,16 @@ static int __init init_ext4_fs(void)
3927{ 3946{
3928 int err; 3947 int err;
3929 3948
3949 err = init_ext4_system_zone();
3950 if (err)
3951 return err;
3930 ext4_kset = kset_create_and_add("ext4", NULL, fs_kobj); 3952 ext4_kset = kset_create_and_add("ext4", NULL, fs_kobj);
3931 if (!ext4_kset) 3953 if (!ext4_kset)
3932 return -ENOMEM; 3954 goto out4;
3933 ext4_proc_root = proc_mkdir("fs/ext4", NULL); 3955 ext4_proc_root = proc_mkdir("fs/ext4", NULL);
3934 err = init_ext4_mballoc(); 3956 err = init_ext4_mballoc();
3935 if (err) 3957 if (err)
3936 return err; 3958 goto out3;
3937 3959
3938 err = init_ext4_xattr(); 3960 err = init_ext4_xattr();
3939 if (err) 3961 if (err)
@@ -3958,6 +3980,11 @@ out1:
3958 exit_ext4_xattr(); 3980 exit_ext4_xattr();
3959out2: 3981out2:
3960 exit_ext4_mballoc(); 3982 exit_ext4_mballoc();
3983out3:
3984 remove_proc_entry("fs/ext4", NULL);
3985 kset_unregister(ext4_kset);
3986out4:
3987 exit_ext4_system_zone();
3961 return err; 3988 return err;
3962} 3989}
3963 3990
@@ -3972,6 +3999,7 @@ static void __exit exit_ext4_fs(void)
3972 exit_ext4_mballoc(); 3999 exit_ext4_mballoc();
3973 remove_proc_entry("fs/ext4", NULL); 4000 remove_proc_entry("fs/ext4", NULL);
3974 kset_unregister(ext4_kset); 4001 kset_unregister(ext4_kset);
4002 exit_ext4_system_zone();
3975} 4003}
3976 4004
3977MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); 4005MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");