aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/inode.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2013-08-17 10:07:17 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-09-26 20:18:15 -0400
commit263c784f2b66e18e34208a2f0e56df65c039d918 (patch)
treeb75d6004263d8604544d4050ff7ddbdf50811e9a /fs/ext4/inode.c
parent25a870d4dc930531f545aa1ae0cdca281d99f980 (diff)
ext4: simplify truncation code in ext4_setattr()
commit 5208386c501276df18fee464e21d3c58d2d79517 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/ext4/inode.c')
-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 ccbfbbb12dc5..904ca1a21dce 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4706,7 +4706,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
4706 ext4_journal_stop(handle); 4706 ext4_journal_stop(handle);
4707 } 4707 }
4708 4708
4709 if (attr->ia_valid & ATTR_SIZE) { 4709 if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
4710 handle_t *handle;
4711 loff_t oldsize = inode->i_size;
4710 4712
4711 if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { 4713 if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
4712 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 4714 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@@ -4714,73 +4716,60 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
4714 if (attr->ia_size > sbi->s_bitmap_maxbytes) 4716 if (attr->ia_size > sbi->s_bitmap_maxbytes)
4715 return -EFBIG; 4717 return -EFBIG;
4716 } 4718 }
4717 } 4719 if (S_ISREG(inode->i_mode) &&
4718 4720 (attr->ia_size < inode->i_size)) {
4719 if (S_ISREG(inode->i_mode) && 4721 if (ext4_should_order_data(inode)) {
4720 attr->ia_valid & ATTR_SIZE && 4722 error = ext4_begin_ordered_truncate(inode,
4721 (attr->ia_size < inode->i_size)) {
4722 handle_t *handle;
4723
4724 handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
4725 if (IS_ERR(handle)) {
4726 error = PTR_ERR(handle);
4727 goto err_out;
4728 }
4729 if (ext4_handle_valid(handle)) {
4730 error = ext4_orphan_add(handle, inode);
4731 orphan = 1;
4732 }
4733 EXT4_I(inode)->i_disksize = attr->ia_size;
4734 rc = ext4_mark_inode_dirty(handle, inode);
4735 if (!error)
4736 error = rc;
4737 ext4_journal_stop(handle);
4738
4739 if (ext4_should_order_data(inode)) {
4740 error = ext4_begin_ordered_truncate(inode,
4741 attr->ia_size); 4723 attr->ia_size);
4742 if (error) { 4724 if (error)
4743 /* Do as much error cleanup as possible */
4744 handle = ext4_journal_start(inode,
4745 EXT4_HT_INODE, 3);
4746 if (IS_ERR(handle)) {
4747 ext4_orphan_del(NULL, inode);
4748 goto err_out; 4725 goto err_out;
4749 } 4726 }
4750 ext4_orphan_del(handle, inode); 4727 handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
4751 orphan = 0; 4728 if (IS_ERR(handle)) {
4752 ext4_journal_stop(handle); 4729 error = PTR_ERR(handle);
4730 goto err_out;
4731 }
4732 if (ext4_handle_valid(handle)) {
4733 error = ext4_orphan_add(handle, inode);
4734 orphan = 1;
4735 }
4736 EXT4_I(inode)->i_disksize = attr->ia_size;
4737 rc = ext4_mark_inode_dirty(handle, inode);
4738 if (!error)
4739 error = rc;
4740 ext4_journal_stop(handle);
4741 if (error) {
4742 ext4_orphan_del(NULL, inode);
4753 goto err_out; 4743 goto err_out;
4754 } 4744 }
4755 } 4745 }
4756 }
4757
4758 if (attr->ia_valid & ATTR_SIZE) {
4759 if (attr->ia_size != inode->i_size) {
4760 loff_t oldsize = inode->i_size;
4761 4746
4762 i_size_write(inode, attr->ia_size); 4747 i_size_write(inode, attr->ia_size);
4763 /* 4748 /*
4764 * Blocks are going to be removed from the inode. Wait 4749 * Blocks are going to be removed from the inode. Wait
4765 * for dio in flight. Temporarily disable 4750 * for dio in flight. Temporarily disable
4766 * dioread_nolock to prevent livelock. 4751 * dioread_nolock to prevent livelock.
4767 */ 4752 */
4768 if (orphan) { 4753 if (orphan) {
4769 if (!ext4_should_journal_data(inode)) { 4754 if (!ext4_should_journal_data(inode)) {
4770 ext4_inode_block_unlocked_dio(inode); 4755 ext4_inode_block_unlocked_dio(inode);
4771 inode_dio_wait(inode); 4756 inode_dio_wait(inode);
4772 ext4_inode_resume_unlocked_dio(inode); 4757 ext4_inode_resume_unlocked_dio(inode);
4773 } else 4758 } else
4774 ext4_wait_for_tail_page_commit(inode); 4759 ext4_wait_for_tail_page_commit(inode);
4775 }
4776 /*
4777 * Truncate pagecache after we've waited for commit
4778 * in data=journal mode to make pages freeable.
4779 */
4780 truncate_pagecache(inode, oldsize, inode->i_size);
4781 } 4760 }
4782 ext4_truncate(inode); 4761 /*
4762 * Truncate pagecache after we've waited for commit
4763 * in data=journal mode to make pages freeable.
4764 */
4765 truncate_pagecache(inode, oldsize, inode->i_size);
4783 } 4766 }
4767 /*
4768 * We want to call ext4_truncate() even if attr->ia_size ==
4769 * inode->i_size for cases like truncation of fallocated space
4770 */
4771 if (attr->ia_valid & ATTR_SIZE)
4772 ext4_truncate(inode);
4784 4773
4785 if (!rc) { 4774 if (!rc) {
4786 setattr_copy(inode, attr); 4775 setattr_copy(inode, attr);