OSDN Git Service

Btrfs: fix removal logic of the tree mod log that leads to use-after-free issues
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / btrfs / ctree.c
index 5b8e235..62caf3b 100644 (file)
@@ -425,7 +425,7 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
        for (node = rb_first(tm_root); node; node = next) {
                next = rb_next(node);
                tm = container_of(node, struct tree_mod_elem, node);
-               if (tm->seq > min_seq)
+               if (tm->seq >= min_seq)
                        continue;
                rb_erase(node, tm_root);
                kfree(tm);
@@ -1418,6 +1418,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
        struct tree_mod_elem *tm;
        struct extent_buffer *eb = NULL;
        struct extent_buffer *eb_root;
+       u64 eb_root_owner = 0;
        struct extent_buffer *old;
        struct tree_mod_root *old_root = NULL;
        u64 old_generation = 0;
@@ -1451,6 +1452,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
                        free_extent_buffer(old);
                }
        } else if (old_root) {
+               eb_root_owner = btrfs_header_owner(eb_root);
                btrfs_tree_read_unlock(eb_root);
                free_extent_buffer(eb_root);
                eb = alloc_dummy_extent_buffer(root->fs_info, logical);
@@ -1468,7 +1470,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
        if (old_root) {
                btrfs_set_header_bytenr(eb, eb->start);
                btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV);
-               btrfs_set_header_owner(eb, btrfs_header_owner(eb_root));
+               btrfs_set_header_owner(eb, eb_root_owner);
                btrfs_set_header_level(eb, old_root->level);
                btrfs_set_header_generation(eb, old_generation);
        }
@@ -1551,6 +1553,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
                       trans->transid, root->fs_info->generation);
 
        if (!should_cow_block(trans, root, buf)) {
+               trans->dirty = true;
                *cow_ret = buf;
                return 0;
        }
@@ -1725,20 +1728,6 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
        return err;
 }
 
-/*
- * The leaf data grows from end-to-front in the node.
- * this returns the address of the start of the last item,
- * which is the stop of the leaf data stack
- */
-static inline unsigned int leaf_data_end(struct btrfs_root *root,
-                                        struct extent_buffer *leaf)
-{
-       u32 nr = btrfs_header_nritems(leaf);
-       if (nr == 0)
-               return BTRFS_LEAF_DATA_SIZE(root);
-       return btrfs_item_offset_nr(leaf, nr - 1);
-}
-
 
 /*
  * search for key in the extent_buffer.  The items start at offset p,
@@ -2496,10 +2485,8 @@ read_block_for_search(struct btrfs_trans_handle *trans,
        if (p->reada)
                reada_for_search(root, p, level, slot, key->objectid);
 
-       btrfs_release_path(p);
-
        ret = -EAGAIN;
-       tmp = read_tree_block(root, blocknr, 0);
+       tmp = read_tree_block(root, blocknr, gen);
        if (!IS_ERR(tmp)) {
                /*
                 * If the read above didn't mark this buffer up to date,
@@ -2511,6 +2498,8 @@ read_block_for_search(struct btrfs_trans_handle *trans,
                        ret = -EIO;
                free_extent_buffer(tmp);
        }
+
+       btrfs_release_path(p);
        return ret;
 }
 
@@ -2768,13 +2757,17 @@ again:
                 * contention with the cow code
                 */
                if (cow) {
+                       bool last_level = (level == (BTRFS_MAX_LEVEL - 1));
+
                        /*
                         * if we don't really need to cow this block
                         * then we don't want to set the path blocking,
                         * so we test it here
                         */
-                       if (!should_cow_block(trans, root, b))
+                       if (!should_cow_block(trans, root, b)) {
+                               trans->dirty = true;
                                goto cow_done;
+                       }
 
                        /*
                         * must have write locks on this node and the
@@ -2790,9 +2783,13 @@ again:
                        }
 
                        btrfs_set_path_blocking(p);
-                       err = btrfs_cow_block(trans, root, b,
-                                             p->nodes[level + 1],
-                                             p->slots[level + 1], &b);
+                       if (last_level)
+                               err = btrfs_cow_block(trans, root, b, NULL, 0,
+                                                     &b);
+                       else
+                               err = btrfs_cow_block(trans, root, b,
+                                                     p->nodes[level + 1],
+                                                     p->slots[level + 1], &b);
                        if (err) {
                                ret = err;
                                goto done;
@@ -2969,6 +2966,10 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key,
 
 again:
        b = get_old_root(root, time_seq);
+       if (!b) {
+               ret = -EIO;
+               goto done;
+       }
        level = btrfs_header_level(b);
        p->locks[level] = BTRFS_READ_LOCK;
 
@@ -5438,6 +5439,7 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
        advance_left = advance_right = 0;
 
        while (1) {
+               cond_resched();
                if (advance_left && !left_end_reached) {
                        ret = tree_advance(left_root, left_path, &left_level,
                                        left_root_level,