diff options
author | Jan Kara <jack@suse.cz> | 2013-08-17 10:07:17 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2013-08-17 10:07:17 -0400 |
commit | 5208386c501276df18fee464e21d3c58d2d79517 (patch) | |
tree | 96d312aa9a9e3afed79e82185e3be055fb5c2a87 | |
parent | 5f1132b2ba8c873f25982cf45917e8455fb6c962 (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.c | 109 |
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); |