Btrfs: fix all callers of read_tree_block
We kept leaking extent buffers when mounting a broken file system and it turns out it's because not everybody uses read_tree_block properly. You need to check and make sure the extent_buffer is uptodate before you use it. This patch fixes everybody who calls read_tree_block directly to make sure they check that it is uptodate and free it and return an error if it is not. With this we no longer leak EB's when things go horribly wrong. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com>
This commit is contained in:
Родитель
51bf5f0bc4
Коммит
416bc6580b
|
@ -423,7 +423,10 @@ static int __add_missing_keys(struct btrfs_fs_info *fs_info,
|
|||
BUG_ON(!ref->wanted_disk_byte);
|
||||
eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte,
|
||||
fs_info->tree_root->leafsize, 0);
|
||||
BUG_ON(!eb);
|
||||
if (!eb || !extent_buffer_uptodate(eb)) {
|
||||
free_extent_buffer(eb);
|
||||
return -EIO;
|
||||
}
|
||||
btrfs_tree_read_lock(eb);
|
||||
if (btrfs_header_level(eb) == 0)
|
||||
btrfs_item_key_to_cpu(eb, &ref->key_for_search, 0);
|
||||
|
@ -913,7 +916,10 @@ again:
|
|||
info_level);
|
||||
eb = read_tree_block(fs_info->extent_root,
|
||||
ref->parent, bsz, 0);
|
||||
BUG_ON(!eb);
|
||||
if (!eb || !extent_buffer_uptodate(eb)) {
|
||||
free_extent_buffer(eb);
|
||||
return -EIO;
|
||||
}
|
||||
ret = find_extent_in_eb(eb, bytenr,
|
||||
*extent_item_pos, &eie);
|
||||
ref->inode_list = eie;
|
||||
|
|
|
@ -1281,7 +1281,8 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
|
|||
free_extent_buffer(eb_root);
|
||||
blocksize = btrfs_level_size(root, old_root->level);
|
||||
old = read_tree_block(root, logical, blocksize, 0);
|
||||
if (!old) {
|
||||
if (!old || !extent_buffer_uptodate(old)) {
|
||||
free_extent_buffer(old);
|
||||
pr_warn("btrfs: failed to read tree block %llu from get_old_root\n",
|
||||
logical);
|
||||
WARN_ON(1);
|
||||
|
@ -1526,8 +1527,10 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
|
|||
if (!cur) {
|
||||
cur = read_tree_block(root, blocknr,
|
||||
blocksize, gen);
|
||||
if (!cur)
|
||||
if (!cur || !extent_buffer_uptodate(cur)) {
|
||||
free_extent_buffer(cur);
|
||||
return -EIO;
|
||||
}
|
||||
} else if (!uptodate) {
|
||||
err = btrfs_read_buffer(cur, gen);
|
||||
if (err) {
|
||||
|
@ -1692,6 +1695,8 @@ static noinline struct extent_buffer *read_node_slot(struct btrfs_root *root,
|
|||
struct extent_buffer *parent, int slot)
|
||||
{
|
||||
int level = btrfs_header_level(parent);
|
||||
struct extent_buffer *eb;
|
||||
|
||||
if (slot < 0)
|
||||
return NULL;
|
||||
if (slot >= btrfs_header_nritems(parent))
|
||||
|
@ -1699,9 +1704,15 @@ static noinline struct extent_buffer *read_node_slot(struct btrfs_root *root,
|
|||
|
||||
BUG_ON(level == 0);
|
||||
|
||||
return read_tree_block(root, btrfs_node_blockptr(parent, slot),
|
||||
btrfs_level_size(root, level - 1),
|
||||
btrfs_node_ptr_generation(parent, slot));
|
||||
eb = read_tree_block(root, btrfs_node_blockptr(parent, slot),
|
||||
btrfs_level_size(root, level - 1),
|
||||
btrfs_node_ptr_generation(parent, slot));
|
||||
if (eb && !extent_buffer_uptodate(eb)) {
|
||||
free_extent_buffer(eb);
|
||||
eb = NULL;
|
||||
}
|
||||
|
||||
return eb;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1496,6 +1496,14 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
|
|||
blocksize = btrfs_level_size(root, btrfs_root_level(&root->root_item));
|
||||
root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item),
|
||||
blocksize, generation);
|
||||
if (!root->node || !extent_buffer_uptodate(root->node)) {
|
||||
ret = (!root->node) ? -ENOMEM : -EIO;
|
||||
|
||||
free_extent_buffer(root->node);
|
||||
kfree(root);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
root->commit_root = btrfs_root_node(root);
|
||||
BUG_ON(!root->node); /* -ENOMEM */
|
||||
out:
|
||||
|
@ -2515,8 +2523,8 @@ int open_ctree(struct super_block *sb,
|
|||
chunk_root->node = read_tree_block(chunk_root,
|
||||
btrfs_super_chunk_root(disk_super),
|
||||
blocksize, generation);
|
||||
BUG_ON(!chunk_root->node); /* -ENOMEM */
|
||||
if (!test_bit(EXTENT_BUFFER_UPTODATE, &chunk_root->node->bflags)) {
|
||||
if (!chunk_root->node ||
|
||||
!test_bit(EXTENT_BUFFER_UPTODATE, &chunk_root->node->bflags)) {
|
||||
printk(KERN_WARNING "btrfs: failed to read chunk root on %s\n",
|
||||
sb->s_id);
|
||||
goto fail_tree_roots;
|
||||
|
@ -2701,6 +2709,13 @@ retry_root_backup:
|
|||
log_tree_root->node = read_tree_block(tree_root, bytenr,
|
||||
blocksize,
|
||||
generation + 1);
|
||||
if (!log_tree_root->node ||
|
||||
!extent_buffer_uptodate(log_tree_root->node)) {
|
||||
printk(KERN_ERR "btrfs: failed to read log tree\n");
|
||||
free_extent_buffer(log_tree_root->node);
|
||||
kfree(log_tree_root);
|
||||
goto fail_trans_kthread;
|
||||
}
|
||||
/* returns with log_tree_root freed on success */
|
||||
ret = btrfs_recover_log_trees(log_tree_root);
|
||||
if (ret) {
|
||||
|
|
|
@ -7087,8 +7087,10 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
|
|||
if (reada && level == 1)
|
||||
reada_walk_down(trans, root, wc, path);
|
||||
next = read_tree_block(root, bytenr, blocksize, generation);
|
||||
if (!next)
|
||||
if (!next || !extent_buffer_uptodate(next)) {
|
||||
free_extent_buffer(next);
|
||||
return -EIO;
|
||||
}
|
||||
btrfs_tree_lock(next);
|
||||
btrfs_set_lock_blocking(next);
|
||||
}
|
||||
|
|
|
@ -1771,7 +1771,11 @@ again:
|
|||
|
||||
eb = read_tree_block(dest, old_bytenr, blocksize,
|
||||
old_ptr_gen);
|
||||
BUG_ON(!eb);
|
||||
if (!eb || !extent_buffer_uptodate(eb)) {
|
||||
ret = (!eb) ? -ENOMEM : -EIO;
|
||||
free_extent_buffer(eb);
|
||||
return ret;
|
||||
}
|
||||
btrfs_tree_lock(eb);
|
||||
if (cow) {
|
||||
ret = btrfs_cow_block(trans, dest, eb, parent,
|
||||
|
@ -1924,6 +1928,10 @@ int walk_down_reloc_tree(struct btrfs_root *root, struct btrfs_path *path,
|
|||
bytenr = btrfs_node_blockptr(eb, path->slots[i]);
|
||||
blocksize = btrfs_level_size(root, i - 1);
|
||||
eb = read_tree_block(root, bytenr, blocksize, ptr_gen);
|
||||
if (!eb || !extent_buffer_uptodate(eb)) {
|
||||
free_extent_buffer(eb);
|
||||
return -EIO;
|
||||
}
|
||||
BUG_ON(btrfs_header_level(eb) != i - 1);
|
||||
path->nodes[i - 1] = eb;
|
||||
path->slots[i - 1] = 0;
|
||||
|
@ -2601,7 +2609,8 @@ static int do_relocation(struct btrfs_trans_handle *trans,
|
|||
blocksize = btrfs_level_size(root, node->level);
|
||||
generation = btrfs_node_ptr_generation(upper->eb, slot);
|
||||
eb = read_tree_block(root, bytenr, blocksize, generation);
|
||||
if (!eb) {
|
||||
if (!eb || !extent_buffer_uptodate(eb)) {
|
||||
free_extent_buffer(eb);
|
||||
err = -EIO;
|
||||
goto next;
|
||||
}
|
||||
|
@ -2762,7 +2771,10 @@ static int get_tree_block_key(struct reloc_control *rc,
|
|||
BUG_ON(block->key_ready);
|
||||
eb = read_tree_block(rc->extent_root, block->bytenr,
|
||||
block->key.objectid, block->key.offset);
|
||||
BUG_ON(!eb);
|
||||
if (!eb || !extent_buffer_uptodate(eb)) {
|
||||
free_extent_buffer(eb);
|
||||
return -EIO;
|
||||
}
|
||||
WARN_ON(btrfs_header_level(eb) != block->level);
|
||||
if (block->level == 0)
|
||||
btrfs_item_key_to_cpu(eb, &block->key, 0);
|
||||
|
|
Загрузка…
Ссылка в новой задаче