diff options
author | Josef Bacik <jbacik@fb.com> | 2015-06-22 00:31:26 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2015-06-22 00:31:26 -0400 |
commit | 3da40c7b089810ac9cf2bb1e59633f619f3a7312 (patch) | |
tree | e1649f9e7cbe2ae93c7e1cd82ae21fbdef8342a6 | |
parent | 04e22412f420ade46dbf792a10e7f0d26ae55359 (diff) |
ext4: only call ext4_truncate when size <= isize
At LSF we decided that if we truncate up from isize we shouldn't trim
fallocated blocks that were fallocated with KEEP_SIZE and are past the
new i_size. This patch fixes ext4 to do this.
[ Completely reworked patch so that i_disksize would actually get set
when truncating up. Also reworked the code for handling truncate so
that it's easier to handle. -- tytso ]
Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Lukas Czerner <lczerner@redhat.com>
-rw-r--r-- | fs/ext4/inode.c | 38 |
1 files changed, 18 insertions, 20 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ae93f0bb9485..e057c6fcc227 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4681,8 +4681,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
4681 | ext4_journal_stop(handle); | 4681 | ext4_journal_stop(handle); |
4682 | } | 4682 | } |
4683 | 4683 | ||
4684 | if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) { | 4684 | if (attr->ia_valid & ATTR_SIZE) { |
4685 | handle_t *handle; | 4685 | handle_t *handle; |
4686 | loff_t oldsize = inode->i_size; | ||
4687 | int shrink = (attr->ia_size <= inode->i_size); | ||
4686 | 4688 | ||
4687 | if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { | 4689 | if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { |
4688 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | 4690 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); |
@@ -4690,24 +4692,26 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
4690 | if (attr->ia_size > sbi->s_bitmap_maxbytes) | 4692 | if (attr->ia_size > sbi->s_bitmap_maxbytes) |
4691 | return -EFBIG; | 4693 | return -EFBIG; |
4692 | } | 4694 | } |
4695 | if (!S_ISREG(inode->i_mode)) | ||
4696 | return -EINVAL; | ||
4693 | 4697 | ||
4694 | if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size) | 4698 | if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size) |
4695 | inode_inc_iversion(inode); | 4699 | inode_inc_iversion(inode); |
4696 | 4700 | ||
4697 | if (S_ISREG(inode->i_mode) && | 4701 | if (ext4_should_order_data(inode) && |
4698 | (attr->ia_size < inode->i_size)) { | 4702 | (attr->ia_size < inode->i_size)) { |
4699 | if (ext4_should_order_data(inode)) { | 4703 | error = ext4_begin_ordered_truncate(inode, |
4700 | error = ext4_begin_ordered_truncate(inode, | ||
4701 | attr->ia_size); | 4704 | attr->ia_size); |
4702 | if (error) | 4705 | if (error) |
4703 | goto err_out; | 4706 | goto err_out; |
4704 | } | 4707 | } |
4708 | if (attr->ia_size != inode->i_size) { | ||
4705 | handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); | 4709 | handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); |
4706 | if (IS_ERR(handle)) { | 4710 | if (IS_ERR(handle)) { |
4707 | error = PTR_ERR(handle); | 4711 | error = PTR_ERR(handle); |
4708 | goto err_out; | 4712 | goto err_out; |
4709 | } | 4713 | } |
4710 | if (ext4_handle_valid(handle)) { | 4714 | if (ext4_handle_valid(handle) && shrink) { |
4711 | error = ext4_orphan_add(handle, inode); | 4715 | error = ext4_orphan_add(handle, inode); |
4712 | orphan = 1; | 4716 | orphan = 1; |
4713 | } | 4717 | } |
@@ -4726,15 +4730,13 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
4726 | up_write(&EXT4_I(inode)->i_data_sem); | 4730 | up_write(&EXT4_I(inode)->i_data_sem); |
4727 | ext4_journal_stop(handle); | 4731 | ext4_journal_stop(handle); |
4728 | if (error) { | 4732 | if (error) { |
4729 | ext4_orphan_del(NULL, inode); | 4733 | if (orphan) |
4734 | ext4_orphan_del(NULL, inode); | ||
4730 | goto err_out; | 4735 | goto err_out; |
4731 | } | 4736 | } |
4732 | } else { | ||
4733 | loff_t oldsize = inode->i_size; | ||
4734 | |||
4735 | i_size_write(inode, attr->ia_size); | ||
4736 | pagecache_isize_extended(inode, oldsize, inode->i_size); | ||
4737 | } | 4737 | } |
4738 | if (!shrink) | ||
4739 | pagecache_isize_extended(inode, oldsize, inode->i_size); | ||
4738 | 4740 | ||
4739 | /* | 4741 | /* |
4740 | * Blocks are going to be removed from the inode. Wait | 4742 | * Blocks are going to be removed from the inode. Wait |
@@ -4754,13 +4756,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
4754 | * in data=journal mode to make pages freeable. | 4756 | * in data=journal mode to make pages freeable. |
4755 | */ | 4757 | */ |
4756 | truncate_pagecache(inode, inode->i_size); | 4758 | truncate_pagecache(inode, inode->i_size); |
4759 | if (shrink) | ||
4760 | ext4_truncate(inode); | ||
4757 | } | 4761 | } |
4758 | /* | ||
4759 | * We want to call ext4_truncate() even if attr->ia_size == | ||
4760 | * inode->i_size for cases like truncation of fallocated space | ||
4761 | */ | ||
4762 | if (attr->ia_valid & ATTR_SIZE) | ||
4763 | ext4_truncate(inode); | ||
4764 | 4762 | ||
4765 | if (!rc) { | 4763 | if (!rc) { |
4766 | setattr_copy(inode, attr); | 4764 | setattr_copy(inode, attr); |