summaryrefslogtreecommitdiffstats
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
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>
-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 2c5216a8d03..ba7bd5a176c 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3653,6 +3653,7 @@ void ext4_ext_truncate(struct inode *inode)
struct super_block *sb = inode->i_sb;
ext4_lblk_t last_block;
handle_t *handle;
+ loff_t page_len;
int err = 0;
/*
@@ -3669,8 +3670,16 @@ void ext4_ext_truncate(struct inode *inode)
if (IS_ERR(handle))
return;
- if (inode->i_size & (sb->s_blocksize - 1))
- ext4_block_truncate_page(handle, mapping, inode->i_size);
+ if (inode->i_size % PAGE_CACHE_SIZE != 0) {
+ page_len = PAGE_CACHE_SIZE -
+ (inode->i_size & (PAGE_CACHE_SIZE - 1));
+
+ err = ext4_discard_partial_page_buffers(handle,
+ mapping, inode->i_size, page_len, 0);
+
+ if (err)
+ goto out_stop;
+ }
if (ext4_orphan_add(handle, inode))
goto out_stop;
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 0962642119c..ea704dff866 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -1343,7 +1343,9 @@ void ext4_ind_truncate(struct inode *inode)
__le32 nr = 0;
int n = 0;
ext4_lblk_t last_block, max_block;
+ loff_t page_len;
unsigned blocksize = inode->i_sb->s_blocksize;
+ int err;
handle = start_transaction(inode);
if (IS_ERR(handle))
@@ -1354,9 +1356,16 @@ void ext4_ind_truncate(struct inode *inode)
max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1)
>> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
- if (inode->i_size & (blocksize - 1))
- if (ext4_block_truncate_page(handle, mapping, inode->i_size))
+ if (inode->i_size % PAGE_CACHE_SIZE != 0) {
+ page_len = PAGE_CACHE_SIZE -
+ (inode->i_size & (PAGE_CACHE_SIZE - 1));
+
+ err = ext4_discard_partial_page_buffers(handle,
+ mapping, inode->i_size, page_len, 0);
+
+ if (err)
goto out_stop;
+ }
if (last_block != max_block) {
n = ext4_block_to_path(inode, last_block, offsets, NULL);