diff options
Diffstat (limited to 'fs/ext4/inode.c')
-rw-r--r-- | fs/ext4/inode.c | 47 |
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 | ||
374 | static int __ext4_check_blockref(const char *function, struct inode *inode, | 374 | static 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 | ||
1129 | static 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 | ||