+static int calculate_bg_used(struct btrfs_root *extent_root,
+ struct chunk_record *chunk_rec,
+ struct btrfs_path *path,
+ u64 *used)
+{
+ struct extent_buffer *node;
+ struct btrfs_key found_key;
+ int slot;
+ int ret = 0;
+ u64 used_ret = 0;
+
+ while (1) {
+ node = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(node, &found_key, slot);
+ if (found_key.objectid >= chunk_rec->offset + chunk_rec->length)
+ break;
+ if (found_key.type != BTRFS_METADATA_ITEM_KEY &&
+ found_key.type != BTRFS_EXTENT_DATA_KEY)
+ goto next;
+ if (found_key.type == BTRFS_METADATA_ITEM_KEY)
+ used_ret += extent_root->fs_info->nodesize;
+ else
+ used_ret += found_key.offset;
+next:
+ if (slot + 1 < btrfs_header_nritems(node)) {
+ slot++;
+ } else {
+ ret = btrfs_next_leaf(extent_root, path);
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ if (ret < 0)
+ break;
+ }
+ }
+ if (!ret)
+ *used = used_ret;
+ return ret;
+}
+
+static int __insert_block_group(struct btrfs_trans_handle *trans,
+ struct chunk_record *chunk_rec,
+ struct btrfs_root *extent_root,
+ u64 used)
+{
+ struct btrfs_block_group_item bg_item;
+ struct btrfs_key key;
+ int ret = 0;
+
+ btrfs_set_block_group_used(&bg_item, used);
+ btrfs_set_block_group_chunk_objectid(&bg_item, used);
+ btrfs_set_block_group_flags(&bg_item, chunk_rec->type_flags);
+ key.objectid = chunk_rec->offset;
+ key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+ key.offset = chunk_rec->length;
+
+ ret = btrfs_insert_item(trans, extent_root, &key, &bg_item,
+ sizeof(bg_item));
+ return ret;
+}
+
+/*
+ * Search through the extent tree to rebuild the 'used' member of the block
+ * group.
+ * However, since block group and extent item shares the extent tree,
+ * the extent item may also missing.
+ * In that case, we fill the 'used' with the length of the block group to
+ * ensure no write into the block group.
+ * Btrfsck will hate it but we will inform user to call '--init-extent-tree'
+ * if possible, or just salvage as much data as possible from the fs.
+ */
+static int rebuild_block_group(struct btrfs_trans_handle *trans,
+ struct recover_control *rc,
+ struct btrfs_root *root)
+{
+ struct chunk_record *chunk_rec;
+ struct btrfs_key search_key;
+ struct btrfs_path path;
+ u64 used = 0;
+ int ret = 0;
+
+ if (list_empty(&rc->rebuild_chunks))
+ return 0;
+
+ btrfs_init_path(&path);
+ list_for_each_entry(chunk_rec, &rc->rebuild_chunks, list) {
+ search_key.objectid = chunk_rec->offset;
+ search_key.type = BTRFS_EXTENT_ITEM_KEY;
+ search_key.offset = 0;
+ ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
+ &search_key, &path, 0, 0);
+ if (ret < 0)
+ goto out;
+ ret = calculate_bg_used(root->fs_info->extent_root,
+ chunk_rec, &path, &used);
+ /*
+ * Extent tree is damaged, better to rebuild the whole extent
+ * tree. Currently, change the used to chunk's len to prevent
+ * write/block reserve happening in that block group.
+ */
+ if (ret < 0) {
+ fprintf(stderr,
+ "Fail to search extent tree for block group: [%llu,%llu]\n",
+ chunk_rec->offset,
+ chunk_rec->offset + chunk_rec->length);
+ fprintf(stderr,
+ "Mark the block group full to prevent block rsv problems\n");
+ used = chunk_rec->length;
+ }
+ btrfs_release_path(&path);
+ ret = __insert_block_group(trans, chunk_rec,
+ root->fs_info->extent_root,
+ used);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+