summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/utils.c b/utils.c
index 368c32a2..ea1d5ce6 100644
--- a/utils.c
+++ b/utils.c
@@ -307,6 +307,136 @@ out:
}
/*
+ * Setup an extent buffer for tree block.
+ */
+static int setup_temp_extent_buffer(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ u64 bytenr, u64 owner)
+{
+ unsigned char fsid[BTRFS_FSID_SIZE];
+ unsigned char chunk_uuid[BTRFS_UUID_SIZE];
+ int ret;
+
+ /* We rely on cfg->fs_uuid and chunk_uuid to fsid and chunk uuid */
+ BUG_ON(!cfg->fs_uuid || !cfg->chunk_uuid);
+ ret = uuid_parse(cfg->fs_uuid, fsid);
+ if (ret)
+ return -EINVAL;
+ ret = uuid_parse(cfg->chunk_uuid, chunk_uuid);
+ if (ret)
+ return -EINVAL;
+
+ memset(buf->data, 0, cfg->nodesize);
+ buf->len = cfg->nodesize;
+ btrfs_set_header_bytenr(buf, bytenr);
+ btrfs_set_header_generation(buf, 1);
+ btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
+ btrfs_set_header_owner(buf, owner);
+ btrfs_set_header_flags(buf, BTRFS_HEADER_FLAG_WRITTEN);
+ write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf),
+ BTRFS_UUID_SIZE);
+ write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE);
+ return 0;
+}
+
+static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf,
+ u64 bytenr)
+{
+ int ret;
+
+ csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+
+ /* Temporary extent buffer is always mapped 1:1 on disk */
+ ret = pwrite(fd, buf->data, buf->len, bytenr);
+ if (ret < buf->len)
+ ret = (ret < 0 ? ret : -EIO);
+ else
+ ret = 0;
+ return ret;
+}
+
+/*
+ * Insert a root item for temporary tree root
+ *
+ * Only used in make_btrfs_v2().
+ */
+static void insert_temp_root_item(struct extent_buffer *buf,
+ struct btrfs_mkfs_config *cfg,
+ int *slot, u32 *itemoff, u64 objectid,
+ u64 bytenr)
+{
+ struct btrfs_root_item root_item;
+ struct btrfs_inode_item *inode_item;
+ struct btrfs_disk_key disk_key;
+
+ btrfs_set_header_nritems(buf, *slot + 1);
+ (*itemoff) -= sizeof(root_item);
+ memset(&root_item, 0, sizeof(root_item));
+ inode_item = &root_item.inode;
+ btrfs_set_stack_inode_generation(inode_item, 1);
+ btrfs_set_stack_inode_size(inode_item, 3);
+ btrfs_set_stack_inode_nlink(inode_item, 1);
+ btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize);
+ btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755);
+ btrfs_set_root_refs(&root_item, 1);
+ btrfs_set_root_used(&root_item, cfg->nodesize);
+ btrfs_set_root_generation(&root_item, 1);
+ btrfs_set_root_bytenr(&root_item, bytenr);
+
+ memset(&disk_key, 0, sizeof(disk_key));
+ btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY);
+ btrfs_set_disk_key_objectid(&disk_key, objectid);
+ btrfs_set_disk_key_offset(&disk_key, 0);
+
+ 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(root_item));
+ write_extent_buffer(buf, &root_item,
+ btrfs_item_ptr_offset(buf, *slot),
+ sizeof(root_item));
+ (*slot)++;
+}
+
+static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg,
+ 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;
+
+ /*
+ * Provided bytenr must in ascending order, or tree root will have a
+ * bad key order.
+ */
+ BUG_ON(!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr &&
+ dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr));
+ buf = malloc(sizeof(*buf) + cfg->nodesize);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = setup_temp_extent_buffer(buf, cfg, root_bytenr,
+ BTRFS_ROOT_TREE_OBJECTID);
+ if (ret < 0)
+ goto out;
+
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_EXTENT_TREE_OBJECTID, extent_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_DEV_TREE_OBJECTID, dev_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_FS_TREE_OBJECTID, fs_bytenr);
+ insert_temp_root_item(buf, cfg, &slot, &itemoff,
+ BTRFS_CSUM_TREE_OBJECTID, csum_bytenr);
+
+ ret = write_temp_extent_buffer(fd, buf, root_bytenr);
+out:
+ free(buf);
+ return ret;
+}
+
+/*
* Improved version of make_btrfs().
*
* This one will
@@ -328,6 +458,10 @@ static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
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 */
@@ -359,6 +493,8 @@ static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
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 |
* -------------------------------------
@@ -376,9 +512,20 @@ static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
*/
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;
+
out:
return ret;
}