+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /* Must ensure SYS chunk starts before META chunk */
+ if (meta_chunk_start < sys_chunk_start) {
+ error("wrong chunk order: meta < system %llu < %llu",
+ (unsigned long long)meta_chunk_start,
+ (unsigned long long)sys_chunk_start);
+ return -EINVAL;
+ }
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, chunk_bytenr,
+ BTRFS_CHUNK_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = insert_temp_dev_item(fd, buf, cfg, &slot, &itemoff);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
+ sys_chunk_start,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff,
+ meta_chunk_start,
+ BTRFS_CONVERT_META_GROUP_SIZE,
+ BTRFS_BLOCK_GROUP_METADATA);
+ if (ret < 0)
+ goto out;
+ ret = write_temp_extent_buffer(fd, buf, chunk_bytenr);
+
+out:
+ free(buf);
+ return ret;
+}
+
+static void insert_temp_dev_extent(struct extent_buffer *buf,
+ int *slot, u32 *itemoff, u64 start, u64 len)
+{
+ struct btrfs_dev_extent *dev_extent;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(*dev_extent);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, 1);
+ btrfs_set_disk_key_offset(&disk_key, start);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_extent));
+
+ dev_extent = btrfs_item_ptr(buf, *slot, struct btrfs_dev_extent);
+ btrfs_set_dev_extent_chunk_objectid(buf, dev_extent,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ btrfs_set_dev_extent_length(buf, dev_extent, len);
+ btrfs_set_dev_extent_chunk_offset(buf, dev_extent, start);
+ btrfs_set_dev_extent_chunk_tree(buf, dev_extent,
+ BTRFS_CHUNK_TREE_OBJECTID);
+ (*slot)++;
+}
+
+static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 sys_chunk_start, u64 meta_chunk_start,
+ u64 dev_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /* Must ensure SYS chunk starts before META chunk */
+ if (meta_chunk_start < sys_chunk_start) {
+ error("wrong chunk order: meta < system %llu < %llu",
+ (unsigned long long)meta_chunk_start,
+ (unsigned long long)sys_chunk_start);
+ return -EINVAL;
+ }
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, dev_bytenr,
+ BTRFS_DEV_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ insert_temp_dev_extent(buf, &slot, &itemoff, sys_chunk_start,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE);
+ insert_temp_dev_extent(buf, &slot, &itemoff, meta_chunk_start,
+ BTRFS_CONVERT_META_GROUP_SIZE);
+ ret = write_temp_extent_buffer(fd, buf, dev_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int setup_temp_fs_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 fs_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ int ret;
+
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, fs_bytenr,
+ BTRFS_FS_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ /*
+ * Temporary fs tree is completely empty.
+ */
+ ret = write_temp_extent_buffer(fd, buf, fs_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+static int setup_temp_csum_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ int ret;
+
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(buf, cfg, csum_bytenr,
+ BTRFS_CSUM_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ /*
+ * Temporary csum tree is completely empty.
+ */
+ ret = write_temp_extent_buffer(fd, buf, csum_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+/*
+ * Insert one temporary extent item.
+ *
+ * NOTE: if skinny_metadata is not enabled, this function must be called
+ * after all other trees are initialized.
+ * Or fs without skinny-metadata will be screwed up.
+ */
+static int insert_temp_extent_item(int fd, struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 bytenr,
+ u64 ref_root)
+{
+ struct extent_buffer *tmp;
+ struct btrfs_extent_item *ei;
+ struct btrfs_extent_inline_ref *iref;
+ struct btrfs_disk_key disk_key;
+ struct btrfs_disk_key tree_info_key;
+ struct btrfs_tree_block_info *info;
+ int itemsize;
+ int skinny_metadata = cfg->features &
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
+ int ret;
+
+ if (skinny_metadata)
+ itemsize = sizeof(*ei) + sizeof(*iref);
+ else
+ itemsize = sizeof(*ei) + sizeof(*iref) +
+ sizeof(struct btrfs_tree_block_info);
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ *(itemoff) -= itemsize;
+
+ if (skinny_metadata) {
+ btrfs_set_disk_key_type(&disk_key, BTRFS_METADATA_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+ } else {
+ btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY);
+ btrfs_set_disk_key_offset(&disk_key, cfg->nodesize);
+ }
+ btrfs_set_disk_key_objectid(&disk_key, bytenr);
+
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), itemsize);
+
+ ei = btrfs_item_ptr(buf, *slot, struct btrfs_extent_item);
+ btrfs_set_extent_refs(buf, ei, 1);
+ btrfs_set_extent_generation(buf, ei, 1);
+ btrfs_set_extent_flags(buf, ei, BTRFS_EXTENT_FLAG_TREE_BLOCK);
+
+ if (skinny_metadata) {
+ iref = (struct btrfs_extent_inline_ref *)(ei + 1);
+ } else {
+ info = (struct btrfs_tree_block_info *)(ei + 1);
+ iref = (struct btrfs_extent_inline_ref *)(info + 1);
+ }
+ btrfs_set_extent_inline_ref_type(buf, iref,
+ BTRFS_TREE_BLOCK_REF_KEY);
+ btrfs_set_extent_inline_ref_offset(buf, iref, ref_root);
+
+ (*slot)++;
+ if (skinny_metadata)
+ return 0;
+
+ /*
+ * Lastly, check the tree block key by read the tree block
+ * Since we do 1:1 mapping for convert case, we can directly
+ * read the bytenr from disk
+ */
+ tmp = malloc(sizeof(*tmp) + cfg->nodesize);
+ if (!tmp)
+ return -ENOMEM;
+ ret = setup_temp_extent_buffer(tmp, cfg, bytenr, ref_root);
+ if (ret < 0)
+ goto out;
+ ret = pread(fd, tmp->data, cfg->nodesize, bytenr);
+ if (ret < cfg->nodesize) {
+ ret = (ret < 0 ? -errno : -EIO);
+ goto out;
+ }
+ if (btrfs_header_nritems(tmp) == 0) {
+ btrfs_set_disk_key_type(&tree_info_key, 0);
+ btrfs_set_disk_key_objectid(&tree_info_key, 0);
+ btrfs_set_disk_key_offset(&tree_info_key, 0);
+ } else {
+ btrfs_item_key(tmp, &tree_info_key, 0);
+ }
+ btrfs_set_tree_block_key(buf, info, &tree_info_key);
+
+out:
+ free(tmp);
+ return ret;
+}
+
+static void insert_temp_block_group(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff,
+ u64 bytenr, u64 len, u64 used, u64 flag)
+{
+ struct btrfs_block_group_item bgi;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(bgi);
+ btrfs_set_disk_key_type(&disk_key, BTRFS_BLOCK_GROUP_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, bytenr);
+ btrfs_set_disk_key_offset(&disk_key, len);
+ btrfs_set_item_key(buf, &disk_key, *slot);
+ btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff);
+ btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(bgi));
+
+ btrfs_set_block_group_flags(&bgi, flag);
+ btrfs_set_block_group_used(&bgi, used);
+ btrfs_set_block_group_chunk_objectid(&bgi,
+ BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+ write_extent_buffer(buf, &bgi, btrfs_item_ptr_offset(buf, *slot),
+ sizeof(bgi));
+ (*slot)++;
+}
+
+static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg,
+ u64 chunk_bytenr, u64 root_bytenr,
+ u64 extent_bytenr, u64 dev_bytenr,
+ u64 fs_bytenr, u64 csum_bytenr)
+{
+ struct extent_buffer *buf = NULL;
+ u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize);
+ int slot = 0;
+ int ret;
+
+ /*
+ * We must ensure provided bytenr are in ascending order,
+ * or extent tree key order will be broken.
+ */
+ if (!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr &&
+ extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr &&
+ fs_bytenr < csum_bytenr)) {
+ error("bad tree bytenr order: "
+ "chunk < root %llu < %llu, "
+ "root < extent %llu < %llu, "
+ "extent < dev %llu < %llu, "
+ "dev < fs %llu < %llu, "
+ "fs < csum %llu < %llu",
+ (unsigned long long)chunk_bytenr,
+ (unsigned long long)root_bytenr,
+ (unsigned long long)root_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)extent_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)dev_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)fs_bytenr,
+ (unsigned long long)csum_bytenr);
+ return -EINVAL;
+ }
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = setup_temp_extent_buffer(buf, cfg, extent_bytenr,
+ BTRFS_EXTENT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ chunk_bytenr, BTRFS_CHUNK_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ insert_temp_block_group(buf, cfg, &slot, &itemoff, chunk_bytenr,
+ BTRFS_MKFS_SYSTEM_GROUP_SIZE, cfg->nodesize,
+ BTRFS_BLOCK_GROUP_SYSTEM);
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ root_bytenr, BTRFS_ROOT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ /* 5 tree block used, root, extent, dev, fs and csum*/
+ insert_temp_block_group(buf, cfg, &slot, &itemoff, root_bytenr,
+ BTRFS_CONVERT_META_GROUP_SIZE, cfg->nodesize * 5,
+ BTRFS_BLOCK_GROUP_METADATA);
+
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ extent_bytenr, BTRFS_EXTENT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ dev_bytenr, BTRFS_DEV_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ fs_bytenr, BTRFS_FS_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+ ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff,
+ csum_bytenr, BTRFS_CSUM_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ ret = write_temp_extent_buffer(fd, buf, extent_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+/*
+ * Improved version of make_btrfs().
+ *
+ * This one will
+ * 1) Do chunk allocation to avoid used data
+ * And after this function, extent type matches chunk type
+ * 2) Better structured code
+ * No super long hand written codes to initialized all tree blocks
+ * Split into small blocks and reuse codes.
+ * TODO: Reuse tree operation facilities by introducing new flags
+ */
+static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
+{
+ struct cache_tree *free = &cctx->free;
+ struct cache_tree *used = &cctx->used;
+ u64 sys_chunk_start;
+ u64 meta_chunk_start;
+ /* chunk tree bytenr, in system chunk */
+ u64 chunk_bytenr;
+ /* metadata trees bytenr, in metadata chunk */
+ u64 root_bytenr;
+ u64 extent_bytenr;
+ u64 dev_bytenr;
+ u64 fs_bytenr;
+ u64 csum_bytenr;
+ int ret;
+
+ /* Shouldn't happen */
+ BUG_ON(cache_tree_empty(used));
+
+ /*
+ * reserve space for temporary superblock first
+ * Here we allocate a little larger space, to keep later
+ * free space will be STRIPE_LEN aligned
+ */
+ ret = reserve_free_space(free, BTRFS_STRIPE_LEN,
+ &cfg->super_bytenr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Then reserve system chunk space
+ * TODO: Change system group size depending on cctx->total_bytes.
+ * If using current 4M, it can only handle less than one TB for
+ * worst case and then run out of sys space.
+ */
+ ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE,
+ &sys_chunk_start);
+ if (ret < 0)
+ goto out;
+ ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE,
+ &meta_chunk_start);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Allocated meta/sys chunks will be mapped 1:1 with device offset.
+ *
+ * Inside the allocated metadata chunk, the layout will be:
+ * | offset | contents |
+ * -------------------------------------
+ * | +0 | tree root |
+ * | +nodesize | extent root |
+ * | +nodesize * 2 | device root |
+ * | +nodesize * 3 | fs tree |
+ * | +nodesize * 4 | csum tree |
+ * -------------------------------------
+ * Inside the allocated system chunk, the layout will be:
+ * | offset | contents |
+ * -------------------------------------
+ * | +0 | chunk root |
+ * -------------------------------------
+ */
+ chunk_bytenr = sys_chunk_start;
+ root_bytenr = meta_chunk_start;
+ extent_bytenr = meta_chunk_start + cfg->nodesize;
+ dev_bytenr = meta_chunk_start + cfg->nodesize * 2;
+ fs_bytenr = meta_chunk_start + cfg->nodesize * 3;
+ csum_bytenr = meta_chunk_start + cfg->nodesize * 4;
+
+ ret = setup_temp_super(fd, cfg, root_bytenr, chunk_bytenr);
+ if (ret < 0)
+ goto out;
+
+ ret = setup_temp_root_tree(fd, cfg, root_bytenr, extent_bytenr,
+ dev_bytenr, fs_bytenr, csum_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_chunk_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
+ chunk_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_dev_tree(fd, cfg, sys_chunk_start, meta_chunk_start,
+ dev_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_fs_tree(fd, cfg, fs_bytenr);
+ if (ret < 0)
+ goto out;
+ ret = setup_temp_csum_tree(fd, cfg, csum_bytenr);
+ if (ret < 0)
+ goto out;
+ /*
+ * Setup extent tree last, since it may need to read tree block key
+ * for non-skinny metadata case.
+ */
+ ret = setup_temp_extent_tree(fd, cfg, chunk_bytenr, root_bytenr,
+ extent_bytenr, dev_bytenr, fs_bytenr,
+ csum_bytenr);
+out:
+ return ret;
+}
+
+/*
+ * @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID
+ *
+ * The superblock signature is not valid, denotes a partially created
+ * filesystem, needs to be finalized.
+ */
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
+{
+ struct btrfs_super_block super;
+ struct extent_buffer *buf;