aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAllison Henderson <achender@linux.vnet.ibm.com>2011-09-06 21:49:44 -0400
committerTheodore Ts'o <tytso@mit.edu>2011-09-06 21:49:44 -0400
commit189e868fa8fdca702eb9db9d8afc46b5cb9144c9 (patch)
tree5b226cfe59cc9351b9d870a5c31cdfe4f5544652 /fs
parentdecbd919f4bb9cb698486880c026c4104b13d3c3 (diff)
ext4: fix fsx truncate failure
While running extended fsx tests to verify the first two patches, a similar bug was also found in the truncate operation. This bug happens because the truncate routine only zeros the unblock aligned portion of the last page. This means that the block aligned portions of the page appearing after i_size are left unzeroed, and the buffer heads still mapped. This bug is corrected by using ext4_discard_partial_page_buffers in the truncate routine to zero the partial page and unmap the buffer headers. Signed-off-by: Allison Henderson <achender@linux.vnet.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/extents.c13
-rw-r--r--fs/ext4/indirect.c13
2 files changed, 22 insertions, 4 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 2c5216a8d03b..ba7bd5a176ce 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3653,6 +3653,7 @@ void ext4_ext_truncate(struct inode *inode)
3653 struct super_block *sb = inode->i_sb; 3653 struct super_block *sb = inode->i_sb;
3654 ext4_lblk_t last_block; 3654 ext4_lblk_t last_block;
3655 handle_t *handle; 3655 handle_t *handle;
3656 loff_t page_len;
3656 int err = 0; 3657 int err = 0;
3657 3658
3658 /* 3659 /*
@@ -3669,8 +3670,16 @@ void ext4_ext_truncate(struct inode *inode)
3669 if (IS_ERR(handle)) 3670 if (IS_ERR(handle))
3670 return; 3671 return;
3671 3672
3672 if (inode->i_size & (sb->s_blocksize - 1)) 3673 if (inode->i_size % PAGE_CACHE_SIZE != 0) {
3673 ext4_block_truncate_page(handle, mapping, inode->i_size); 3674 page_len = PAGE_CACHE_SIZE -
3675 (inode->i_size & (PAGE_CACHE_SIZE - 1));
3676
3677 err = ext4_discard_partial_page_buffers(handle,
3678 mapping, inode->i_size, page_len, 0);
3679
3680 if (err)
3681 goto out_stop;
3682 }
3674 3683
3675 if (ext4_orphan_add(handle, inode)) 3684 if (ext4_orphan_add(handle, inode))
3676 goto out_stop; 3685 goto out_stop;
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 0962642119c0..ea704dff8669 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -1343,7 +1343,9 @@ void ext4_ind_truncate(struct inode *inode)
1343 __le32 nr = 0; 1343 __le32 nr = 0;
1344 int n = 0; 1344 int n = 0;
1345 ext4_lblk_t last_block, max_block; 1345 ext4_lblk_t last_block, max_block;
1346 loff_t page_len;
1346 unsigned blocksize = inode->i_sb->s_blocksize; 1347 unsigned blocksize = inode->i_sb->s_blocksize;
1348 int err;
1347 1349
1348 handle = start_transaction(inode); 1350 handle = start_transaction(inode);
1349 if (IS_ERR(handle)) 1351 if (IS_ERR(handle))
@@ -1354,9 +1356,16 @@ void ext4_ind_truncate(struct inode *inode)
1354 max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1) 1356 max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1)
1355 >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); 1357 >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
1356 1358
1357 if (inode->i_size & (blocksize - 1)) 1359 if (inode->i_size % PAGE_CACHE_SIZE != 0) {
1358 if (ext4_block_truncate_page(handle, mapping, inode->i_size)) 1360 page_len = PAGE_CACHE_SIZE -
1361 (inode->i_size & (PAGE_CACHE_SIZE - 1));
1362
1363 err = ext4_discard_partial_page_buffers(handle,
1364 mapping, inode->i_size, page_len, 0);
1365
1366 if (err)
1359 goto out_stop; 1367 goto out_stop;
1368 }
1360 1369
1361 if (last_block != max_block) { 1370 if (last_block != max_block) {
1362 n = ext4_block_to_path(inode, last_block, offsets, NULL); 1371 n = ext4_block_to_path(inode, last_block, offsets, NULL);