+/* Check if the type of extent matches with its chunk */
+static void check_extent_type(struct extent_record *rec)
+{
+ struct btrfs_block_group_cache *bg_cache;
+
+ bg_cache = btrfs_lookup_first_block_group(global_info, rec->start);
+ if (!bg_cache)
+ return;
+
+ /* data extent, check chunk directly*/
+ if (!rec->metadata) {
+ if (!(bg_cache->flags & BTRFS_BLOCK_GROUP_DATA))
+ rec->wrong_chunk_type = 1;
+ return;
+ }
+
+ /* metadata extent, check the obvious case first */
+ if (!(bg_cache->flags & (BTRFS_BLOCK_GROUP_SYSTEM |
+ BTRFS_BLOCK_GROUP_METADATA))) {
+ rec->wrong_chunk_type = 1;
+ return;
+ }
+
+ /*
+ * Check SYSTEM extent, as it's also marked as metadata, we can only
+ * make sure it's a SYSTEM extent by its backref
+ */
+ if (!RB_EMPTY_ROOT(&rec->backref_tree)) {
+ struct extent_backref *node;
+ struct tree_backref *tback;
+ u64 bg_type;
+
+ 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;
+ return;
+ }
+ tback = container_of(node, struct tree_backref, node);
+
+ if (tback->root == BTRFS_CHUNK_TREE_OBJECTID)
+ bg_type = BTRFS_BLOCK_GROUP_SYSTEM;
+ else
+ bg_type = BTRFS_BLOCK_GROUP_METADATA;
+ if (!(bg_cache->flags & bg_type))
+ rec->wrong_chunk_type = 1;
+ }
+}
+
+/*
+ * Allocate a new extent record, fill default values from @tmpl and insert int
+ * @extent_cache. Caller is supposed to make sure the [start,nr) is not in
+ * the cache, otherwise it fails.
+ */
+static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
+ struct extent_record *tmpl)
+{
+ struct extent_record *rec;
+ int ret = 0;
+
+ rec = malloc(sizeof(*rec));
+ if (!rec)
+ return -ENOMEM;
+ rec->start = tmpl->start;
+ rec->max_size = tmpl->max_size;
+ rec->nr = max(tmpl->nr, tmpl->max_size);
+ rec->found_rec = tmpl->found_rec;
+ rec->content_checked = tmpl->content_checked;
+ rec->owner_ref_checked = tmpl->owner_ref_checked;
+ rec->num_duplicates = 0;
+ rec->metadata = tmpl->metadata;
+ rec->flag_block_full_backref = FLAG_UNSET;
+ rec->bad_full_backref = 0;
+ rec->crossing_stripes = 0;
+ rec->wrong_chunk_type = 0;
+ rec->is_root = tmpl->is_root;
+ rec->refs = tmpl->refs;
+ rec->extent_item_refs = tmpl->extent_item_refs;
+ rec->parent_generation = tmpl->parent_generation;
+ 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;
+ ret = insert_cache_extent(extent_cache, &rec->cache);
+ BUG_ON(ret);
+ bytes_used += rec->nr;
+
+ if (tmpl->metadata)
+ rec->crossing_stripes = check_crossing_stripes(rec->start,
+ global_info->tree_root->nodesize);
+ check_extent_type(rec);
+ return ret;
+}
+
+/*
+ * Lookup and modify an extent, some values of @tmpl are interpreted verbatim,
+ * some are hints:
+ * - refs - if found, increase refs
+ * - is_root - if found, set
+ * - content_checked - if found, set
+ * - owner_ref_checked - if found, set
+ *
+ * If not found, create a new one, initialize and insert.
+ */