/* given a node and slot number, this reads the blocks it points to.  The
  * extent buffer is returned with a reference taken (but unlocked).
- * NULL is returned on error.
  */
 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))
-               return NULL;
+       if (slot < 0 || slot >= btrfs_header_nritems(parent))
+               return ERR_PTR(-ENOENT);
 
        BUG_ON(level == 0);
 
        eb = read_tree_block(root, btrfs_node_blockptr(parent, slot),
                             btrfs_node_ptr_generation(parent, slot));
-       if (IS_ERR(eb) || !extent_buffer_uptodate(eb)) {
-               if (!IS_ERR(eb))
-                       free_extent_buffer(eb);
-               eb = NULL;
+       if (!IS_ERR(eb) && !extent_buffer_uptodate(eb)) {
+               free_extent_buffer(eb);
+               eb = ERR_PTR(-EIO);
        }
 
        return eb;
 
                /* promote the child to a root */
                child = read_node_slot(root, mid, 0);
-               if (!child) {
-                       ret = -EROFS;
+               if (IS_ERR(child)) {
+                       ret = PTR_ERR(child);
                        btrfs_handle_fs_error(root->fs_info, ret, NULL);
                        goto enospc;
                }
                return 0;
 
        left = read_node_slot(root, parent, pslot - 1);
+       if (IS_ERR(left))
+               left = NULL;
+
        if (left) {
                btrfs_tree_lock(left);
                btrfs_set_lock_blocking(left);
                        goto enospc;
                }
        }
+
        right = read_node_slot(root, parent, pslot + 1);
+       if (IS_ERR(right))
+               right = NULL;
+
        if (right) {
                btrfs_tree_lock(right);
                btrfs_set_lock_blocking(right);
                return 1;
 
        left = read_node_slot(root, parent, pslot - 1);
+       if (IS_ERR(left))
+               left = NULL;
 
        /* first, try to make some room in the middle buffer */
        if (left) {
                free_extent_buffer(left);
        }
        right = read_node_slot(root, parent, pslot + 1);
+       if (IS_ERR(right))
+               right = NULL;
 
        /*
         * then try to empty the right most buffer into the middle
        btrfs_assert_tree_locked(path->nodes[1]);
 
        right = read_node_slot(root, upper, slot + 1);
-       if (right == NULL)
+       /*
+        * slot + 1 is not valid or we fail to read the right node,
+        * no big deal, just return.
+        */
+       if (IS_ERR(right))
                return 1;
 
        btrfs_tree_lock(right);
        btrfs_assert_tree_locked(path->nodes[1]);
 
        left = read_node_slot(root, path->nodes[1], slot - 1);
-       if (left == NULL)
+       /*
+        * slot - 1 is not valid or we fail to read the left node,
+        * no big deal, just return.
+        */
+       if (IS_ERR(left))
                return 1;
 
        btrfs_tree_lock(left);
                }
                btrfs_set_path_blocking(path);
                cur = read_node_slot(root, cur, slot);
-               BUG_ON(!cur); /* -ENOMEM */
+               if (IS_ERR(cur)) {
+                       ret = PTR_ERR(cur);
+                       goto out;
+               }
 
                btrfs_tree_read_lock(cur);
 
        return ret;
 }
 
-static void tree_move_down(struct btrfs_root *root,
+static int tree_move_down(struct btrfs_root *root,
                           struct btrfs_path *path,
                           int *level, int root_level)
 {
+       struct extent_buffer *eb;
+
        BUG_ON(*level == 0);
-       path->nodes[*level - 1] = read_node_slot(root, path->nodes[*level],
-                                       path->slots[*level]);
+       eb = read_node_slot(root, path->nodes[*level], path->slots[*level]);
+       if (IS_ERR(eb))
+               return PTR_ERR(eb);
+
+       path->nodes[*level - 1] = eb;
        path->slots[*level - 1] = 0;
        (*level)--;
+       return 0;
 }
 
 static int tree_move_next_or_upnext(struct btrfs_root *root,
        if (*level == 0 || !allow_down) {
                ret = tree_move_next_or_upnext(root, path, level, root_level);
        } else {
-               tree_move_down(root, path, level, root_level);
-               ret = 0;
+               ret = tree_move_down(root, path, level, root_level);
        }
        if (ret >= 0) {
                if (*level == 0)
                                        left_root_level,
                                        advance_left != ADVANCE_ONLY_NEXT,
                                        &left_key);
-                       if (ret < 0)
+                       if (ret == -1)
                                left_end_reached = ADVANCE;
+                       else if (ret < 0)
+                               goto out;
                        advance_left = 0;
                }
                if (advance_right && !right_end_reached) {
                                        right_root_level,
                                        advance_right != ADVANCE_ONLY_NEXT,
                                        &right_key);
-                       if (ret < 0)
+                       if (ret == -1)
                                right_end_reached = ADVANCE;
+                       else if (ret < 0)
+                               goto out;
                        advance_right = 0;
                }