diff options
Diffstat (limited to 'fs/ext4/indirect.c')
-rw-r--r-- | fs/ext4/indirect.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 8a57e9fcd1b9..fd69da194826 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c | |||
@@ -389,7 +389,13 @@ static int ext4_alloc_branch(handle_t *handle, struct inode *inode, | |||
389 | return 0; | 389 | return 0; |
390 | failed: | 390 | failed: |
391 | for (; i >= 0; i--) { | 391 | for (; i >= 0; i--) { |
392 | if (i != indirect_blks && branch[i].bh) | 392 | /* |
393 | * We want to ext4_forget() only freshly allocated indirect | ||
394 | * blocks. Buffer for new_blocks[i-1] is at branch[i].bh and | ||
395 | * buffer at branch[0].bh is indirect block / inode already | ||
396 | * existing before ext4_alloc_branch() was called. | ||
397 | */ | ||
398 | if (i > 0 && i != indirect_blks && branch[i].bh) | ||
393 | ext4_forget(handle, 1, inode, branch[i].bh, | 399 | ext4_forget(handle, 1, inode, branch[i].bh, |
394 | branch[i].bh->b_blocknr); | 400 | branch[i].bh->b_blocknr); |
395 | ext4_free_blocks(handle, inode, NULL, new_blocks[i], | 401 | ext4_free_blocks(handle, inode, NULL, new_blocks[i], |
@@ -1310,16 +1316,24 @@ static int free_hole_blocks(handle_t *handle, struct inode *inode, | |||
1310 | blk = *i_data; | 1316 | blk = *i_data; |
1311 | if (level > 0) { | 1317 | if (level > 0) { |
1312 | ext4_lblk_t first2; | 1318 | ext4_lblk_t first2; |
1319 | ext4_lblk_t count2; | ||
1320 | |||
1313 | bh = sb_bread(inode->i_sb, le32_to_cpu(blk)); | 1321 | bh = sb_bread(inode->i_sb, le32_to_cpu(blk)); |
1314 | if (!bh) { | 1322 | if (!bh) { |
1315 | EXT4_ERROR_INODE_BLOCK(inode, le32_to_cpu(blk), | 1323 | EXT4_ERROR_INODE_BLOCK(inode, le32_to_cpu(blk), |
1316 | "Read failure"); | 1324 | "Read failure"); |
1317 | return -EIO; | 1325 | return -EIO; |
1318 | } | 1326 | } |
1319 | first2 = (first > offset) ? first - offset : 0; | 1327 | if (first > offset) { |
1328 | first2 = first - offset; | ||
1329 | count2 = count; | ||
1330 | } else { | ||
1331 | first2 = 0; | ||
1332 | count2 = count - (offset - first); | ||
1333 | } | ||
1320 | ret = free_hole_blocks(handle, inode, bh, | 1334 | ret = free_hole_blocks(handle, inode, bh, |
1321 | (__le32 *)bh->b_data, level - 1, | 1335 | (__le32 *)bh->b_data, level - 1, |
1322 | first2, count - offset, | 1336 | first2, count2, |
1323 | inode->i_sb->s_blocksize >> 2); | 1337 | inode->i_sb->s_blocksize >> 2); |
1324 | if (ret) { | 1338 | if (ret) { |
1325 | brelse(bh); | 1339 | brelse(bh); |
@@ -1329,8 +1343,8 @@ static int free_hole_blocks(handle_t *handle, struct inode *inode, | |||
1329 | if (level == 0 || | 1343 | if (level == 0 || |
1330 | (bh && all_zeroes((__le32 *)bh->b_data, | 1344 | (bh && all_zeroes((__le32 *)bh->b_data, |
1331 | (__le32 *)bh->b_data + addr_per_block))) { | 1345 | (__le32 *)bh->b_data + addr_per_block))) { |
1332 | ext4_free_data(handle, inode, parent_bh, &blk, &blk+1); | 1346 | ext4_free_data(handle, inode, parent_bh, |
1333 | *i_data = 0; | 1347 | i_data, i_data + 1); |
1334 | } | 1348 | } |
1335 | brelse(bh); | 1349 | brelse(bh); |
1336 | bh = NULL; | 1350 | bh = NULL; |