aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/inode.c
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/ext4/inode.c
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/ext4/inode.c')
-rw-r--r--fs/ext4/inode.c47
1 files changed, 38 insertions, 9 deletions
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