diff options
-rw-r--r-- | fs/ext4/ext4.h | 1 | ||||
-rw-r--r-- | fs/ext4/inode.c | 39 | ||||
-rw-r--r-- | fs/ext4/mballoc.c | 7 |
3 files changed, 35 insertions, 12 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 602d5ad6f5e7..307ecd13a762 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -377,6 +377,7 @@ struct ext4_new_group_data { | |||
377 | */ | 377 | */ |
378 | #define EXT4_FREE_BLOCKS_METADATA 0x0001 | 378 | #define EXT4_FREE_BLOCKS_METADATA 0x0001 |
379 | #define EXT4_FREE_BLOCKS_FORGET 0x0002 | 379 | #define EXT4_FREE_BLOCKS_FORGET 0x0002 |
380 | #define EXT4_FREE_BLOCKS_VALIDATED 0x0004 | ||
380 | 381 | ||
381 | /* | 382 | /* |
382 | * ioctl commands | 383 | * ioctl commands |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2059c34ac4c8..3e8afd969236 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4130,18 +4130,27 @@ no_top: | |||
4130 | * We release `count' blocks on disk, but (last - first) may be greater | 4130 | * We release `count' blocks on disk, but (last - first) may be greater |
4131 | * than `count' because there can be holes in there. | 4131 | * than `count' because there can be holes in there. |
4132 | */ | 4132 | */ |
4133 | static void ext4_clear_blocks(handle_t *handle, struct inode *inode, | 4133 | static int ext4_clear_blocks(handle_t *handle, struct inode *inode, |
4134 | struct buffer_head *bh, | 4134 | struct buffer_head *bh, |
4135 | ext4_fsblk_t block_to_free, | 4135 | ext4_fsblk_t block_to_free, |
4136 | unsigned long count, __le32 *first, | 4136 | unsigned long count, __le32 *first, |
4137 | __le32 *last) | 4137 | __le32 *last) |
4138 | { | 4138 | { |
4139 | __le32 *p; | 4139 | __le32 *p; |
4140 | int flags = EXT4_FREE_BLOCKS_FORGET; | 4140 | int flags = EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_VALIDATED; |
4141 | 4141 | ||
4142 | if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) | 4142 | if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) |
4143 | flags |= EXT4_FREE_BLOCKS_METADATA; | 4143 | flags |= EXT4_FREE_BLOCKS_METADATA; |
4144 | 4144 | ||
4145 | if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free, | ||
4146 | count)) { | ||
4147 | ext4_error(inode->i_sb, __func__, "inode #%lu: " | ||
4148 | "attempt to clear blocks %llu len %lu, invalid", | ||
4149 | inode->i_ino, (unsigned long long) block_to_free, | ||
4150 | count); | ||
4151 | return 1; | ||
4152 | } | ||
4153 | |||
4145 | if (try_to_extend_transaction(handle, inode)) { | 4154 | if (try_to_extend_transaction(handle, inode)) { |
4146 | if (bh) { | 4155 | if (bh) { |
4147 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); | 4156 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); |
@@ -4160,6 +4169,7 @@ static void ext4_clear_blocks(handle_t *handle, struct inode *inode, | |||
4160 | *p = 0; | 4169 | *p = 0; |
4161 | 4170 | ||
4162 | ext4_free_blocks(handle, inode, 0, block_to_free, count, flags); | 4171 | ext4_free_blocks(handle, inode, 0, block_to_free, count, flags); |
4172 | return 0; | ||
4163 | } | 4173 | } |
4164 | 4174 | ||
4165 | /** | 4175 | /** |
@@ -4215,9 +4225,10 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, | |||
4215 | } else if (nr == block_to_free + count) { | 4225 | } else if (nr == block_to_free + count) { |
4216 | count++; | 4226 | count++; |
4217 | } else { | 4227 | } else { |
4218 | ext4_clear_blocks(handle, inode, this_bh, | 4228 | if (ext4_clear_blocks(handle, inode, this_bh, |
4219 | block_to_free, | 4229 | block_to_free, count, |
4220 | count, block_to_free_p, p); | 4230 | block_to_free_p, p)) |
4231 | break; | ||
4221 | block_to_free = nr; | 4232 | block_to_free = nr; |
4222 | block_to_free_p = p; | 4233 | block_to_free_p = p; |
4223 | count = 1; | 4234 | count = 1; |
@@ -4281,6 +4292,16 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, | |||
4281 | if (!nr) | 4292 | if (!nr) |
4282 | continue; /* A hole */ | 4293 | continue; /* A hole */ |
4283 | 4294 | ||
4295 | if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), | ||
4296 | nr, 1)) { | ||
4297 | ext4_error(inode->i_sb, __func__, | ||
4298 | "indirect mapped block in inode " | ||
4299 | "#%lu invalid (level %d, blk #%lu)", | ||
4300 | inode->i_ino, depth, | ||
4301 | (unsigned long) nr); | ||
4302 | break; | ||
4303 | } | ||
4304 | |||
4284 | /* Go read the buffer for the next level down */ | 4305 | /* Go read the buffer for the next level down */ |
4285 | bh = sb_bread(inode->i_sb, nr); | 4306 | bh = sb_bread(inode->i_sb, nr); |
4286 | 4307 | ||
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index d34afad3e137..d129c1039f1d 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c | |||
@@ -4476,10 +4476,11 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, | |||
4476 | 4476 | ||
4477 | sbi = EXT4_SB(sb); | 4477 | sbi = EXT4_SB(sb); |
4478 | es = EXT4_SB(sb)->s_es; | 4478 | es = EXT4_SB(sb)->s_es; |
4479 | if (!ext4_data_block_valid(sbi, block, count)) { | 4479 | if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) && |
4480 | !ext4_data_block_valid(sbi, block, count)) { | ||
4480 | ext4_error(sb, __func__, | 4481 | ext4_error(sb, __func__, |
4481 | "Freeing blocks not in datazone - " | 4482 | "Freeing blocks not in datazone - " |
4482 | "block = %llu, count = %lu", block, count); | 4483 | "block = %llu, count = %lu", block, count); |
4483 | goto error_return; | 4484 | goto error_return; |
4484 | } | 4485 | } |
4485 | 4486 | ||