Btrfs progs v4.13.3
[platform/upstream/btrfs-progs.git] / cmds-check.c
index 6b00201..5c822b8 100644 (file)
@@ -66,7 +66,6 @@ static u64 total_extent_tree_bytes = 0;
 static u64 btree_space_waste = 0;
 static u64 data_bytes_allocated = 0;
 static u64 data_bytes_referenced = 0;
-static int found_old_backref = 0;
 static LIST_HEAD(duplicate_extents);
 static LIST_HEAD(delete_items);
 static int no_holes = 0;
@@ -86,7 +85,7 @@ enum btrfs_check_mode {
 static enum btrfs_check_mode check_mode = CHECK_MODE_DEFAULT;
 
 struct extent_backref {
-       struct list_head list;
+       struct rb_node node;
        unsigned int is_data:1;
        unsigned int found_extent_tree:1;
        unsigned int full_backref:1;
@@ -94,9 +93,9 @@ struct extent_backref {
        unsigned int broken:1;
 };
 
-static inline struct extent_backref* to_extent_backref(struct list_head *entry)
+static inline struct extent_backref* rb_node_to_extent_backref(struct rb_node *node)
 {
-       return list_entry(entry, struct extent_backref, list);
+       return rb_entry(node, struct extent_backref, node);
 }
 
 struct data_backref {
@@ -137,6 +136,51 @@ static inline struct data_backref* to_data_backref(struct extent_backref *back)
        return container_of(back, struct data_backref, node);
 }
 
+static int compare_data_backref(struct rb_node *node1, struct rb_node *node2)
+{
+       struct extent_backref *ext1 = rb_node_to_extent_backref(node1);
+       struct extent_backref *ext2 = rb_node_to_extent_backref(node2);
+       struct data_backref *back1 = to_data_backref(ext1);
+       struct data_backref *back2 = to_data_backref(ext2);
+
+       WARN_ON(!ext1->is_data);
+       WARN_ON(!ext2->is_data);
+
+       /* parent and root are a union, so this covers both */
+       if (back1->parent > back2->parent)
+               return 1;
+       if (back1->parent < back2->parent)
+               return -1;
+
+       /* This is a full backref and the parents match. */
+       if (back1->node.full_backref)
+               return 0;
+
+       if (back1->owner > back2->owner)
+               return 1;
+       if (back1->owner < back2->owner)
+               return -1;
+
+       if (back1->offset > back2->offset)
+               return 1;
+       if (back1->offset < back2->offset)
+               return -1;
+
+       if (back1->found_ref && back2->found_ref) {
+               if (back1->disk_bytenr > back2->disk_bytenr)
+                       return 1;
+               if (back1->disk_bytenr < back2->disk_bytenr)
+                       return -1;
+
+               if (back1->bytes > back2->bytes)
+                       return 1;
+               if (back1->bytes < back2->bytes)
+                       return -1;
+       }
+
+       return 0;
+}
+
 /*
  * Much like data_backref, just removed the undetermined members
  * and change it to use list_head.
@@ -165,12 +209,54 @@ static inline struct tree_backref* to_tree_backref(struct extent_backref *back)
        return container_of(back, struct tree_backref, node);
 }
 
+static int compare_tree_backref(struct rb_node *node1, struct rb_node *node2)
+{
+       struct extent_backref *ext1 = rb_node_to_extent_backref(node1);
+       struct extent_backref *ext2 = rb_node_to_extent_backref(node2);
+       struct tree_backref *back1 = to_tree_backref(ext1);
+       struct tree_backref *back2 = to_tree_backref(ext2);
+
+       WARN_ON(ext1->is_data);
+       WARN_ON(ext2->is_data);
+
+       /* parent and root are a union, so this covers both */
+       if (back1->parent > back2->parent)
+               return 1;
+       if (back1->parent < back2->parent)
+               return -1;
+
+       return 0;
+}
+
+static int compare_extent_backref(struct rb_node *node1, struct rb_node *node2)
+{
+       struct extent_backref *ext1 = rb_node_to_extent_backref(node1);
+       struct extent_backref *ext2 = rb_node_to_extent_backref(node2);
+
+       if (ext1->is_data > ext2->is_data)
+               return 1;
+
+       if (ext1->is_data < ext2->is_data)
+               return -1;
+
+       if (ext1->full_backref > ext2->full_backref)
+               return 1;
+       if (ext1->full_backref < ext2->full_backref)
+               return -1;
+
+       if (ext1->is_data)
+               return compare_data_backref(node1, node2);
+       else
+               return compare_tree_backref(node1, node2);
+}
+
 /* Explicit initialization for extent_record::flag_block_full_backref */
 enum { FLAG_UNSET = 2 };
 
 struct extent_record {
        struct list_head backrefs;
        struct list_head dups;
+       struct rb_root backref_tree;
        struct list_head list;
        struct cache_extent cache;
        struct btrfs_disk_key parent_key;
@@ -226,7 +312,6 @@ struct root_item_record {
        u64 last_snapshot;
        u8 level;
        u8 drop_level;
-       int level_size;
        struct btrfs_key drop_key;
 };
 
@@ -829,7 +914,8 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
                }
                if (!found)
                        fprintf(stderr, "\tstart: 0, len: %llu\n",
-                               round_up(rec->isize, root->sectorsize));
+                               round_up(rec->isize,
+                                        root->fs_info->sectorsize));
        }
 }
 
@@ -1512,15 +1598,29 @@ static int process_dir_item(struct extent_buffer *eb,
                filetype = btrfs_dir_type(eb, di);
 
                rec->found_size += name_len;
-               if (name_len <= BTRFS_NAME_LEN) {
+               if (cur + sizeof(*di) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
+                       error = REF_ERR_NAME_TOO_LONG;
+
+                       if (cur + sizeof(*di) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*di),
+                                   BTRFS_NAME_LEN);
+               } else {
                        len = name_len;
                        error = 0;
-               } else {
-                       len = BTRFS_NAME_LEN;
-                       error = REF_ERR_NAME_TOO_LONG;
                }
+
                read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len);
 
+               if (key->type == BTRFS_DIR_ITEM_KEY &&
+                   key->offset != btrfs_name_hash(namebuf, len)) {
+                       rec->errors |= I_ERR_ODD_DIR_ITEM;
+                       error("DIR_ITEM[%llu %llu] name %s namelen %u filetype %u mismatch with its hash, wanted %llu have %llu",
+                       key->objectid, key->offset, namebuf, len, filetype,
+                       key->offset, btrfs_name_hash(namebuf, len));
+               }
+
                if (location.type == BTRFS_INODE_ITEM_KEY) {
                        add_inode_backref(inode_cache, location.objectid,
                                          key->objectid, key->offset, namebuf,
@@ -1569,13 +1669,22 @@ static int process_inode_ref(struct extent_buffer *eb,
        while (cur < total) {
                name_len = btrfs_inode_ref_name_len(eb, ref);
                index = btrfs_inode_ref_index(eb, ref);
-               if (name_len <= BTRFS_NAME_LEN) {
+
+               /* inode_ref + namelen should not cross item boundary */
+               if (cur + sizeof(*ref) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
+                       if (total < cur + sizeof(*ref))
+                               break;
+
+                       /* Still try to read out the remaining part */
+                       len = min_t(u32, total - cur - sizeof(*ref),
+                                   BTRFS_NAME_LEN);
+                       error = REF_ERR_NAME_TOO_LONG;
+               } else {
                        len = name_len;
                        error = 0;
-               } else {
-                       len = BTRFS_NAME_LEN;
-                       error = REF_ERR_NAME_TOO_LONG;
                }
+
                read_extent_buffer(eb, namebuf, (unsigned long)(ref + 1), len);
                add_inode_backref(inode_cache, key->objectid, key->offset,
                                  index, namebuf, len, 0, key->type, error);
@@ -1684,7 +1793,8 @@ static int count_csum_range(struct btrfs_root *root, u64 start,
                        start = key.offset;
 
                size = btrfs_item_size_nr(leaf, path.slots[0]);
-               csum_end = key.offset + (size / csum_size) * root->sectorsize;
+               csum_end = key.offset + (size / csum_size) *
+                          root->fs_info->sectorsize;
                if (csum_end > start) {
                        size = min(csum_end - start, len);
                        len -= size;
@@ -1711,7 +1821,7 @@ static int process_file_extent(struct btrfs_root *root,
        u64 num_bytes = 0;
        u64 disk_bytenr = 0;
        u64 extent_offset = 0;
-       u64 mask = root->sectorsize - 1;
+       u64 mask = root->fs_info->sectorsize - 1;
        int extent_type;
        int ret;
 
@@ -1950,10 +2060,10 @@ out:
 static void reada_walk_down(struct btrfs_root *root,
                            struct extent_buffer *node, int slot)
 {
+       struct btrfs_fs_info *fs_info = root->fs_info;
        u64 bytenr;
        u64 ptr_gen;
        u32 nritems;
-       u32 blocksize;
        int i;
        int level;
 
@@ -1962,11 +2072,10 @@ static void reada_walk_down(struct btrfs_root *root,
                return;
 
        nritems = btrfs_header_nritems(node);
-       blocksize = root->nodesize;
        for (i = slot; i < nritems; i++) {
                bytenr = btrfs_node_blockptr(node, i);
                ptr_gen = btrfs_node_ptr_generation(node, i);
-               readahead_tree_block(root, bytenr, blocksize, ptr_gen);
+               readahead_tree_block(fs_info, bytenr, ptr_gen);
        }
 }
 
@@ -2087,9 +2196,9 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
        enum btrfs_tree_block_status status;
        u64 bytenr;
        u64 ptr_gen;
+       struct btrfs_fs_info *fs_info = root->fs_info;
        struct extent_buffer *next;
        struct extent_buffer *cur;
-       u32 blocksize;
        int ret, err = 0;
        u64 refs;
 
@@ -2138,7 +2247,6 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
                }
                bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
                ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
-               blocksize = root->nodesize;
 
                if (bytenr == nrefs->bytenr[*level - 1]) {
                        refs = nrefs->refs[*level - 1];
@@ -2162,12 +2270,11 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
                        }
                }
 
-               next = btrfs_find_tree_block(root, bytenr, blocksize);
+               next = btrfs_find_tree_block(fs_info, bytenr, fs_info->nodesize);
                if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
                        free_extent_buffer(next);
                        reada_walk_down(root, cur, path->slots[*level]);
-                       next = read_tree_block(root, bytenr, blocksize,
-                                              ptr_gen);
+                       next = read_tree_block(root->fs_info, bytenr, ptr_gen);
                        if (!extent_buffer_uptodate(next)) {
                                struct btrfs_key node_key;
 
@@ -2177,7 +2284,8 @@ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path,
                                btrfs_add_corrupt_extent_record(root->fs_info,
                                                &node_key,
                                                path->nodes[*level]->start,
-                                               root->nodesize, *level);
+                                               root->fs_info->nodesize,
+                                               *level);
                                err = -EIO;
                                goto out;
                        }
@@ -2224,9 +2332,9 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
        enum btrfs_tree_block_status status;
        u64 bytenr;
        u64 ptr_gen;
+       struct btrfs_fs_info *fs_info = root->fs_info;
        struct extent_buffer *next;
        struct extent_buffer *cur;
-       u32 blocksize;
        int ret;
 
        WARN_ON(*level < 0);
@@ -2266,7 +2374,6 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
                }
                bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
                ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
-               blocksize = root->nodesize;
 
                ret = update_nodes_refs(root, bytenr, nrefs, *level - 1);
                if (ret)
@@ -2276,22 +2383,22 @@ static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
                        continue;
                }
 
-               next = btrfs_find_tree_block(root, bytenr, blocksize);
+               next = btrfs_find_tree_block(fs_info, bytenr, fs_info->nodesize);
                if (!next || !btrfs_buffer_uptodate(next, ptr_gen)) {
                        free_extent_buffer(next);
                        reada_walk_down(root, cur, path->slots[*level]);
-                       next = read_tree_block(root, bytenr, blocksize,
-                                              ptr_gen);
+                       next = read_tree_block(fs_info, bytenr, ptr_gen);
                        if (!extent_buffer_uptodate(next)) {
                                struct btrfs_key node_key;
 
                                btrfs_node_key_to_cpu(path->nodes[*level],
                                                      &node_key,
                                                      path->slots[*level]);
-                               btrfs_add_corrupt_extent_record(root->fs_info,
+                               btrfs_add_corrupt_extent_record(fs_info,
                                                &node_key,
                                                path->nodes[*level]->start,
-                                               root->nodesize, *level);
+                                               fs_info->nodesize,
+                                               *level);
                                ret = -EIO;
                                break;
                        }
@@ -3217,7 +3324,8 @@ static int repair_inode_discount_extent(struct btrfs_trans_handle *trans,
        /* special case for a file losing all its file extent */
        if (!found) {
                ret = btrfs_punch_hole(trans, root, rec->ino, 0,
-                                      round_up(rec->isize, root->sectorsize));
+                                      round_up(rec->isize,
+                                               root->fs_info->sectorsize));
                if (ret < 0)
                        goto out;
        }
@@ -3837,9 +3945,9 @@ static int repair_btree(struct btrfs_root *root,
                 * return value is not concerned.
                 */
                btrfs_release_path(&path);
-               ret = btrfs_free_extent(trans, root, offset, root->nodesize,
-                                       0, root->root_key.objectid,
-                                       level - 1, 0);
+               ret = btrfs_free_extent(trans, root, offset,
+                               root->fs_info->nodesize, 0,
+                               root->root_key.objectid, level - 1, 0);
                cache = next_cache_extent(cache);
        }
 
@@ -4029,7 +4137,7 @@ static int fs_root_objectid(u64 objectid)
        return is_fstree(objectid);
 }
 
-static int check_fs_roots(struct btrfs_root *root,
+static int check_fs_roots(struct btrfs_fs_info *fs_info,
                          struct cache_tree *root_cache)
 {
        struct btrfs_path path;
@@ -4037,7 +4145,7 @@ static int check_fs_roots(struct btrfs_root *root,
        struct walk_control wc;
        struct extent_buffer *leaf, *tree_node;
        struct btrfs_root *tmp_root;
-       struct btrfs_root *tree_root = root->fs_info->tree_root;
+       struct btrfs_root *tree_root = fs_info->tree_root;
        int ret;
        int err = 0;
 
@@ -4051,7 +4159,7 @@ static int check_fs_roots(struct btrfs_root *root,
         * reflected into the free space cache yet.
         */
        if (repair)
-               reset_cached_block_groups(root->fs_info);
+               reset_cached_block_groups(fs_info);
        memset(&wc, 0, sizeof(wc));
        cache_tree_init(&wc.shared);
        btrfs_init_path(&path);
@@ -4087,11 +4195,11 @@ again:
                    fs_root_objectid(key.objectid)) {
                        if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
                                tmp_root = btrfs_read_fs_root_no_cache(
-                                               root->fs_info, &key);
+                                               fs_info, &key);
                        } else {
                                key.offset = (u64)-1;
                                tmp_root = btrfs_read_fs_root(
-                                               root->fs_info, &key);
+                                               fs_info, &key);
                        }
                        if (IS_ERR(tmp_root)) {
                                err = 1;
@@ -4226,16 +4334,22 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *ref_key,
                if (imode_to_type(mode) != filetype)
                        goto next;
 
-               if (name_len <= BTRFS_NAME_LEN) {
-                       len = name_len;
-               } else {
-                       len = BTRFS_NAME_LEN;
+               if (cur + sizeof(*di) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
                        warning("root %llu %s[%llu %llu] name too long %u, trimmed",
-                       root->objectid,
-                       key->type == BTRFS_DIR_ITEM_KEY ?
-                       "DIR_ITEM" : "DIR_INDEX",
-                       key->objectid, key->offset, name_len);
+                               root->objectid,
+                               key->type == BTRFS_DIR_ITEM_KEY ?
+                               "DIR_ITEM" : "DIR_INDEX",
+                               key->objectid, key->offset, name_len);
+
+                       if (cur + sizeof(*di) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*di),
+                                   BTRFS_NAME_LEN);
+               } else {
+                       len = name_len;
                }
+
                read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len);
                if (len != namelen || strncmp(namebuf, name, len))
                        goto next;
@@ -4296,12 +4410,16 @@ next:
 
        index = btrfs_inode_ref_index(node, ref);
        name_len = btrfs_inode_ref_name_len(node, ref);
-       if (name_len <= BTRFS_NAME_LEN) {
-               len = name_len;
-       } else {
-               len = BTRFS_NAME_LEN;
+       if (cur + sizeof(*ref) + name_len > total ||
+           name_len > BTRFS_NAME_LEN) {
                warning("root %llu INODE_REF[%llu %llu] name too long",
                        root->objectid, ref_key->objectid, ref_key->offset);
+
+               if (total < cur + sizeof(*ref))
+                       goto out;
+               len = min_t(u32, total - cur - sizeof(*ref), BTRFS_NAME_LEN);
+       } else {
+               len = name_len;
        }
 
        read_extent_buffer(node, namebuf, (unsigned long)(ref + 1), len);
@@ -4334,6 +4452,7 @@ next:
        if (cur < total)
                goto next;
 
+out:
        return err;
 }
 
@@ -4471,16 +4590,22 @@ static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
                if (index != (u64)-1 && index != ref_index)
                        goto next_ref;
 
-               if (ref_namelen <= BTRFS_NAME_LEN) {
-                       len = ref_namelen;
-               } else {
-                       len = BTRFS_NAME_LEN;
+               if (cur + sizeof(*ref) + ref_namelen > total ||
+                   ref_namelen > BTRFS_NAME_LEN) {
                        warning("root %llu INODE %s[%llu %llu] name too long",
                                root->objectid,
                                key->type == BTRFS_INODE_REF_KEY ?
                                        "REF" : "EXTREF",
                                key->objectid, key->offset);
+
+                       if (cur + sizeof(*ref) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*ref),
+                                   BTRFS_NAME_LEN);
+               } else {
+                       len = ref_namelen;
                }
+
                read_extent_buffer(node, ref_namebuf, (unsigned long)(ref + 1),
                                   len);
 
@@ -4612,21 +4737,35 @@ static int check_dir_item(struct btrfs_root *root, struct btrfs_key *key,
                              key->objectid, key->offset, data_len);
 
                name_len = btrfs_dir_name_len(node, di);
-               if (name_len <= BTRFS_NAME_LEN) {
-                       len = name_len;
-               } else {
-                       len = BTRFS_NAME_LEN;
+               if (cur + sizeof(*di) + name_len > total ||
+                   name_len > BTRFS_NAME_LEN) {
                        warning("root %llu %s[%llu %llu] name too long",
                                root->objectid,
                                key->type == BTRFS_DIR_ITEM_KEY ?
                                "DIR_ITEM" : "DIR_INDEX",
                                key->objectid, key->offset);
+
+                       if (cur + sizeof(*di) > total)
+                               break;
+                       len = min_t(u32, total - cur - sizeof(*di),
+                                   BTRFS_NAME_LEN);
+               } else {
+                       len = name_len;
                }
                (*size) += name_len;
 
                read_extent_buffer(node, namebuf, (unsigned long)(di + 1), len);
                filetype = btrfs_dir_type(node, di);
 
+               if (key->type == BTRFS_DIR_ITEM_KEY &&
+                   key->offset != btrfs_name_hash(namebuf, len)) {
+                       err |= -EIO;
+                       error("root %llu DIR_ITEM[%llu %llu] name %s namelen %u filetype %u mismatch with its hash, wanted %llu have %llu",
+                               root->objectid, key->objectid, key->offset,
+                               namebuf, len, filetype, key->offset,
+                               btrfs_name_hash(namebuf, len));
+               }
+
                btrfs_init_path(&path);
                btrfs_dir_item_key_to_cpu(node, di, &location);
 
@@ -4740,6 +4879,7 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
                                extent_num_bytes, item_inline_len);
                        err |= FILE_EXTENT_ERROR;
                }
+               *end += extent_num_bytes;
                *size += extent_num_bytes;
                return err;
        }
@@ -4799,11 +4939,7 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
        }
 
        /* Check EXTENT_DATA hole */
-       if (no_holes && is_hole) {
-               err |= FILE_EXTENT_ERROR;
-               error("root %llu EXTENT_DATA[%llu %llu] shouldn't be hole",
-                     root->objectid, fkey->objectid, fkey->offset);
-       } else if (!no_holes && *end != fkey->offset) {
+       if (!no_holes && *end != fkey->offset) {
                err |= FILE_EXTENT_ERROR;
                error("root %llu EXTENT_DATA[%llu %llu] interrupt",
                      root->objectid, fkey->objectid, fkey->offset);
@@ -4944,9 +5080,10 @@ out:
                 * Just a warning, as dir inode nbytes is just an
                 * instructive value.
                 */
-               if (!IS_ALIGNED(nbytes, root->nodesize)) {
+               if (!IS_ALIGNED(nbytes, root->fs_info->nodesize)) {
                        warning("root %llu DIR INODE[%llu] nbytes should be aligned to %u",
-                               root->objectid, inode_id, root->nodesize);
+                               root->objectid, inode_id,
+                               root->fs_info->nodesize);
                }
 
                if (isize != size) {
@@ -5017,6 +5154,65 @@ out:
        return ret;
 }
 
+static struct tree_backref *find_tree_backref(struct extent_record *rec,
+                                               u64 parent, u64 root)
+{
+       struct rb_node *node;
+       struct tree_backref *back = NULL;
+       struct tree_backref match = {
+               .node = {
+                       .is_data = 0,
+               },
+       };
+
+       if (parent) {
+               match.parent = parent;
+               match.node.full_backref = 1;
+       } else {
+               match.root = root;
+       }
+
+       node = rb_search(&rec->backref_tree, &match.node.node,
+                        (rb_compare_keys)compare_extent_backref, NULL);
+       if (node)
+               back = to_tree_backref(rb_node_to_extent_backref(node));
+
+       return back;
+}
+
+static struct data_backref *find_data_backref(struct extent_record *rec,
+                                               u64 parent, u64 root,
+                                               u64 owner, u64 offset,
+                                               int found_ref,
+                                               u64 disk_bytenr, u64 bytes)
+{
+       struct rb_node *node;
+       struct data_backref *back = NULL;
+       struct data_backref match = {
+               .node = {
+                       .is_data = 1,
+               },
+               .owner = owner,
+               .offset = offset,
+               .bytes = bytes,
+               .found_ref = found_ref,
+               .disk_bytenr = disk_bytenr,
+       };
+
+       if (parent) {
+               match.parent = parent;
+               match.node.full_backref = 1;
+       } else {
+               match.root = root;
+       }
+
+       node = rb_search(&rec->backref_tree, &match.node.node,
+                        (rb_compare_keys)compare_extent_backref, NULL);
+       if (node)
+               back = to_data_backref(rb_node_to_extent_backref(node));
+
+       return back;
+}
 /*
  * Iterate all item on the tree and call check_inode_item() to check.
  *
@@ -5264,25 +5460,38 @@ out:
        return err;
 }
 
+static int do_check_fs_roots(struct btrfs_fs_info *fs_info,
+                         struct cache_tree *root_cache)
+{
+       int ret;
+
+       if (!ctx.progress_enabled)
+               fprintf(stderr, "checking fs roots\n");
+       if (check_mode == CHECK_MODE_LOWMEM)
+               ret = check_fs_roots_v2(fs_info);
+       else
+               ret = check_fs_roots(fs_info, root_cache);
+
+       return ret;
+}
+
 static int all_backpointers_checked(struct extent_record *rec, int print_errs)
 {
-       struct list_head *cur = rec->backrefs.next;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct tree_backref *tback;
        struct data_backref *dback;
        u64 found = 0;
        int err = 0;
 
-       while(cur != &rec->backrefs) {
-               back = to_extent_backref(cur);
-               cur = cur->next;
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (!back->found_extent_tree) {
                        err = 1;
                        if (!print_errs)
                                goto out;
                        if (back->is_data) {
                                dback = to_data_backref(back);
-                               fprintf(stderr, "Backref %llu %s %llu"
+                               fprintf(stderr, "Data backref %llu %s %llu"
                                        " owner %llu offset %llu num_refs %lu"
                                        " not found in extent tree\n",
                                        (unsigned long long)rec->start,
@@ -5296,7 +5505,7 @@ static int all_backpointers_checked(struct extent_record *rec, int print_errs)
                                        (unsigned long)dback->num_refs);
                        } else {
                                tback = to_tree_backref(back);
-                               fprintf(stderr, "Backref %llu parent %llu"
+                               fprintf(stderr, "Tree backref %llu parent %llu"
                                        " root %llu not found in extent tree\n",
                                        (unsigned long long)rec->start,
                                        (unsigned long long)tback->parent,
@@ -5378,17 +5587,16 @@ out:
        return err;
 }
 
-static int free_all_extent_backrefs(struct extent_record *rec)
+static void __free_one_backref(struct rb_node *node)
 {
-       struct extent_backref *back;
-       struct list_head *cur;
-       while (!list_empty(&rec->backrefs)) {
-               cur = rec->backrefs.next;
-               back = to_extent_backref(cur);
-               list_del(cur);
-               free(back);
-       }
-       return 0;
+       struct extent_backref *back = rb_node_to_extent_backref(node);
+
+       free(back);
+}
+
+static void free_all_extent_backrefs(struct extent_record *rec)
+{
+       rb_free_nodes(&rec->backref_tree, __free_one_backref);
 }
 
 static void free_extent_record_cache(struct cache_tree *extent_cache)
@@ -5427,7 +5635,7 @@ static int check_owner_ref(struct btrfs_root *root,
                            struct extent_record *rec,
                            struct extent_buffer *buf)
 {
-       struct extent_backref *node;
+       struct extent_backref *node, *tmp;
        struct tree_backref *back;
        struct btrfs_root *ref_root;
        struct btrfs_key key;
@@ -5437,7 +5645,8 @@ static int check_owner_ref(struct btrfs_root *root,
        int found = 0;
        int ret;
 
-       list_for_each_entry(node, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(node, tmp,
+                                            &rec->backref_tree, node) {
                if (node->is_data)
                        continue;
                if (!node->found_ref)
@@ -5482,14 +5691,12 @@ static int check_owner_ref(struct btrfs_root *root,
 
 static int is_extent_tree_record(struct extent_record *rec)
 {
-       struct list_head *cur = rec->backrefs.next;
-       struct extent_backref *node;
+       struct extent_backref *node, *tmp;
        struct tree_backref *back;
        int is_extent = 0;
 
-       while(cur != &rec->backrefs) {
-               node = to_extent_backref(cur);
-               cur = cur->next;
+       rbtree_postorder_for_each_entry_safe(node, tmp,
+                                            &rec->backref_tree, node) {
                if (node->is_data)
                        return 0;
                back = to_tree_backref(node);
@@ -5854,6 +6061,7 @@ static int check_block(struct btrfs_root *root,
        return ret;
 }
 
+#if 0
 static struct tree_backref *find_tree_backref(struct extent_record *rec,
                                                u64 parent, u64 root)
 {
@@ -5881,6 +6089,7 @@ static struct tree_backref *find_tree_backref(struct extent_record *rec,
        }
        return NULL;
 }
+#endif
 
 static struct tree_backref *alloc_tree_backref(struct extent_record *rec,
                                                u64 parent, u64 root)
@@ -5897,11 +6106,11 @@ static struct tree_backref *alloc_tree_backref(struct extent_record *rec,
                ref->root = root;
                ref->node.full_backref = 0;
        }
-       list_add_tail(&ref->node.list, &rec->backrefs);
 
        return ref;
 }
 
+#if 0
 static struct data_backref *find_data_backref(struct extent_record *rec,
                                                u64 parent, u64 root,
                                                u64 owner, u64 offset,
@@ -5938,6 +6147,7 @@ static struct data_backref *find_data_backref(struct extent_record *rec,
        }
        return NULL;
 }
+#endif
 
 static struct data_backref *alloc_data_backref(struct extent_record *rec,
                                                u64 parent, u64 root,
@@ -5965,7 +6175,6 @@ static struct data_backref *alloc_data_backref(struct extent_record *rec,
        ref->bytes = max_size;
        ref->found_ref = 0;
        ref->num_refs = 0;
-       list_add_tail(&ref->node.list, &rec->backrefs);
        if (max_size > rec->max_size)
                rec->max_size = max_size;
        return ref;
@@ -5998,12 +6207,12 @@ static void check_extent_type(struct extent_record *rec)
         * Check SYSTEM extent, as it's also marked as metadata, we can only
         * make sure it's a SYSTEM extent by its backref
         */
-       if (!list_empty(&rec->backrefs)) {
+       if (!RB_EMPTY_ROOT(&rec->backref_tree)) {
                struct extent_backref *node;
                struct tree_backref *tback;
                u64 bg_type;
 
-               node = to_extent_backref(rec->backrefs.next);
+               node = rb_node_to_extent_backref(rb_first(&rec->backref_tree));
                if (node->is_data) {
                        /* tree block shouldn't have data backref */
                        rec->wrong_chunk_type = 1;
@@ -6054,6 +6263,7 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
        INIT_LIST_HEAD(&rec->backrefs);
        INIT_LIST_HEAD(&rec->dups);
        INIT_LIST_HEAD(&rec->list);
+       rec->backref_tree = RB_ROOT;
        memcpy(&rec->parent_key, &tmpl->parent_key, sizeof(tmpl->parent_key));
        rec->cache.start = tmpl->start;
        rec->cache.size = tmpl->nr;
@@ -6066,7 +6276,7 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
 
        if (tmpl->metadata)
                rec->crossing_stripes = check_crossing_stripes(global_info,
-                               rec->start, global_info->tree_root->nodesize);
+                               rec->start, global_info->nodesize);
        check_extent_type(rec);
        return ret;
 }
@@ -6168,7 +6378,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
                if (tmpl->metadata)
                        rec->crossing_stripes = check_crossing_stripes(
                                        global_info, rec->start,
-                                       global_info->tree_root->nodesize);
+                                       global_info->nodesize);
                check_extent_type(rec);
                maybe_free_extent_rec(extent_cache, rec);
                return ret;
@@ -6186,6 +6396,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
        struct tree_backref *back;
        struct cache_extent *cache;
        int ret;
+       bool insert = false;
 
        cache = lookup_cache_extent(extent_cache, bytenr, 1);
        if (!cache) {
@@ -6220,6 +6431,7 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
                back = alloc_tree_backref(rec, parent, root);
                if (!back)
                        return -ENOMEM;
+               insert = true;
        }
 
        if (found_ref) {
@@ -6241,6 +6453,9 @@ static int add_tree_backref(struct cache_tree *extent_cache, u64 bytenr,
                }
                back->node.found_extent_tree = 1;
        }
+       if (insert)
+               WARN_ON(rb_insert(&rec->backref_tree, &back->node.node,
+                       compare_extent_backref));
        check_extent_type(rec);
        maybe_free_extent_rec(extent_cache, rec);
        return 0;
@@ -6254,6 +6469,7 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
        struct data_backref *back;
        struct cache_extent *cache;
        int ret;
+       bool insert = false;
 
        cache = lookup_cache_extent(extent_cache, bytenr, 1);
        if (!cache) {
@@ -6293,6 +6509,7 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
                back = alloc_data_backref(rec, parent, root, owner, offset,
                                          max_size);
                BUG_ON(!back);
+               insert = true;
        }
 
        if (found_ref) {
@@ -6301,8 +6518,16 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
                        BUG_ON(back->bytes != max_size);
                back->node.found_ref = 1;
                back->found_ref += 1;
-               back->bytes = max_size;
-               back->disk_bytenr = bytenr;
+               if (back->bytes != max_size || back->disk_bytenr != bytenr) {
+                       back->bytes = max_size;
+                       back->disk_bytenr = bytenr;
+
+                       /* Need to reinsert if not already in the tree */
+                       if (!insert) {
+                               rb_erase(&back->node.node, &rec->backref_tree);
+                               insert = true;
+                       }
+               }
                rec->refs += 1;
                rec->content_checked = 1;
                rec->owner_ref_checked = 1;
@@ -6321,6 +6546,10 @@ static int add_data_backref(struct cache_tree *extent_cache, u64 bytenr,
                back->num_refs = num_refs;
                back->node.found_extent_tree = 1;
        }
+       if (insert)
+               WARN_ON(rb_insert(&rec->backref_tree, &back->node.node,
+                       compare_extent_backref));
+
        maybe_free_extent_rec(extent_cache, rec);
        return 0;
 }
@@ -6583,7 +6812,7 @@ static int process_chunk_item(struct cache_tree *chunk_cache,
         * wrong onwer(3) out of chunk tree, to pass both chunk tree check
         * and owner<->key_type check.
         */
-       ret = btrfs_check_chunk_valid(global_info->tree_root, eb, chunk, slot,
+       ret = btrfs_check_chunk_valid(global_info, eb, chunk, slot,
                                      key->offset);
        if (ret < 0) {
                error("chunk(%llu, %llu) is not valid, ignore it",
@@ -6765,14 +6994,14 @@ static int process_extent_item(struct btrfs_root *root,
 
        if (key.type == BTRFS_METADATA_ITEM_KEY) {
                metadata = 1;
-               num_bytes = root->nodesize;
+               num_bytes = root->fs_info->nodesize;
        } else {
                num_bytes = key.offset;
        }
 
-       if (!IS_ALIGNED(key.objectid, root->sectorsize)) {
+       if (!IS_ALIGNED(key.objectid, root->fs_info->sectorsize)) {
                error("ignoring invalid extent, bytenr %llu is not aligned to %u",
-                     key.objectid, root->sectorsize);
+                     key.objectid, root->fs_info->sectorsize);
                return -EIO;
        }
        if (item_size < sizeof(*ei)) {
@@ -6801,14 +7030,14 @@ static int process_extent_item(struct btrfs_root *root,
                metadata = 1;
        else
                metadata = 0;
-       if (metadata && num_bytes != root->nodesize) {
+       if (metadata && num_bytes != root->fs_info->nodesize) {
                error("ignore invalid metadata extent, length %llu does not equal to %u",
-                     num_bytes, root->nodesize);
+                     num_bytes, root->fs_info->nodesize);
                return -EIO;
        }
-       if (!metadata && !IS_ALIGNED(num_bytes, root->sectorsize)) {
+       if (!metadata && !IS_ALIGNED(num_bytes, root->fs_info->sectorsize)) {
                error("ignore invalid data extent, length %llu is not aligned to %u",
-                     num_bytes, root->sectorsize);
+                     num_bytes, root->fs_info->sectorsize);
                return -EIO;
        }
 
@@ -6889,7 +7118,7 @@ static int check_cache_range(struct btrfs_root *root,
 
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
                bytenr = btrfs_sb_offset(i);
-               ret = btrfs_rmap_block(&root->fs_info->mapping_tree,
+               ret = btrfs_rmap_block(root->fs_info,
                                       cache->key.objectid, bytenr, 0,
                                       &logical, &nr, &stripe_len);
                if (ret)
@@ -7016,7 +7245,7 @@ static int verify_space_cache(struct btrfs_root *root,
                        if (key.type == BTRFS_EXTENT_ITEM_KEY)
                                last = key.objectid + key.offset;
                        else
-                               last = key.objectid + root->nodesize;
+                               last = key.objectid + root->fs_info->nodesize;
                        path.slots[0]++;
                        continue;
                }
@@ -7028,7 +7257,7 @@ static int verify_space_cache(struct btrfs_root *root,
                if (key.type == BTRFS_EXTENT_ITEM_KEY)
                        last = key.objectid + key.offset;
                else
-                       last = key.objectid + root->nodesize;
+                       last = key.objectid + root->fs_info->nodesize;
                path.slots[0]++;
        }
 
@@ -7078,7 +7307,7 @@ static int check_space_cache(struct btrfs_root *root)
                start = cache->key.objectid + cache->key.offset;
                if (!cache->free_space_ctl) {
                        if (btrfs_init_free_space_ctl(cache,
-                                                     root->sectorsize)) {
+                                               root->fs_info->sectorsize)) {
                                ret = -ENOMEM;
                                break;
                        }
@@ -7126,8 +7355,9 @@ static int check_extent_csums(struct btrfs_root *root, u64 bytenr,
                        u64 num_bytes, unsigned long leaf_offset,
                        struct extent_buffer *eb) {
 
+       struct btrfs_fs_info *fs_info = root->fs_info;
        u64 offset = 0;
-       u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy);
+       u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
        char *data;
        unsigned long csum_offset;
        u32 csum;
@@ -7139,7 +7369,7 @@ static int check_extent_csums(struct btrfs_root *root, u64 bytenr,
        int mirror;
        int num_copies;
 
-       if (num_bytes % root->sectorsize)
+       if (num_bytes % fs_info->sectorsize)
                return -EINVAL;
 
        data = malloc(num_bytes);
@@ -7151,7 +7381,7 @@ static int check_extent_csums(struct btrfs_root *root, u64 bytenr,
 again:
                read_len = num_bytes - offset;
                /* read as much space once a time */
-               ret = read_extent_data(root, data + offset,
+               ret = read_extent_data(fs_info, data + offset,
                                bytenr + offset, &read_len, mirror);
                if (ret)
                        goto out;
@@ -7162,11 +7392,11 @@ again:
                        tmp = offset + data_checked;
 
                        csum = btrfs_csum_data((char *)data + tmp,
-                                              csum, root->sectorsize);
+                                              csum, fs_info->sectorsize);
                        btrfs_csum_final(csum, (u8 *)&csum);
 
                        csum_offset = leaf_offset +
-                                tmp / root->sectorsize * csum_size;
+                                tmp / fs_info->sectorsize * csum_size;
                        read_extent_buffer(eb, (char *)&csum_expected,
                                           csum_offset, csum_size);
                        /* try another mirror */
@@ -7174,15 +7404,14 @@ again:
                                fprintf(stderr, "mirror %d bytenr %llu csum %u expected csum %u\n",
                                                mirror, bytenr + tmp,
                                                csum, csum_expected);
-                               num_copies = btrfs_num_copies(
-                                               &root->fs_info->mapping_tree,
+                               num_copies = btrfs_num_copies(root->fs_info,
                                                bytenr, num_bytes);
                                if (mirror < num_copies - 1) {
                                        mirror += 1;
                                        goto again;
                                }
                        }
-                       data_checked += root->sectorsize;
+                       data_checked += fs_info->sectorsize;
                }
                offset += read_len;
        }
@@ -7383,7 +7612,7 @@ static int check_csums(struct btrfs_root *root)
                }
 
                data_len = (btrfs_item_size_nr(leaf, path.slots[0]) /
-                             csum_size) * root->sectorsize;
+                             csum_size) * root->fs_info->sectorsize;
                if (!check_data_csum)
                        goto skip_csum_check;
                leaf_offset = btrfs_item_ptr_offset(leaf, path.slots[0]);
@@ -7568,6 +7797,7 @@ static int run_next_block(struct btrfs_root *root,
                          struct device_extent_tree *dev_extent_cache,
                          struct root_item_record *ri)
 {
+       struct btrfs_fs_info *fs_info = root->fs_info;
        struct extent_buffer *buf;
        struct extent_record *rec = NULL;
        u64 bytenr;
@@ -7597,8 +7827,7 @@ static int run_next_block(struct btrfs_root *root,
                                continue;
 
                        /* fixme, get the parent transid */
-                       readahead_tree_block(root, bits[i].start,
-                                            bits[i].size, 0);
+                       readahead_tree_block(fs_info, bits[i].start, 0);
                }
        }
        *last = bits[0].start;
@@ -7627,7 +7856,7 @@ static int run_next_block(struct btrfs_root *root,
        }
 
        /* fixme, get the real parent transid */
-       buf = read_tree_block(root, bytenr, size, gen);
+       buf = read_tree_block(root->fs_info, bytenr, gen);
        if (!extent_buffer_uptodate(buf)) {
                record_bad_block_io(root->fs_info,
                                    extent_cache, bytenr, size);
@@ -7784,7 +8013,7 @@ static int run_next_block(struct btrfs_root *root,
                                                                       ref),
                                        btrfs_extent_data_ref_offset(buf, ref),
                                        btrfs_extent_data_ref_count(buf, ref),
-                                       0, root->sectorsize);
+                                       0, root->fs_info->sectorsize);
                                continue;
                        }
                        if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
@@ -7794,7 +8023,7 @@ static int run_next_block(struct btrfs_root *root,
                                add_data_backref(extent_cache,
                                        key.objectid, key.offset, 0, 0, 0,
                                        btrfs_shared_data_ref_count(buf, ref),
-                                       0, root->sectorsize);
+                                       0, root->fs_info->sectorsize);
                                continue;
                        }
                        if (key.type == BTRFS_ORPHAN_ITEM_KEY) {
@@ -7826,7 +8055,7 @@ static int run_next_block(struct btrfs_root *root,
 
                        data_bytes_allocated +=
                                btrfs_file_extent_disk_num_bytes(buf, fi);
-                       if (data_bytes_allocated < root->sectorsize) {
+                       if (data_bytes_allocated < root->fs_info->sectorsize) {
                                abort();
                        }
                        data_bytes_referenced +=
@@ -7850,7 +8079,7 @@ static int run_next_block(struct btrfs_root *root,
                        struct extent_record tmpl;
 
                        ptr = btrfs_node_blockptr(buf, i);
-                       size = root->nodesize;
+                       size = root->fs_info->nodesize;
                        btrfs_node_key_to_cpu(buf, &key, i);
                        if (ri != NULL) {
                                if ((level == ri->drop_level)
@@ -7894,11 +8123,6 @@ static int run_next_block(struct btrfs_root *root,
                total_fs_tree_bytes += buf->len;
        if (btrfs_header_owner(buf) == BTRFS_EXTENT_TREE_OBJECTID)
                total_extent_tree_bytes += buf->len;
-       if (!found_old_backref &&
-           btrfs_header_owner(buf) == BTRFS_TREE_RELOC_OBJECTID &&
-           btrfs_header_backref_rev(buf) == BTRFS_MIXED_BACKREF_REV &&
-           !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_RELOC))
-               found_old_backref = 1;
 out:
        free_extent_buffer(buf);
        return ret;
@@ -7981,7 +8205,7 @@ static int free_extent_hook(struct btrfs_trans_handle *trans,
                        back->node.found_extent_tree = 0;
 
                if (!back->node.found_extent_tree && back->node.found_ref) {
-                       list_del(&back->node.list);
+                       rb_erase(&back->node.node, &rec->backref_tree);
                        free(back);
                }
        } else {
@@ -8000,7 +8224,7 @@ static int free_extent_hook(struct btrfs_trans_handle *trans,
                        back->node.found_extent_tree = 0;
                }
                if (!back->node.found_extent_tree && back->node.found_ref) {
-                       list_del(&back->node.list);
+                       rb_erase(&back->node.node, &rec->backref_tree);
                        free(back);
                }
        }
@@ -8076,7 +8300,7 @@ static int delete_extent_records(struct btrfs_trans_handle *trans,
                if (found_key.type == BTRFS_EXTENT_ITEM_KEY ||
                    found_key.type == BTRFS_METADATA_ITEM_KEY) {
                        u64 bytes = (found_key.type == BTRFS_EXTENT_ITEM_KEY) ?
-                               found_key.offset : root->nodesize;
+                               found_key.offset : root->fs_info->nodesize;
 
                        ret = btrfs_update_block_group(trans, root, bytenr,
                                                       bytes, 0, 0);
@@ -8110,7 +8334,7 @@ static int record_extent(struct btrfs_trans_handle *trans,
 
        if (!back->is_data)
                rec->max_size = max_t(u64, rec->max_size,
-                                   info->extent_root->nodesize);
+                                   info->nodesize);
 
        if (!allocated) {
                u32 item_size = sizeof(*ei);
@@ -8441,7 +8665,7 @@ out:
 static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
                           struct extent_record *rec)
 {
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct data_backref *dback;
        struct extent_entry *entry, *best = NULL;
        LIST_HEAD(entries);
@@ -8457,7 +8681,8 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
        if (rec->metadata)
                return 0;
 
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (back->full_backref || !back->is_data)
                        continue;
 
@@ -8583,7 +8808,8 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
         * Ok great we all agreed on an extent record, let's go find the real
         * references and fix up the ones that don't match.
         */
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (back->full_backref || !back->is_data)
                        continue;
 
@@ -8807,7 +9033,7 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
                                  struct extent_record *rec)
 {
        struct btrfs_root *root;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct data_backref *dback;
        struct cache_extent *cache;
        struct btrfs_file_extent_item *fi;
@@ -8815,7 +9041,8 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
        u64 bytenr, bytes;
        int ret;
 
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                /* Don't care about full backrefs (poor unloved backrefs) */
                if (back->full_backref || !back->is_data)
                        continue;
@@ -8903,7 +9130,7 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_key key;
        struct btrfs_root *dest_root;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct data_backref *dback;
        struct orphan_data_extent *orphan;
        struct btrfs_path path;
@@ -8913,7 +9140,8 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
        if (rec->metadata)
                return 1;
        btrfs_init_path(&path);
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (back->full_backref || !back->is_data ||
                    !back->found_extent_tree)
                        continue;
@@ -8981,9 +9209,8 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
        struct btrfs_trans_handle *trans = NULL;
        int ret;
        struct btrfs_path path;
-       struct list_head *cur = rec->backrefs.next;
        struct cache_extent *cache;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        int allocated = 0;
        u64 flags = 0;
 
@@ -9031,10 +9258,8 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
        }
 
        /* step three, recreate all the refs we did find */
-       while(cur != &rec->backrefs) {
-               back = to_extent_backref(cur);
-               cur = cur->next;
-
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                /*
                 * if we didn't find any references, don't create a
                 * new extent record
@@ -9416,7 +9641,9 @@ repair_abort:
                                goto repair_abort;
                        }
 
-                       btrfs_fix_block_accounting(trans, root);
+                       ret = btrfs_fix_block_accounting(trans, root);
+                       if (ret)
+                               goto repair_abort;
                        ret = btrfs_commit_transaction(trans, root);
                        if (ret)
                                goto repair_abort;
@@ -9685,7 +9912,7 @@ static int check_devices(struct rb_root *dev_cache,
 static int add_root_item_to_list(struct list_head *head,
                                  u64 objectid, u64 bytenr, u64 last_snapshot,
                                  u8 level, u8 drop_level,
-                                 int level_size, struct btrfs_key *drop_key)
+                                 struct btrfs_key *drop_key)
 {
 
        struct root_item_record *ri_rec;
@@ -9695,7 +9922,6 @@ static int add_root_item_to_list(struct list_head *head,
        ri_rec->bytenr = bytenr;
        ri_rec->objectid = objectid;
        ri_rec->level = level;
-       ri_rec->level_size = level_size;
        ri_rec->drop_level = drop_level;
        ri_rec->last_snapshot = last_snapshot;
        if (drop_key)
@@ -9740,8 +9966,7 @@ static int deal_root_from_list(struct list_head *list,
                rec = list_entry(list->next,
                                 struct root_item_record, list);
                last = 0;
-               buf = read_tree_block(root->fs_info->tree_root,
-                                     rec->bytenr, rec->level_size, 0);
+               buf = read_tree_block(root->fs_info, rec->bytenr, 0);
                if (!extent_buffer_uptodate(buf)) {
                        free_extent_buffer(buf);
                        ret = -EIO;
@@ -9785,7 +10010,7 @@ static int deal_root_from_list(struct list_head *list,
        return ret;
 }
 
-static int check_chunks_and_extents(struct btrfs_root *root)
+static int check_chunks_and_extents(struct btrfs_fs_info *fs_info)
 {
        struct rb_root dev_cache;
        struct cache_tree chunk_cache;
@@ -9810,10 +10035,11 @@ static int check_chunks_and_extents(struct btrfs_root *root)
        struct list_head dropping_trees;
        struct list_head normal_trees;
        struct btrfs_root *root1;
+       struct btrfs_root *root;
        u64 objectid;
-       u32 level_size;
        u8 level;
 
+       root = fs_info->fs_root;
        dev_cache = RB_ROOT;
        cache_tree_init(&chunk_cache);
        block_group_tree_init(&block_group_cache);
@@ -9830,10 +10056,10 @@ static int check_chunks_and_extents(struct btrfs_root *root)
        INIT_LIST_HEAD(&normal_trees);
 
        if (repair) {
-               root->fs_info->excluded_extents = &excluded_extents;
-               root->fs_info->fsck_extent_cache = &extent_cache;
-               root->fs_info->free_extent_hook = free_extent_hook;
-               root->fs_info->corrupt_blocks = &corrupt_blocks;
+               fs_info->excluded_extents = &excluded_extents;
+               fs_info->fsck_extent_cache = &extent_cache;
+               fs_info->free_extent_hook = free_extent_hook;
+               fs_info->corrupt_blocks = &corrupt_blocks;
        }
 
        bits_nr = 1024;
@@ -9849,26 +10075,23 @@ static int check_chunks_and_extents(struct btrfs_root *root)
        }
 
 again:
-       root1 = root->fs_info->tree_root;
+       root1 = fs_info->tree_root;
        level = btrfs_header_level(root1->node);
        ret = add_root_item_to_list(&normal_trees, root1->root_key.objectid,
-                                   root1->node->start, 0, level, 0,
-                                   root1->nodesize, NULL);
+                                   root1->node->start, 0, level, 0, NULL);
        if (ret < 0)
                goto out;
-       root1 = root->fs_info->chunk_root;
+       root1 = fs_info->chunk_root;
        level = btrfs_header_level(root1->node);
        ret = add_root_item_to_list(&normal_trees, root1->root_key.objectid,
-                                   root1->node->start, 0, level, 0,
-                                   root1->nodesize, NULL);
+                                   root1->node->start, 0, level, 0, NULL);
        if (ret < 0)
                goto out;
        btrfs_init_path(&path);
        key.offset = 0;
        key.objectid = 0;
        key.type = BTRFS_ROOT_ITEM_KEY;
-       ret = btrfs_search_slot(NULL, root->fs_info->tree_root,
-                                       &key, &path, 0, 0);
+       ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
        if (ret < 0)
                goto out;
        while(1) {
@@ -9891,17 +10114,15 @@ again:
                        last_snapshot = btrfs_root_last_snapshot(&ri);
                        if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) {
                                level = btrfs_root_level(&ri);
-                               level_size = root->nodesize;
                                ret = add_root_item_to_list(&normal_trees,
                                                found_key.objectid,
                                                btrfs_root_bytenr(&ri),
                                                last_snapshot, level,
-                                               0, level_size, NULL);
+                                               0, NULL);
                                if (ret < 0)
                                        goto out;
                        } else {
                                level = btrfs_root_level(&ri);
-                               level_size = root->nodesize;
                                objectid = found_key.objectid;
                                btrfs_disk_key_to_cpu(&found_key,
                                                      &ri.drop_progress);
@@ -9909,8 +10130,7 @@ again:
                                                objectid,
                                                btrfs_root_bytenr(&ri),
                                                last_snapshot, level,
-                                               ri.drop_level,
-                                               level_size, &found_key);
+                                               ri.drop_level, &found_key);
                                if (ret < 0)
                                        goto out;
                        }
@@ -9965,12 +10185,12 @@ again:
 out:
        task_stop(ctx.info);
        if (repair) {
-               free_corrupt_blocks_tree(root->fs_info->corrupt_blocks);
+               free_corrupt_blocks_tree(fs_info->corrupt_blocks);
                extent_io_tree_cleanup(&excluded_extents);
-               root->fs_info->fsck_extent_cache = NULL;
-               root->fs_info->free_extent_hook = NULL;
-               root->fs_info->corrupt_blocks = NULL;
-               root->fs_info->excluded_extents = NULL;
+               fs_info->fsck_extent_cache = NULL;
+               fs_info->free_extent_hook = NULL;
+               fs_info->corrupt_blocks = NULL;
+               fs_info->excluded_extents = NULL;
        }
        free(bits);
        free_chunk_cache_tree(&chunk_cache);
@@ -9985,7 +10205,7 @@ out:
        free_root_item_list(&dropping_trees);
        return ret;
 loop:
-       free_corrupt_blocks_tree(root->fs_info->corrupt_blocks);
+       free_corrupt_blocks_tree(fs_info->corrupt_blocks);
        free_extent_cache_tree(&seen);
        free_extent_cache_tree(&pending);
        free_extent_cache_tree(&reada);
@@ -10028,7 +10248,7 @@ static int check_tree_block_ref(struct btrfs_root *root,
        int slot;
        int skinny_level;
        int type;
-       u32 nodesize = root->nodesize;
+       u32 nodesize = root->fs_info->nodesize;
        u32 item_size;
        u64 offset;
        int tree_reloc_root = 0;
@@ -10213,20 +10433,20 @@ static int check_extent_data_item(struct btrfs_root *root,
        extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi);
 
        /* Check unaligned disk_num_bytes and num_bytes */
-       if (!IS_ALIGNED(disk_num_bytes, root->sectorsize)) {
+       if (!IS_ALIGNED(disk_num_bytes, root->fs_info->sectorsize)) {
                error(
 "file extent [%llu, %llu] has unaligned disk num bytes: %llu, should be aligned to %u",
                        fi_key.objectid, fi_key.offset, disk_num_bytes,
-                       root->sectorsize);
+                       root->fs_info->sectorsize);
                err |= BYTES_UNALIGNED;
        } else {
                data_bytes_allocated += disk_num_bytes;
        }
-       if (!IS_ALIGNED(extent_num_bytes, root->sectorsize)) {
+       if (!IS_ALIGNED(extent_num_bytes, root->fs_info->sectorsize)) {
                error(
 "file extent [%llu, %llu] has unaligned num bytes: %llu, should be aligned to %u",
                        fi_key.objectid, fi_key.offset, extent_num_bytes,
-                       root->sectorsize);
+                       root->fs_info->sectorsize);
                err |= BYTES_UNALIGNED;
        } else {
                data_bytes_referenced += extent_num_bytes;
@@ -10340,7 +10560,6 @@ static int query_tree_block_level(struct btrfs_fs_info *fs_info, u64 bytenr)
        struct btrfs_extent_item *ei;
        u64 flags;
        u64 transid;
-       u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
        u8 backref_level;
        u8 header_level;
        int ret;
@@ -10386,7 +10605,7 @@ static int query_tree_block_level(struct btrfs_fs_info *fs_info, u64 bytenr)
        btrfs_release_path(&path);
 
        /* Get level from tree block as an alternative source */
-       eb = read_tree_block_fs_info(fs_info, bytenr, nodesize, transid);
+       eb = read_tree_block(fs_info, bytenr, transid);
        if (!extent_buffer_uptodate(eb)) {
                free_extent_buffer(eb);
                return -EIO;
@@ -10439,7 +10658,7 @@ static int check_tree_block_backref(struct btrfs_fs_info *fs_info, u64 root_id,
        }
 
        /* Read out the tree block to get item/node key */
-       eb = read_tree_block(root, bytenr, root->nodesize, 0);
+       eb = read_tree_block(fs_info, bytenr, 0);
        if (!extent_buffer_uptodate(eb)) {
                err |= REFERENCER_MISSING;
                free_extent_buffer(eb);
@@ -10536,12 +10755,11 @@ static int check_shared_block_backref(struct btrfs_fs_info *fs_info,
                                     u64 parent, u64 bytenr, int level)
 {
        struct extent_buffer *eb;
-       u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
        u32 nr;
        int found_parent = 0;
        int i;
 
-       eb = read_tree_block_fs_info(fs_info, parent, nodesize, 0);
+       eb = read_tree_block(fs_info, parent, 0);
        if (!extent_buffer_uptodate(eb))
                goto out;
 
@@ -10572,7 +10790,7 @@ out:
        if (!found_parent) {
                error(
        "shared extent[%llu %u] lost its parent (parent: %llu, level: %u)",
-                       bytenr, nodesize, parent, level);
+                       bytenr, fs_info->nodesize, parent, level);
                return REFERENCER_MISSING;
        }
        return 0;
@@ -10690,12 +10908,11 @@ static int check_shared_data_backref(struct btrfs_fs_info *fs_info,
        struct extent_buffer *eb;
        struct btrfs_key key;
        struct btrfs_file_extent_item *fi;
-       u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
        u32 nr;
        int found_parent = 0;
        int i;
 
-       eb = read_tree_block_fs_info(fs_info, parent, nodesize, 0);
+       eb = read_tree_block(fs_info, parent, 0);
        if (!extent_buffer_uptodate(eb))
                goto out;
 
@@ -10883,7 +11100,12 @@ static int check_dev_extent_item(struct btrfs_fs_info *fs_info,
 
        l = path.nodes[0];
        chunk = btrfs_item_ptr(l, path.slots[0], struct btrfs_chunk);
-       if (btrfs_chunk_length(l, chunk) != length)
+       ret = btrfs_check_chunk_valid(fs_info, l, chunk, path.slots[0],
+                                     chunk_key.offset);
+       if (ret < 0)
+               goto out;
+
+       if (btrfs_stripe_length(fs_info, l, chunk) != length)
                goto out;
 
        num_stripes = btrfs_chunk_num_stripes(l, chunk);
@@ -11121,11 +11343,10 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
        struct btrfs_block_group_item *bi;
        struct btrfs_block_group_item bg_item;
        struct btrfs_dev_extent *ptr;
-       u32 sectorsize = btrfs_super_sectorsize(fs_info->super_copy);
        u64 length;
        u64 chunk_end;
+       u64 stripe_len;
        u64 type;
-       u64 profile;
        int num_stripes;
        u64 offset;
        u64 objectid;
@@ -11137,25 +11358,15 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
        chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
        length = btrfs_chunk_length(eb, chunk);
        chunk_end = chunk_key.offset + length;
-       if (!IS_ALIGNED(length, sectorsize)) {
-               error("chunk[%llu %llu) not aligned to %u",
-                       chunk_key.offset, chunk_end, sectorsize);
-               err |= BYTES_UNALIGNED;
+       ret = btrfs_check_chunk_valid(fs_info, eb, chunk, slot,
+                                     chunk_key.offset);
+       if (ret < 0) {
+               error("chunk[%llu %llu) is invalid", chunk_key.offset,
+                       chunk_end);
+               err |= BYTES_UNALIGNED | UNKNOWN_TYPE;
                goto out;
        }
-
        type = btrfs_chunk_type(eb, chunk);
-       profile = type & BTRFS_BLOCK_GROUP_PROFILE_MASK;
-       if (!(type & BTRFS_BLOCK_GROUP_TYPE_MASK)) {
-               error("chunk[%llu %llu) has no chunk type",
-                       chunk_key.offset, chunk_end);
-               err |= UNKNOWN_TYPE;
-       }
-       if (profile && (profile & (profile - 1))) {
-               error("chunk[%llu %llu) multiple profiles detected: %llx",
-                       chunk_key.offset, chunk_end, profile);
-               err |= UNKNOWN_TYPE;
-       }
 
        bg_key.objectid = chunk_key.offset;
        bg_key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
@@ -11184,6 +11395,7 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
        }
 
        num_stripes = btrfs_chunk_num_stripes(eb, chunk);
+       stripe_len = btrfs_stripe_length(fs_info, eb, chunk);
        for (i = 0; i < num_stripes; i++) {
                btrfs_release_path(&path);
                btrfs_init_path(&path);
@@ -11203,7 +11415,7 @@ static int check_chunk_item(struct btrfs_fs_info *fs_info,
                offset = btrfs_dev_extent_chunk_offset(leaf, ptr);
                if (objectid != chunk_key.objectid ||
                    offset != chunk_key.offset ||
-                   btrfs_dev_extent_length(leaf, ptr) != length)
+                   btrfs_dev_extent_length(leaf, ptr) != stripe_len)
                        goto not_match_dev;
                continue;
 not_match_dev:
@@ -11419,11 +11631,6 @@ static int traverse_tree_block(struct btrfs_root *root,
                total_fs_tree_bytes += node->len;
        if (btrfs_header_owner(node) == BTRFS_EXTENT_TREE_OBJECTID)
                total_extent_tree_bytes += node->len;
-       if (!found_old_backref &&
-           btrfs_header_owner(node) == BTRFS_TREE_RELOC_OBJECTID &&
-           btrfs_header_backref_rev(node) == BTRFS_MIXED_BACKREF_REV &&
-           !btrfs_header_flag(node, BTRFS_HEADER_FLAG_RELOC))
-               found_old_backref = 1;
 
        /* pre-order tranversal, check itself first */
        level = btrfs_header_level(node);
@@ -11462,7 +11669,7 @@ static int traverse_tree_block(struct btrfs_root *root,
                 * As a btrfs tree has most 8 levels (0..7), so it's quite safe
                 * to call the function itself.
                 */
-               eb = read_tree_block(root, blocknr, root->nodesize, 0);
+               eb = read_tree_block(root->fs_info, blocknr, 0);
                if (extent_buffer_uptodate(eb)) {
                        ret = traverse_tree_block(root, eb);
                        err |= ret;
@@ -11476,15 +11683,18 @@ static int traverse_tree_block(struct btrfs_root *root,
 /*
  * Low memory usage version check_chunks_and_extents.
  */
-static int check_chunks_and_extents_v2(struct btrfs_root *root)
+static int check_chunks_and_extents_v2(struct btrfs_fs_info *fs_info)
 {
        struct btrfs_path path;
        struct btrfs_key key;
        struct btrfs_root *root1;
+       struct btrfs_root *root;
        struct btrfs_root *cur_root;
        int err = 0;
        int ret;
 
+       root = fs_info->fs_root;
+
        root1 = root->fs_info->chunk_root;
        ret = traverse_tree_block(root1, root1->node);
        err |= ret;
@@ -11536,6 +11746,20 @@ out:
        return err;
 }
 
+static int do_check_chunks_and_extents(struct btrfs_fs_info *fs_info)
+{
+       int ret;
+
+       if (!ctx.progress_enabled)
+               fprintf(stderr, "checking extents\n");
+       if (check_mode == CHECK_MODE_LOWMEM)
+               ret = check_chunks_and_extents_v2(fs_info);
+       else
+               ret = check_chunks_and_extents(fs_info);
+
+       return ret;
+}
+
 static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, int overwrite)
 {
@@ -11553,7 +11777,7 @@ static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
                goto init;
        }
        c = btrfs_alloc_free_block(trans, root,
-                                  root->nodesize,
+                                  root->fs_info->nodesize,
                                   root->root_key.objectid,
                                   &disk_key, level, 0, 0);
        if (IS_ERR(c)) {
@@ -11610,7 +11834,6 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
        struct btrfs_root_item *ri;
        struct btrfs_key key;
        u64 bytenr;
-       u32 nodesize;
        int level = btrfs_header_level(eb);
        int nritems;
        int ret;
@@ -11627,7 +11850,6 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
 
        btrfs_pin_extent(fs_info, eb->start, eb->len);
 
-       nodesize = btrfs_super_nodesize(fs_info->super_copy);
        nritems = btrfs_header_nritems(eb);
        for (i = 0; i < nritems; i++) {
                if (level == 0) {
@@ -11648,8 +11870,7 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
                         * in, but for now this doesn't actually use the root so
                         * just pass in extent_root.
                         */
-                       tmp = read_tree_block(fs_info->extent_root, bytenr,
-                                             nodesize, 0);
+                       tmp = read_tree_block(fs_info, bytenr, 0);
                        if (!extent_buffer_uptodate(tmp)) {
                                fprintf(stderr, "Error reading root block\n");
                                return -EIO;
@@ -11663,12 +11884,12 @@ static int pin_down_tree_blocks(struct btrfs_fs_info *fs_info,
 
                        /* If we aren't the tree root don't read the block */
                        if (level == 1 && !tree_root) {
-                               btrfs_pin_extent(fs_info, bytenr, nodesize);
+                               btrfs_pin_extent(fs_info, bytenr,
+                                               fs_info->nodesize);
                                continue;
                        }
 
-                       tmp = read_tree_block(fs_info->extent_root, bytenr,
-                                             nodesize, 0);
+                       tmp = read_tree_block(fs_info, bytenr, 0);
                        if (!extent_buffer_uptodate(tmp)) {
                                fprintf(stderr, "Error reading tree block\n");
                                return -EIO;
@@ -12046,13 +12267,14 @@ static int populate_csum(struct btrfs_trans_handle *trans,
                         struct btrfs_root *csum_root, char *buf, u64 start,
                         u64 len)
 {
+       struct btrfs_fs_info *fs_info = csum_root->fs_info;
        u64 offset = 0;
        u64 sectorsize;
        int ret = 0;
 
        while (offset < len) {
-               sectorsize = csum_root->sectorsize;
-               ret = read_extent_data(csum_root, buf, start + offset,
+               sectorsize = fs_info->sectorsize;
+               ret = read_extent_data(fs_info, buf, start + offset,
                                       &sectorsize, 0);
                if (ret)
                        break;
@@ -12079,7 +12301,7 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans,
        int slot = 0;
        int ret = 0;
 
-       buf = malloc(cur_root->fs_info->csum_root->sectorsize);
+       buf = malloc(cur_root->fs_info->sectorsize);
        if (!buf)
                return -ENOMEM;
 
@@ -12211,7 +12433,7 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
                return ret;
        }
 
-       buf = malloc(csum_root->sectorsize);
+       buf = malloc(csum_root->fs_info->sectorsize);
        if (!buf) {
                btrfs_release_path(&path);
                return -ENOMEM;
@@ -12624,6 +12846,45 @@ static int clear_free_space_cache(struct btrfs_fs_info *fs_info)
        return ret;
 }
 
+static int do_clear_free_space_cache(struct btrfs_fs_info *fs_info,
+               int clear_version)
+{
+       int ret = 0;
+
+       if (clear_version == 1) {
+               if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
+                       error(
+               "free space cache v2 detected, use --clear-space-cache v2");
+                       ret = 1;
+                       goto close_out;
+               }
+               printf("Clearing free space cache\n");
+               ret = clear_free_space_cache(fs_info);
+               if (ret) {
+                       error("failed to clear free space cache");
+                       ret = 1;
+               } else {
+                       printf("Free space cache cleared\n");
+               }
+       } else if (clear_version == 2) {
+               if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
+                       printf("no free space cache v2 to clear\n");
+                       ret = 0;
+                       goto close_out;
+               }
+               printf("Clear free space cache v2\n");
+               ret = btrfs_clear_free_space_tree(fs_info);
+               if (ret) {
+                       error("failed to clear free space cache v2: %d", ret);
+                       ret = 1;
+               } else {
+                       printf("free space cache v2 cleared\n");
+               }
+       }
+close_out:
+       return ret;
+}
+
 const char * const cmd_check_usage[] = {
        "btrfs check [options] <device>",
        "Check structural integrity of a filesystem (unmounted).",
@@ -12634,6 +12895,7 @@ const char * const cmd_check_usage[] = {
        "",
        "-s|--super <superblock>     use this superblock copy",
        "-b|--backup                 use the first valid backup root copy",
+       "--force                     skip mount checks, repair is not possible",
        "--repair                    try to repair the filesystem",
        "--readonly                  run in read-only mode (default)",
        "--init-csum-tree            create a new CRC tree",
@@ -12665,7 +12927,7 @@ int cmd_check(int argc, char **argv)
        u64 tree_root_bytenr = 0;
        u64 chunk_root_bytenr = 0;
        char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
-       int ret;
+       int ret = 0;
        int err = 0;
        u64 num;
        int init_csum_tree = 0;
@@ -12674,13 +12936,15 @@ int cmd_check(int argc, char **argv)
        int qgroup_report = 0;
        int qgroups_repaired = 0;
        unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE;
+       int force = 0;
 
        while(1) {
                int c;
                enum { GETOPT_VAL_REPAIR = 257, GETOPT_VAL_INIT_CSUM,
                        GETOPT_VAL_INIT_EXTENT, GETOPT_VAL_CHECK_CSUM,
                        GETOPT_VAL_READONLY, GETOPT_VAL_CHUNK_TREE,
-                       GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE };
+                       GETOPT_VAL_MODE, GETOPT_VAL_CLEAR_SPACE_CACHE,
+                       GETOPT_VAL_FORCE };
                static const struct option long_options[] = {
                        { "super", required_argument, NULL, 's' },
                        { "repair", no_argument, NULL, GETOPT_VAL_REPAIR },
@@ -12702,10 +12966,11 @@ int cmd_check(int argc, char **argv)
                                GETOPT_VAL_MODE },
                        { "clear-space-cache", required_argument, NULL,
                                GETOPT_VAL_CLEAR_SPACE_CACHE},
+                       { "force", no_argument, NULL, GETOPT_VAL_FORCE },
                        { NULL, 0, NULL, 0}
                };
 
-               c = getopt_long(argc, argv, "as:br:p", long_options, NULL);
+               c = getopt_long(argc, argv, "as:br:pEQ", long_options, NULL);
                if (c < 0)
                        break;
                switch(c) {
@@ -12786,6 +13051,9 @@ int cmd_check(int argc, char **argv)
                                }
                                ctree_flags |= OPEN_CTREE_WRITES;
                                break;
+                       case GETOPT_VAL_FORCE:
+                               force = 1;
+                               break;
                }
        }
 
@@ -12814,15 +13082,38 @@ int cmd_check(int argc, char **argv)
        radix_tree_init();
        cache_tree_init(&root_cache);
 
-       if((ret = check_mounted(argv[optind])) < 0) {
-               error("could not check mount status: %s", strerror(-ret));
-               err |= !!ret;
-               goto err_out;
-       } else if(ret) {
-               error("%s is currently mounted, aborting", argv[optind]);
-               ret = -EBUSY;
-               err |= !!ret;
-               goto err_out;
+       ret = check_mounted(argv[optind]);
+       if (!force) {
+               if (ret < 0) {
+                       error("could not check mount status: %s",
+                                       strerror(-ret));
+                       err |= !!ret;
+                       goto err_out;
+               } else if (ret) {
+                       error(
+"%s is currently mounted, use --force if you really intend to check the filesystem",
+                               argv[optind]);
+                       ret = -EBUSY;
+                       err |= !!ret;
+                       goto err_out;
+               }
+       } else {
+               if (repair) {
+                       error("repair and --force is not yet supported");
+                       ret = 1;
+                       err |= !!ret;
+                       goto err_out;
+               }
+               if (ret < 0) {
+                       warning(
+"cannot check mount status of %s, the filesystem could be mounted, continuing because of --force",
+                               argv[optind]);
+               } else if (ret) {
+                       warning(
+                       "filesystem mounted, continuing because of --force");
+               }
+               /* A block device is mounted in exclusive mode by kernel */
+               ctree_flags &= ~OPEN_CTREE_EXCLUSIVE;
        }
 
        /* only allow partial opening under repair mode */
@@ -12840,36 +13131,26 @@ int cmd_check(int argc, char **argv)
 
        global_info = info;
        root = info->fs_root;
-       if (clear_space_cache == 1) {
-               if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
-                       error(
-               "free space cache v2 detected, use --clear-space-cache v2");
-                       ret = 1;
-                       goto close_out;
-               }
-               printf("Clearing free space cache\n");
-               ret = clear_free_space_cache(info);
-               if (ret) {
-                       error("failed to clear free space cache");
-                       ret = 1;
-               } else {
-                       printf("Free space cache cleared\n");
-               }
+       uuid_unparse(info->super_copy->fsid, uuidbuf);
+
+       printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf);
+
+       /*
+        * Check the bare minimum before starting anything else that could rely
+        * on it, namely the tree roots, any local consistency checks
+        */
+       if (!extent_buffer_uptodate(info->tree_root->node) ||
+           !extent_buffer_uptodate(info->dev_root->node) ||
+           !extent_buffer_uptodate(info->chunk_root->node)) {
+               error("critical roots corrupted, unable to check the filesystem");
+               err |= !!ret;
+               ret = -EIO;
                goto close_out;
-       } else if (clear_space_cache == 2) {
-               if (!btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
-                       printf("no free space cache v2 to clear\n");
-                       ret = 0;
-                       goto close_out;
-               }
-               printf("Clear free space cache v2\n");
-               ret = btrfs_clear_free_space_tree(info);
-               if (ret) {
-                       error("failed to clear free space cache v2: %d", ret);
-                       ret = 1;
-               } else {
-                       printf("free space cache v2 cleared\n");
-               }
+       }
+
+       if (clear_space_cache) {
+               ret = do_clear_free_space_cache(info, clear_space_cache);
+               err |= !!ret;
                goto close_out;
        }
 
@@ -12892,7 +13173,6 @@ int cmd_check(int argc, char **argv)
                }
        }
 
-       uuid_unparse(info->super_copy->fsid, uuidbuf);
        if (qgroup_report) {
                printf("Print quota groups for %s\nUUID: %s\n", argv[optind],
                       uuidbuf);
@@ -12909,16 +13189,6 @@ int cmd_check(int argc, char **argv)
                err |= !!ret;
                goto close_out;
        }
-       printf("Checking filesystem on %s\nUUID: %s\n", argv[optind], uuidbuf);
-
-       if (!extent_buffer_uptodate(info->tree_root->node) ||
-           !extent_buffer_uptodate(info->dev_root->node) ||
-           !extent_buffer_uptodate(info->chunk_root->node)) {
-               error("critical roots corrupted, unable to check the filesystem");
-               err |= !!ret;
-               ret = -EIO;
-               goto close_out;
-       }
 
        if (init_extent_tree || init_csum_tree) {
                struct btrfs_trans_handle *trans;
@@ -12980,12 +13250,7 @@ int cmd_check(int argc, char **argv)
                goto close_out;
        }
 
-       if (!ctx.progress_enabled)
-               fprintf(stderr, "checking extents\n");
-       if (check_mode == CHECK_MODE_LOWMEM)
-               ret = check_chunks_and_extents_v2(root);
-       else
-               ret = check_chunks_and_extents(root);
+       ret = do_check_chunks_and_extents(info);
        err |= !!ret;
        if (ret)
                error(
@@ -13034,12 +13299,7 @@ int cmd_check(int argc, char **argv)
         * ignore it when this happens.
         */
        no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
-       if (!ctx.progress_enabled)
-               fprintf(stderr, "checking fs roots\n");
-       if (check_mode == CHECK_MODE_LOWMEM)
-               ret = check_fs_roots_v2(root->fs_info);
-       else
-               ret = check_fs_roots(root, &root_cache);
+       ret = do_check_fs_roots(info, &root_cache);
        err |= !!ret;
        if (ret) {
                error("errors found in fs roots");
@@ -13115,17 +13375,6 @@ int cmd_check(int argc, char **argv)
                err |= !!ret;
        }
 out:
-       if (found_old_backref) { /*
-                * there was a disk format change when mixed
-                * backref was in testing tree. The old format
-                * existed about one week.
-                */
-               printf("\n * Found old mixed backref format. "
-                      "The old format is not supported! *"
-                      "\n * Please mount the FS in readonly mode, "
-                      "backup data and re-format the FS. *\n\n");
-               err |= 1;
-       }
        printf("found %llu bytes used, ",
               (unsigned long long)bytes_used);
        if (err)