diff options
author | Josef Bacik <jbacik@fb.com> | 2016-11-14 14:06:22 -0500 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2016-11-30 13:45:19 +0100 |
commit | f94480bd7be6bb1b0823d1036f3ee4ebe7450172 (patch) | |
tree | 185626f23c4cf4c5be566f9984d2ede1137904e7 | |
parent | 62fe51c1d0100ff07a761cd077872e01f2a2b8ca (diff) | |
download | linux-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.c | 19 |
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; } |