Btrfs: only update disk_i_size as we remove extents
This fixes a problem where if we fail a truncate we will leave the i_size set where we wanted to truncate to instead of where we were able to truncate to. Fix this by making btrfs_truncate_inode_items do the disk_i_size update as it removes extents, that way it will always be consistent with where its extents are. Then if the truncate fails at all we can update the in-ram i_size with what we have on disk and delete the orphan item. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
This commit is contained in:
parent
f45388f387
commit
7f4f6e0a3f
@ -3861,6 +3861,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
|
|||||||
u64 extent_num_bytes = 0;
|
u64 extent_num_bytes = 0;
|
||||||
u64 extent_offset = 0;
|
u64 extent_offset = 0;
|
||||||
u64 item_end = 0;
|
u64 item_end = 0;
|
||||||
|
u64 last_size = (u64)-1;
|
||||||
u32 found_type = (u8)-1;
|
u32 found_type = (u8)-1;
|
||||||
int found_extent;
|
int found_extent;
|
||||||
int del_item;
|
int del_item;
|
||||||
@ -3958,6 +3959,11 @@ search_again:
|
|||||||
if (found_type != BTRFS_EXTENT_DATA_KEY)
|
if (found_type != BTRFS_EXTENT_DATA_KEY)
|
||||||
goto delete;
|
goto delete;
|
||||||
|
|
||||||
|
if (del_item)
|
||||||
|
last_size = found_key.offset;
|
||||||
|
else
|
||||||
|
last_size = new_size;
|
||||||
|
|
||||||
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
|
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
|
||||||
u64 num_dec;
|
u64 num_dec;
|
||||||
extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
|
extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
|
||||||
@ -4069,6 +4075,8 @@ out:
|
|||||||
btrfs_abort_transaction(trans, root, ret);
|
btrfs_abort_transaction(trans, root, ret);
|
||||||
}
|
}
|
||||||
error:
|
error:
|
||||||
|
if (last_size != (u64)-1)
|
||||||
|
btrfs_ordered_update_i_size(inode, last_size, NULL);
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -4397,8 +4405,26 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr)
|
|||||||
btrfs_inode_resume_unlocked_dio(inode);
|
btrfs_inode_resume_unlocked_dio(inode);
|
||||||
|
|
||||||
ret = btrfs_truncate(inode);
|
ret = btrfs_truncate(inode);
|
||||||
if (ret && inode->i_nlink)
|
if (ret && inode->i_nlink) {
|
||||||
btrfs_orphan_del(NULL, inode);
|
int err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* failed to truncate, disk_i_size is only adjusted down
|
||||||
|
* as we remove extents, so it should represent the true
|
||||||
|
* size of the inode, so reset the in memory size and
|
||||||
|
* delete our orphan entry.
|
||||||
|
*/
|
||||||
|
trans = btrfs_join_transaction(root);
|
||||||
|
if (IS_ERR(trans)) {
|
||||||
|
btrfs_orphan_del(NULL, inode);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
i_size_write(inode, BTRFS_I(inode)->disk_i_size);
|
||||||
|
err = btrfs_orphan_del(trans, inode);
|
||||||
|
if (err)
|
||||||
|
btrfs_abort_transaction(trans, root, err);
|
||||||
|
btrfs_end_transaction(trans, root);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -7537,7 +7563,6 @@ static int btrfs_truncate(struct inode *inode)
|
|||||||
u64 min_size = btrfs_calc_trunc_metadata_size(root, 1);
|
u64 min_size = btrfs_calc_trunc_metadata_size(root, 1);
|
||||||
|
|
||||||
btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
|
btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
|
||||||
btrfs_ordered_update_i_size(inode, inode->i_size, NULL);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Yes ladies and gentelment, this is indeed ugly. The fact is we have
|
* Yes ladies and gentelment, this is indeed ugly. The fact is we have
|
||||||
|
Loading…
x
Reference in New Issue
Block a user