btrfs-progs: Introduce function to setup temporary extent tree
authorQu Wenruo <quwenruo@cn.fujitsu.com>
Fri, 29 Jan 2016 05:03:21 +0000 (13:03 +0800)
committerDavid Sterba <dsterba@suse.com>
Tue, 7 Jun 2016 16:15:19 +0000 (18:15 +0200)
Introduce new function, setup_temporary_extent_tree() to build a
temporary extent tree for make_btrfs_v2().

Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
utils.c

diff --git a/utils.c b/utils.c
index b083b73..72dc7d2 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -701,6 +701,194 @@ out:
 }
 
 /*
+ * 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.
+        */
+       BUG_ON(!(chunk_bytenr < root_bytenr && 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, 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
@@ -801,7 +989,15 @@ static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
        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;
 }