aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2013-08-17 10:07:17 -0400
committerTheodore Ts'o <tytso@mit.edu>2013-08-17 10:07:17 -0400
commit5208386c501276df18fee464e21d3c58d2d79517 (patch)
tree96d312aa9a9e3afed79e82185e3be055fb5c2a87
parent5f1132b2ba8c873f25982cf45917e8455fb6c962 (diff)
ext4: simplify truncation code in ext4_setattr()
Merge conditions in ext4_setattr() handling inode size changes, also move ext4_begin_ordered_truncate() call somewhat earlier because it simplifies error recovery in case of failure. Also add error handling in case i_disksize update fails. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: stable@vger.kernel.org
-rw-r--r--fs/ext4/inode.c109
1 files changed, 49 insertions, 60 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 19fa2e076275..38f430119fef 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4600,7 +4600,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
4600 ext4_journal_stop(handle); 4600 ext4_journal_stop(handle);
4601 } 4601 }
4602 4602
4603 if (attr->ia_valid & ATTR_SIZE) { 4603 if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
4604 handle_t *handle;
4605 loff_t oldsize = inode->i_size;
4604 4606
4605 if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { 4607 if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
4606 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 4608 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@@ -4608,73 +4610,60 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
4608 if (attr->ia_size > sbi->s_bitmap_maxbytes) 4610 if (attr->ia_size > sbi->s_bitmap_maxbytes)
4609 return -EFBIG; 4611 return -EFBIG;
4610 } 4612 }
4611 } 4613 if (S_ISREG(inode->i_mode) &&
4612 4614 (attr->ia_size < inode->i_size)) {
4613 if (S_ISREG(inode->i_mode) && 4615 if (ext4_should_order_data(inode)) {
4614 attr->ia_valid & ATTR_SIZE && 4616 error = ext4_begin_ordered_truncate(inode,
4615 (attr->ia_size < inode->i_size)) {
4616 handle_t *handle;
4617
4618 handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
4619 if (IS_ERR(handle)) {
4620 error = PTR_ERR(handle);
4621 goto err_out;
4622 }
4623 if (ext4_handle_valid(handle)) {
4624 error = ext4_orphan_add(handle, inode);
4625 orphan = 1;
4626 }
4627 EXT4_I(inode)->i_disksize = attr->ia_size;
4628 rc = ext4_mark_inode_dirty(handle, inode);
4629 if (!error)
4630 error = rc;
4631 ext4_journal_stop(handle);
4632
4633 if (ext4_should_order_data(inode)) {
4634 error = ext4_begin_ordered_truncate(inode,
4635 attr->ia_size); 4617 attr->ia_size);
4636 if (error) { 4618 if (error)
4637 /* Do as much error cleanup as possible */
4638 handle = ext4_journal_start(inode,
4639 EXT4_HT_INODE, 3);
4640 if (IS_ERR(handle)) {
4641 ext4_orphan_del(NULL, inode);
4642 goto err_out; 4619 goto err_out;
4643 } 4620 }
4644 ext4_orphan_del(handle, inode); 4621 handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
4645 orphan = 0; 4622 if (IS_ERR(handle)) {
4646 ext4_journal_stop(handle); 4623 error = PTR_ERR(handle);
4624 goto err_out;
4625 }
4626 if (ext4_handle_valid(handle)) {
4627 error = ext4_orphan_add(handle, inode);
4628 orphan = 1;
4629 }
4630 EXT4_I(inode)->i_disksize = attr->ia_size;
4631 rc = ext4_mark_inode_dirty(handle, inode);
4632 if (!error)
4633 error = rc;
4634 ext4_journal_stop(handle);
4635 if (error) {
4636 ext4_orphan_del(NULL, inode);
4647 goto err_out; 4637 goto err_out;
4648 } 4638 }
4649 } 4639 }
4650 }
4651
4652 if (attr->ia_valid & ATTR_SIZE) {
4653 if (attr->ia_size != inode->i_size) {
4654 loff_t oldsize = inode->i_size;
4655 4640
4656 i_size_write(inode, attr->ia_size); 4641 i_size_write(inode, attr->ia_size);
4657 /* 4642 /*
4658 * Blocks are going to be removed from the inode. Wait 4643 * Blocks are going to be removed from the inode. Wait
4659 * for dio in flight. Temporarily disable 4644 * for dio in flight. Temporarily disable
4660 * dioread_nolock to prevent livelock. 4645 * dioread_nolock to prevent livelock.
4661 */ 4646 */
4662 if (orphan) { 4647 if (orphan) {
4663 if (!ext4_should_journal_data(inode)) { 4648 if (!ext4_should_journal_data(inode)) {
4664 ext4_inode_block_unlocked_dio(inode); 4649 ext4_inode_block_unlocked_dio(inode);
4665 inode_dio_wait(inode); 4650 inode_dio_wait(inode);
4666 ext4_inode_resume_unlocked_dio(inode); 4651 ext4_inode_resume_unlocked_dio(inode);
4667 } else 4652 } else
4668 ext4_wait_for_tail_page_commit(inode); 4653 ext4_wait_for_tail_page_commit(inode);
4669 }
4670 /*
4671 * Truncate pagecache after we've waited for commit
4672 * in data=journal mode to make pages freeable.
4673 */
4674 truncate_pagecache(inode, oldsize, inode->i_size);
4675 } 4654 }
4676 ext4_truncate(inode); 4655 /*
4656 * Truncate pagecache after we've waited for commit
4657 * in data=journal mode to make pages freeable.
4658 */
4659 truncate_pagecache(inode, oldsize, inode->i_size);
4677 } 4660 }
4661 /*
4662 * We want to call ext4_truncate() even if attr->ia_size ==
4663 * inode->i_size for cases like truncation of fallocated space
4664 */
4665 if (attr->ia_valid & ATTR_SIZE)
4666 ext4_truncate(inode);
4678 4667
4679 if (!rc) { 4668 if (!rc) {
4680 setattr_copy(inode, attr); 4669 setattr_copy(inode, attr);