Btrfs: fix all callers of read_tree_block
authorJosef Bacik <jbacik@fusionio.com>
Tue, 23 Apr 2013 18:17:42 +0000 (14:17 -0400)
committerJosef Bacik <jbacik@fusionio.com>
Mon, 6 May 2013 19:55:07 +0000 (15:55 -0400)
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>
fs/btrfs/backref.c
fs/btrfs/ctree.c
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/relocation.c

index 23e927b191c9ea95ec26514b75f74876c08978ad..04b5b3093893c6328c62054e299a21b230d96978 100644 (file)
@@ -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;
index 566d99b51befe6cb9cacc6c9521bbb6f577b586e..2bc34408872d70a86c8bdfe065d17286969392c9 100644 (file)
@@ -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;
 }
 
 /*
index c7d8fb0dbff6686dbfbf4a4cde579932c9db0c3a..deb6e3d07281fc239e6852dfd34083bf0f3885a9 100644 (file)
@@ -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) {
index 94bed61b799f8d1253610987e7aa52a6c5258704..6526f1faf6c2a9e6fc402b186f4528d1be440054 100644 (file)
@@ -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);
        }
index 208154986c4cfdc367a4da7e608b58d411957732..63cdd9246c707452bbf077d091945470193558f5 100644 (file)
@@ -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);