summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fb.com>2016-11-14 14:06:22 -0500
committerDavid Sterba <dsterba@suse.com>2016-11-30 13:45:19 +0100
commitf94480bd7be6bb1b0823d1036f3ee4ebe7450172 (patch)
tree185626f23c4cf4c5be566f9984d2ede1137904e7
parent62fe51c1d0100ff07a761cd077872e01f2a2b8ca (diff)
downloadlinux-f94480bd7be6bb1b0823d1036f3ee4ebe7450172.tar.gz
linux-f94480bd7be6bb1b0823d1036f3ee4ebe7450172.tar.bz2
linux-f94480bd7be6bb1b0823d1036f3ee4ebe7450172.zip
Btrfs: abort transaction if fill_holes() fails
At this point we will have dropped extent entries from the file, so if we fail to insert the new hole entries then we are leaving the fs in a corrupt state (albeit an easily fixed one). Abort the transaciton if this happens so we can avoid corrupting the fs. Thanks, Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/file.c19
1 files changed, 17 insertions, 2 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index f5288fa0aad0..3c1f4be36f16 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2232,9 +2232,15 @@ static int fill_holes(struct btrfs_trans_handle *trans, struct inode *inode,
key.offset = offset;
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
- if (ret < 0)
+ if (ret <= 0) {
+ /*
+ * We should have dropped this offset, so if we find it then
+ * something has gone horribly wrong.
+ */
+ if (ret == 0)
+ ret = -EINVAL;
return ret;
- BUG_ON(!ret);
+ }
leaf = path->nodes[0];
if (hole_mergeable(inode, leaf, path->slots[0]-1, offset, end)) {
@@ -2537,6 +2543,13 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
ret = fill_holes(trans, inode, path, cur_offset,
drop_end);
if (ret) {
+ /*
+ * If we failed then we didn't insert our hole
+ * entries for the area we dropped, so now the
+ * fs is corrupted, so we must abort the
+ * transaction.
+ */
+ btrfs_abort_transaction(trans, ret);
err = ret;
break;
}
@@ -2601,6 +2614,8 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
if (cur_offset < ino_size && cur_offset < drop_end) {
ret = fill_holes(trans, inode, path, cur_offset, drop_end);
if (ret) {
+ /* Same comment as above. */
+ btrfs_abort_transaction(trans, ret);
err = ret;
goto out_trans;
}