|
@@ -4600,7 +4600,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
ext4_journal_stop(handle);
|
|
|
}
|
|
|
|
|
|
- if (attr->ia_valid & ATTR_SIZE) {
|
|
|
+ if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
|
|
|
+ handle_t *handle;
|
|
|
+ loff_t oldsize = inode->i_size;
|
|
|
|
|
|
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
@@ -4608,73 +4610,60 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
if (attr->ia_size > sbi->s_bitmap_maxbytes)
|
|
|
return -EFBIG;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- if (S_ISREG(inode->i_mode) &&
|
|
|
- attr->ia_valid & ATTR_SIZE &&
|
|
|
- (attr->ia_size < inode->i_size)) {
|
|
|
- handle_t *handle;
|
|
|
-
|
|
|
- handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
|
|
|
- if (IS_ERR(handle)) {
|
|
|
- error = PTR_ERR(handle);
|
|
|
- goto err_out;
|
|
|
- }
|
|
|
- if (ext4_handle_valid(handle)) {
|
|
|
- error = ext4_orphan_add(handle, inode);
|
|
|
- orphan = 1;
|
|
|
- }
|
|
|
- EXT4_I(inode)->i_disksize = attr->ia_size;
|
|
|
- rc = ext4_mark_inode_dirty(handle, inode);
|
|
|
- if (!error)
|
|
|
- error = rc;
|
|
|
- ext4_journal_stop(handle);
|
|
|
-
|
|
|
- if (ext4_should_order_data(inode)) {
|
|
|
- error = ext4_begin_ordered_truncate(inode,
|
|
|
+ if (S_ISREG(inode->i_mode) &&
|
|
|
+ (attr->ia_size < inode->i_size)) {
|
|
|
+ if (ext4_should_order_data(inode)) {
|
|
|
+ error = ext4_begin_ordered_truncate(inode,
|
|
|
attr->ia_size);
|
|
|
- if (error) {
|
|
|
- /* Do as much error cleanup as possible */
|
|
|
- handle = ext4_journal_start(inode,
|
|
|
- EXT4_HT_INODE, 3);
|
|
|
- if (IS_ERR(handle)) {
|
|
|
- ext4_orphan_del(NULL, inode);
|
|
|
+ if (error)
|
|
|
goto err_out;
|
|
|
- }
|
|
|
- ext4_orphan_del(handle, inode);
|
|
|
- orphan = 0;
|
|
|
- ext4_journal_stop(handle);
|
|
|
+ }
|
|
|
+ handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
|
|
|
+ if (IS_ERR(handle)) {
|
|
|
+ error = PTR_ERR(handle);
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
+ if (ext4_handle_valid(handle)) {
|
|
|
+ error = ext4_orphan_add(handle, inode);
|
|
|
+ orphan = 1;
|
|
|
+ }
|
|
|
+ EXT4_I(inode)->i_disksize = attr->ia_size;
|
|
|
+ rc = ext4_mark_inode_dirty(handle, inode);
|
|
|
+ if (!error)
|
|
|
+ error = rc;
|
|
|
+ ext4_journal_stop(handle);
|
|
|
+ if (error) {
|
|
|
+ ext4_orphan_del(NULL, inode);
|
|
|
goto err_out;
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- if (attr->ia_valid & ATTR_SIZE) {
|
|
|
- if (attr->ia_size != inode->i_size) {
|
|
|
- loff_t oldsize = inode->i_size;
|
|
|
|
|
|
- i_size_write(inode, attr->ia_size);
|
|
|
- /*
|
|
|
- * Blocks are going to be removed from the inode. Wait
|
|
|
- * for dio in flight. Temporarily disable
|
|
|
- * dioread_nolock to prevent livelock.
|
|
|
- */
|
|
|
- if (orphan) {
|
|
|
- if (!ext4_should_journal_data(inode)) {
|
|
|
- ext4_inode_block_unlocked_dio(inode);
|
|
|
- inode_dio_wait(inode);
|
|
|
- ext4_inode_resume_unlocked_dio(inode);
|
|
|
- } else
|
|
|
- ext4_wait_for_tail_page_commit(inode);
|
|
|
- }
|
|
|
- /*
|
|
|
- * Truncate pagecache after we've waited for commit
|
|
|
- * in data=journal mode to make pages freeable.
|
|
|
- */
|
|
|
- truncate_pagecache(inode, oldsize, inode->i_size);
|
|
|
+ i_size_write(inode, attr->ia_size);
|
|
|
+ /*
|
|
|
+ * Blocks are going to be removed from the inode. Wait
|
|
|
+ * for dio in flight. Temporarily disable
|
|
|
+ * dioread_nolock to prevent livelock.
|
|
|
+ */
|
|
|
+ if (orphan) {
|
|
|
+ if (!ext4_should_journal_data(inode)) {
|
|
|
+ ext4_inode_block_unlocked_dio(inode);
|
|
|
+ inode_dio_wait(inode);
|
|
|
+ ext4_inode_resume_unlocked_dio(inode);
|
|
|
+ } else
|
|
|
+ ext4_wait_for_tail_page_commit(inode);
|
|
|
}
|
|
|
- ext4_truncate(inode);
|
|
|
+ /*
|
|
|
+ * Truncate pagecache after we've waited for commit
|
|
|
+ * in data=journal mode to make pages freeable.
|
|
|
+ */
|
|
|
+ truncate_pagecache(inode, oldsize, inode->i_size);
|
|
|
}
|
|
|
+ /*
|
|
|
+ * We want to call ext4_truncate() even if attr->ia_size ==
|
|
|
+ * inode->i_size for cases like truncation of fallocated space
|
|
|
+ */
|
|
|
+ if (attr->ia_valid & ATTR_SIZE)
|
|
|
+ ext4_truncate(inode);
|
|
|
|
|
|
if (!rc) {
|
|
|
setattr_copy(inode, attr);
|