forked from Minki/linux
Btrfs: Verify checksums on tree blocks found without read_tree_block
Checksums were only verified by btrfs_read_tree_block, which meant the functions to probe the page cache for blocks were not validating checksums. Normally this is fine because the buffers will only be in cache if they have already been validated. But, there is a window while the buffer is being read from disk where it could be up to date in the cache but not yet verified. This patch makes sure all buffers go through checksum verification before they are used. This is safer, and it prevents modification of buffers before they go through the csum code. Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
ecbe2402cb
commit
0999df54f8
@ -158,6 +158,8 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
} else {
|
||||
root_gen = 0;
|
||||
}
|
||||
if (!(buf->flags & EXTENT_CSUM))
|
||||
WARN_ON(1);
|
||||
|
||||
WARN_ON(root->ref_cows && trans->transid !=
|
||||
root->fs_info->running_transaction->transid);
|
||||
@ -245,6 +247,8 @@ int btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
root->fs_info->generation);
|
||||
WARN_ON(1);
|
||||
}
|
||||
if (!(buf->flags & EXTENT_CSUM))
|
||||
WARN_ON(1);
|
||||
|
||||
header_trans = btrfs_header_generation(buf);
|
||||
spin_lock(&root->fs_info->hash_lock);
|
||||
@ -396,6 +400,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
|
||||
if (search_start == 0)
|
||||
search_start = last_block;
|
||||
|
||||
btrfs_verify_block_csum(root, cur);
|
||||
err = __btrfs_cow_block(trans, root, cur, parent, i,
|
||||
&tmp, search_start,
|
||||
min(16 * blocksize,
|
||||
|
@ -46,27 +46,6 @@ static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
|
||||
|
||||
static struct extent_io_ops btree_extent_io_ops;
|
||||
|
||||
struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct inode *btree_inode = root->fs_info->btree_inode;
|
||||
struct extent_buffer *eb;
|
||||
eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
|
||||
bytenr, blocksize, GFP_NOFS);
|
||||
return eb;
|
||||
}
|
||||
|
||||
struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct inode *btree_inode = root->fs_info->btree_inode;
|
||||
struct extent_buffer *eb;
|
||||
|
||||
eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
|
||||
bytenr, blocksize, NULL, GFP_NOFS);
|
||||
return eb;
|
||||
}
|
||||
|
||||
struct extent_map *btree_get_extent(struct inode *inode, struct page *page,
|
||||
size_t page_offset, u64 start, u64 len,
|
||||
int create)
|
||||
@ -380,13 +359,69 @@ static int close_all_devices(struct btrfs_fs_info *fs_info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_verify_block_csum(struct btrfs_root *root,
|
||||
struct extent_buffer *buf)
|
||||
{
|
||||
struct extent_io_tree *io_tree;
|
||||
u64 end;
|
||||
int ret;
|
||||
|
||||
io_tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
|
||||
if (buf->flags & EXTENT_CSUM)
|
||||
return 0;
|
||||
|
||||
end = min_t(u64, buf->len, PAGE_CACHE_SIZE);
|
||||
end = buf->start + end - 1;
|
||||
if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
|
||||
buf->flags |= EXTENT_CSUM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock_extent(io_tree, buf->start, end, GFP_NOFS);
|
||||
|
||||
if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
|
||||
buf->flags |= EXTENT_CSUM;
|
||||
ret = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = csum_tree_block(root, buf, 1);
|
||||
set_extent_bits(io_tree, buf->start, end, EXTENT_CSUM, GFP_NOFS);
|
||||
buf->flags |= EXTENT_CSUM;
|
||||
|
||||
out_unlock:
|
||||
unlock_extent(io_tree, buf->start, end, GFP_NOFS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct inode *btree_inode = root->fs_info->btree_inode;
|
||||
struct extent_buffer *eb;
|
||||
eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
|
||||
bytenr, blocksize, GFP_NOFS);
|
||||
return eb;
|
||||
}
|
||||
|
||||
struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
|
||||
u64 bytenr, u32 blocksize)
|
||||
{
|
||||
struct inode *btree_inode = root->fs_info->btree_inode;
|
||||
struct extent_buffer *eb;
|
||||
|
||||
eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
|
||||
bytenr, blocksize, NULL, GFP_NOFS);
|
||||
return eb;
|
||||
}
|
||||
|
||||
|
||||
struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
|
||||
u32 blocksize)
|
||||
{
|
||||
struct extent_buffer *buf = NULL;
|
||||
struct inode *btree_inode = root->fs_info->btree_inode;
|
||||
struct extent_io_tree *io_tree;
|
||||
u64 end;
|
||||
int ret;
|
||||
|
||||
io_tree = &BTRFS_I(btree_inode)->io_tree;
|
||||
@ -397,28 +432,7 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
|
||||
read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, 0, 1,
|
||||
btree_get_extent);
|
||||
|
||||
if (buf->flags & EXTENT_CSUM)
|
||||
return buf;
|
||||
|
||||
end = buf->start + PAGE_CACHE_SIZE - 1;
|
||||
if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
|
||||
buf->flags |= EXTENT_CSUM;
|
||||
return buf;
|
||||
}
|
||||
|
||||
lock_extent(io_tree, buf->start, end, GFP_NOFS);
|
||||
|
||||
if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
|
||||
buf->flags |= EXTENT_CSUM;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = csum_tree_block(root, buf, 1);
|
||||
set_extent_bits(io_tree, buf->start, end, EXTENT_CSUM, GFP_NOFS);
|
||||
buf->flags |= EXTENT_CSUM;
|
||||
|
||||
out_unlock:
|
||||
unlock_extent(io_tree, buf->start, end, GFP_NOFS);
|
||||
ret = btrfs_verify_block_csum(root, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -69,4 +69,6 @@ u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len);
|
||||
void btrfs_csum_final(u32 crc, char *result);
|
||||
void btrfs_throttle(struct btrfs_root *root);
|
||||
int btrfs_open_device(struct btrfs_device *dev);
|
||||
int btrfs_verify_block_csum(struct btrfs_root *root,
|
||||
struct extent_buffer *buf);
|
||||
#endif
|
||||
|
@ -2069,6 +2069,8 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
|
||||
BUG_ON(ret);
|
||||
continue;
|
||||
}
|
||||
} else if (next) {
|
||||
btrfs_verify_block_csum(root, next);
|
||||
}
|
||||
WARN_ON(*level <= 0);
|
||||
if (path->nodes[*level-1])
|
||||
|
@ -101,6 +101,7 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
|
||||
path->slots[*level]++;
|
||||
continue;
|
||||
}
|
||||
btrfs_verify_block_csum(root, next);
|
||||
} else {
|
||||
next = read_tree_block(root, bytenr,
|
||||
btrfs_level_size(root, *level - 1));
|
||||
|
Loading…
Reference in New Issue
Block a user