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 | ||
