cryptospore / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone
9ae3a8
From 243d499717ebba0c0644620237c266112164d5ed Mon Sep 17 00:00:00 2001
9ae3a8
From: Jeffrey Cody <jcody@redhat.com>
9ae3a8
Date: Wed, 20 Nov 2013 19:44:05 +0100
9ae3a8
Subject: [PATCH 22/25] block: vhdx - add .bdrv_create() support
9ae3a8
9ae3a8
RH-Author: Jeffrey Cody <jcody@redhat.com>
9ae3a8
Message-id: <450971418e351130082c4c5f3c8ac8231810c556.1384975172.git.jcody@redhat.com>
9ae3a8
Patchwork-id: 55814
9ae3a8
O-Subject: [RHEL7 qemu-kvm PATCH 22/26] block: vhdx - add .bdrv_create() support
9ae3a8
Bugzilla: 879234
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
9ae3a8
This adds support for VHDX image creation, for images of type "Fixed"
9ae3a8
and "Dynamic".  "Differencing" types (i.e., VHDX images with backing
9ae3a8
files) are currently not supported.
9ae3a8
9ae3a8
Options for image creation include:
9ae3a8
    * log size:
9ae3a8
        The size of the journaling log for VHDX.  Minimum is 1MB,
9ae3a8
        and it must be a multiple of 1MB. Invalid log sizes will be
9ae3a8
        silently fixed by rounding up to the nearest MB.
9ae3a8
9ae3a8
        Default is 1MB.
9ae3a8
9ae3a8
    * block size:
9ae3a8
        This is the size of a payload block.  The range is 1MB to 256MB,
9ae3a8
        inclusive, and must be a multiple of 1MB as well.  Invalid sizes
9ae3a8
        and multiples will be silently fixed.  If '0' is passed, then
9ae3a8
        a sane size is chosen (depending on virtual image size).
9ae3a8
9ae3a8
        Default is 0 (Auto-select).
9ae3a8
9ae3a8
    * subformat:
9ae3a8
        - "dynamic"
9ae3a8
            An image without data pre-allocated.
9ae3a8
        - "fixed"
9ae3a8
            An image with data pre-allocated.
9ae3a8
9ae3a8
        Default is "dynamic"
9ae3a8
9ae3a8
When creating the image file, the lettered sections are created:
9ae3a8
9ae3a8
-----------------------------------------------------------------.
9ae3a8
|   (A)    |   (B)    |    (C)    |     (D)       |     (E)
9ae3a8
|  File ID |  Header1 |  Header 2 |  Region Tbl 1 |  Region Tbl 2
9ae3a8
|          |          |           |               |
9ae3a8
.-----------------------------------------------------------------.
9ae3a8
0         64KB      128KB       192KB           256KB          320KB
9ae3a8
9ae3a8
.---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
9ae3a8
|     (F)     |     (G)       |    (H)    |
9ae3a8
| Journal Log |  BAT / Bitmap |  Metadata |  .... data ......
9ae3a8
|             |               |           |
9ae3a8
.---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
9ae3a8
1MB         (var.)          (var.)      (var.)
9ae3a8
9ae3a8
Signed-off-by: Jeff Cody <jcody@redhat.com>
9ae3a8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
(cherry picked from commit 3412f7b1bd8f250c34c9f933767d06b9444bb821)
9ae3a8
9ae3a8
RHEL7 Note: Although the cherry-pick applied clean, the 'bdrv_unref()'
9ae3a8
            call in the upstream version was reverted back to the
9ae3a8
            original 'bdrv_delete()' that is present in RHEL7.
9ae3a8
9ae3a8
Signed-off-by: Jeff Cody <jcody@redhat.com>
9ae3a8
---
9ae3a8
 block/vhdx.c | 558 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/vhdx.h |  15 +-
9ae3a8
 2 files changed, 572 insertions(+), 1 deletion(-)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/vhdx.c |  557 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/vhdx.h |   15 ++-
9ae3a8
 2 files changed, 571 insertions(+), 1 deletions(-)
9ae3a8
9ae3a8
diff --git a/block/vhdx.c b/block/vhdx.c
9ae3a8
index 5a112e8..8e3b371 100644
9ae3a8
--- a/block/vhdx.c
9ae3a8
+++ b/block/vhdx.c
9ae3a8
@@ -23,6 +23,19 @@
9ae3a8
 #include "migration/migration.h"
9ae3a8
 
9ae3a8
 #include <uuid/uuid.h>
9ae3a8
+#include <glib.h>
9ae3a8
+
9ae3a8
+/* Options for VHDX creation */
9ae3a8
+
9ae3a8
+#define VHDX_BLOCK_OPT_LOG_SIZE   "log_size"
9ae3a8
+#define VHDX_BLOCK_OPT_BLOCK_SIZE "block_size"
9ae3a8
+#define VHDX_BLOCK_OPT_ZERO "block_state_zero"
9ae3a8
+
9ae3a8
+typedef enum VHDXImageType {
9ae3a8
+    VHDX_TYPE_DYNAMIC = 0,
9ae3a8
+    VHDX_TYPE_FIXED,
9ae3a8
+    VHDX_TYPE_DIFFERENCING,   /* Currently unsupported */
9ae3a8
+} VHDXImageType;
9ae3a8
 
9ae3a8
 /* Several metadata and region table data entries are identified by
9ae3a8
  * guids in  a MS-specific GUID format. */
9ae3a8
@@ -1332,6 +1345,548 @@ exit:
9ae3a8
 }
9ae3a8
 
9ae3a8
 
9ae3a8
+
9ae3a8
+/*
9ae3a8
+ * Create VHDX Headers
9ae3a8
+ *
9ae3a8
+ * There are 2 headers, and the highest sequence number will represent
9ae3a8
+ * the active header
9ae3a8
+ */
9ae3a8
+static int vhdx_create_new_headers(BlockDriverState *bs, uint64_t image_size,
9ae3a8
+                                   uint32_t log_size)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    VHDXHeader *hdr = NULL;
9ae3a8
+
9ae3a8
+    hdr = g_malloc0(sizeof(VHDXHeader));
9ae3a8
+
9ae3a8
+    hdr->signature       = VHDX_HEADER_SIGNATURE;
9ae3a8
+    hdr->sequence_number = g_random_int();
9ae3a8
+    hdr->log_version     = 0;
9ae3a8
+    hdr->version         = 1;
9ae3a8
+    hdr->log_length      = log_size;
9ae3a8
+    hdr->log_offset      = VHDX_HEADER_SECTION_END;
9ae3a8
+    vhdx_guid_generate(&hdr->file_write_guid);
9ae3a8
+    vhdx_guid_generate(&hdr->data_write_guid);
9ae3a8
+
9ae3a8
+    ret = vhdx_write_header(bs, hdr, VHDX_HEADER1_OFFSET, false);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+    hdr->sequence_number++;
9ae3a8
+    ret = vhdx_write_header(bs, hdr, VHDX_HEADER2_OFFSET, false);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    g_free(hdr);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+
9ae3a8
+/*
9ae3a8
+ * Create the Metadata entries.
9ae3a8
+ *
9ae3a8
+ * For more details on the entries, see section 3.5 (pg 29) in the
9ae3a8
+ * VHDX 1.00 specification.
9ae3a8
+ *
9ae3a8
+ * We support 5 metadata entries (all required by spec):
9ae3a8
+ *          File Parameters,
9ae3a8
+ *          Virtual Disk Size,
9ae3a8
+ *          Page 83 Data,
9ae3a8
+ *          Logical Sector Size,
9ae3a8
+ *          Physical Sector Size
9ae3a8
+ *
9ae3a8
+ * The first 64KB of the Metadata section is reserved for the metadata
9ae3a8
+ * header and entries; beyond that, the metadata items themselves reside.
9ae3a8
+ */
9ae3a8
+static int vhdx_create_new_metadata(BlockDriverState *bs,
9ae3a8
+                                    uint64_t image_size,
9ae3a8
+                                    uint32_t block_size,
9ae3a8
+                                    uint32_t sector_size,
9ae3a8
+                                    uint64_t metadata_offset,
9ae3a8
+                                    VHDXImageType type)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint32_t offset = 0;
9ae3a8
+    void *buffer = NULL;
9ae3a8
+    void *entry_buffer;
9ae3a8
+    VHDXMetadataTableHeader *md_table;;
9ae3a8
+    VHDXMetadataTableEntry  *md_table_entry;
9ae3a8
+
9ae3a8
+    /* Metadata entries */
9ae3a8
+    VHDXFileParameters     *mt_file_params;
9ae3a8
+    VHDXVirtualDiskSize    *mt_virtual_size;
9ae3a8
+    VHDXPage83Data         *mt_page83;
9ae3a8
+    VHDXVirtualDiskLogicalSectorSize  *mt_log_sector_size;
9ae3a8
+    VHDXVirtualDiskPhysicalSectorSize *mt_phys_sector_size;
9ae3a8
+
9ae3a8
+    entry_buffer = g_malloc0(sizeof(VHDXFileParameters)               +
9ae3a8
+                             sizeof(VHDXVirtualDiskSize)              +
9ae3a8
+                             sizeof(VHDXPage83Data)                   +
9ae3a8
+                             sizeof(VHDXVirtualDiskLogicalSectorSize) +
9ae3a8
+                             sizeof(VHDXVirtualDiskPhysicalSectorSize));
9ae3a8
+
9ae3a8
+    mt_file_params = entry_buffer;
9ae3a8
+    offset += sizeof(VHDXFileParameters);
9ae3a8
+    mt_virtual_size = entry_buffer + offset;
9ae3a8
+    offset += sizeof(VHDXVirtualDiskSize);
9ae3a8
+    mt_page83 = entry_buffer + offset;
9ae3a8
+    offset += sizeof(VHDXPage83Data);
9ae3a8
+    mt_log_sector_size = entry_buffer + offset;
9ae3a8
+    offset += sizeof(VHDXVirtualDiskLogicalSectorSize);
9ae3a8
+    mt_phys_sector_size = entry_buffer + offset;
9ae3a8
+
9ae3a8
+    mt_file_params->block_size = cpu_to_le32(block_size);
9ae3a8
+    if (type == VHDX_TYPE_FIXED) {
9ae3a8
+        mt_file_params->data_bits |= VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED;
9ae3a8
+        cpu_to_le32s(&mt_file_params->data_bits);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    vhdx_guid_generate(&mt_page83->page_83_data);
9ae3a8
+    cpu_to_leguids(&mt_page83->page_83_data);
9ae3a8
+    mt_virtual_size->virtual_disk_size        = cpu_to_le64(image_size);
9ae3a8
+    mt_log_sector_size->logical_sector_size   = cpu_to_le32(sector_size);
9ae3a8
+    mt_phys_sector_size->physical_sector_size = cpu_to_le32(sector_size);
9ae3a8
+
9ae3a8
+    buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE);
9ae3a8
+    md_table = buffer;
9ae3a8
+
9ae3a8
+    md_table->signature   = VHDX_METADATA_SIGNATURE;
9ae3a8
+    md_table->entry_count = 5;
9ae3a8
+    vhdx_metadata_header_le_export(md_table);
9ae3a8
+
9ae3a8
+
9ae3a8
+    /* This will reference beyond the reserved table portion */
9ae3a8
+    offset = 64 * KiB;
9ae3a8
+
9ae3a8
+    md_table_entry = buffer + sizeof(VHDXMetadataTableHeader);
9ae3a8
+
9ae3a8
+    md_table_entry[0].item_id = file_param_guid;
9ae3a8
+    md_table_entry[0].offset  = offset;
9ae3a8
+    md_table_entry[0].length  = sizeof(VHDXFileParameters);
9ae3a8
+    md_table_entry[0].data_bits |= VHDX_META_FLAGS_IS_REQUIRED;
9ae3a8
+    offset += md_table_entry[0].length;
9ae3a8
+    vhdx_metadata_entry_le_export(&md_table_entry[0]);
9ae3a8
+
9ae3a8
+    md_table_entry[1].item_id = virtual_size_guid;
9ae3a8
+    md_table_entry[1].offset  = offset;
9ae3a8
+    md_table_entry[1].length  = sizeof(VHDXVirtualDiskSize);
9ae3a8
+    md_table_entry[1].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
9ae3a8
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
9ae3a8
+    offset += md_table_entry[1].length;
9ae3a8
+    vhdx_metadata_entry_le_export(&md_table_entry[1]);
9ae3a8
+
9ae3a8
+    md_table_entry[2].item_id = page83_guid;
9ae3a8
+    md_table_entry[2].offset  = offset;
9ae3a8
+    md_table_entry[2].length  = sizeof(VHDXPage83Data);
9ae3a8
+    md_table_entry[2].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
9ae3a8
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
9ae3a8
+    offset += md_table_entry[2].length;
9ae3a8
+    vhdx_metadata_entry_le_export(&md_table_entry[2]);
9ae3a8
+
9ae3a8
+    md_table_entry[3].item_id = logical_sector_guid;
9ae3a8
+    md_table_entry[3].offset  = offset;
9ae3a8
+    md_table_entry[3].length  = sizeof(VHDXVirtualDiskLogicalSectorSize);
9ae3a8
+    md_table_entry[3].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
9ae3a8
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
9ae3a8
+    offset += md_table_entry[3].length;
9ae3a8
+    vhdx_metadata_entry_le_export(&md_table_entry[3]);
9ae3a8
+
9ae3a8
+    md_table_entry[4].item_id = phys_sector_guid;
9ae3a8
+    md_table_entry[4].offset  = offset;
9ae3a8
+    md_table_entry[4].length  = sizeof(VHDXVirtualDiskPhysicalSectorSize);
9ae3a8
+    md_table_entry[4].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
9ae3a8
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
9ae3a8
+    vhdx_metadata_entry_le_export(&md_table_entry[4]);
9ae3a8
+
9ae3a8
+    ret = bdrv_pwrite(bs, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = bdrv_pwrite(bs, metadata_offset + (64 * KiB), entry_buffer,
9ae3a8
+                      VHDX_HEADER_BLOCK_SIZE);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    g_free(buffer);
9ae3a8
+    g_free(entry_buffer);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* This create the actual BAT itself.  We currently only support
9ae3a8
+ * 'Dynamic' and 'Fixed' image types.
9ae3a8
+ *
9ae3a8
+ *  Dynamic images: default state of the BAT is all zeroes.
9ae3a8
+ *
9ae3a8
+ *  Fixed images: default state of the BAT is fully populated, with
9ae3a8
+ *                file offsets and state PAYLOAD_BLOCK_FULLY_PRESENT.
9ae3a8
+ */
9ae3a8
+static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
9ae3a8
+                           uint64_t image_size, VHDXImageType type,
9ae3a8
+                           bool use_zero_blocks, VHDXRegionTableEntry *rt_bat)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint64_t data_file_offset;
9ae3a8
+    uint64_t total_sectors = 0;
9ae3a8
+    uint64_t sector_num = 0;
9ae3a8
+    uint64_t unused;
9ae3a8
+    int block_state;
9ae3a8
+    VHDXSectorInfo sinfo;
9ae3a8
+
9ae3a8
+    assert(s->bat == NULL);
9ae3a8
+
9ae3a8
+    /* this gives a data start after BAT/bitmap entries, and well
9ae3a8
+     * past any metadata entries (with a 4 MB buffer for future
9ae3a8
+     * expansion */
9ae3a8
+    data_file_offset = rt_bat->file_offset + rt_bat->length + 5 * MiB;
9ae3a8
+    total_sectors = image_size >> s->logical_sector_size_bits;
9ae3a8
+
9ae3a8
+    if (type == VHDX_TYPE_DYNAMIC) {
9ae3a8
+        /* All zeroes, so we can just extend the file - the end of the BAT
9ae3a8
+         * is the furthest thing we have written yet */
9ae3a8
+        ret = bdrv_truncate(bs, data_file_offset);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+    } else if (type == VHDX_TYPE_FIXED) {
9ae3a8
+        ret = bdrv_truncate(bs, data_file_offset + image_size);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+    } else {
9ae3a8
+        ret = -ENOTSUP;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (type == VHDX_TYPE_FIXED ||
9ae3a8
+                use_zero_blocks ||
9ae3a8
+                bdrv_has_zero_init(bs) == 0) {
9ae3a8
+        /* for a fixed file, the default BAT entry is not zero */
9ae3a8
+        s->bat = g_malloc0(rt_bat->length);
9ae3a8
+        block_state = type == VHDX_TYPE_FIXED ? PAYLOAD_BLOCK_FULLY_PRESENT :
9ae3a8
+                                                PAYLOAD_BLOCK_NOT_PRESENT;
9ae3a8
+        block_state = use_zero_blocks ? PAYLOAD_BLOCK_ZERO : block_state;
9ae3a8
+        /* fill the BAT by emulating sector writes of sectors_per_block size */
9ae3a8
+        while (sector_num < total_sectors) {
9ae3a8
+            vhdx_block_translate(s, sector_num, s->sectors_per_block, &sinfo);
9ae3a8
+            sinfo.file_offset = data_file_offset +
9ae3a8
+                                (sector_num << s->logical_sector_size_bits);
9ae3a8
+            sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB);
9ae3a8
+            vhdx_update_bat_table_entry(bs, s, &sinfo, &unused, &unused,
9ae3a8
+                                        block_state);
9ae3a8
+            cpu_to_le64s(&s->bat[sinfo.bat_idx]);
9ae3a8
+            sector_num += s->sectors_per_block;
9ae3a8
+        }
9ae3a8
+        ret = bdrv_pwrite(bs, rt_bat->file_offset, s->bat, rt_bat->length);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    g_free(s->bat);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Creates the region table header, and region table entries.
9ae3a8
+ * There are 2 supported region table entries: BAT, and Metadata/
9ae3a8
+ *
9ae3a8
+ * As the calculations for the BAT region table are also needed
9ae3a8
+ * to create the BAT itself, we will also cause the BAT to be
9ae3a8
+ * created.
9ae3a8
+ */
9ae3a8
+static int vhdx_create_new_region_table(BlockDriverState *bs,
9ae3a8
+                                        uint64_t image_size,
9ae3a8
+                                        uint32_t block_size,
9ae3a8
+                                        uint32_t sector_size,
9ae3a8
+                                        uint32_t log_size,
9ae3a8
+                                        bool use_zero_blocks,
9ae3a8
+                                        VHDXImageType type,
9ae3a8
+                                        uint64_t *metadata_offset)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint32_t offset = 0;
9ae3a8
+    void *buffer = NULL;
9ae3a8
+    BDRVVHDXState *s = NULL;
9ae3a8
+    VHDXRegionTableHeader *region_table;
9ae3a8
+    VHDXRegionTableEntry *rt_bat;
9ae3a8
+    VHDXRegionTableEntry *rt_metadata;
9ae3a8
+
9ae3a8
+    assert(metadata_offset != NULL);
9ae3a8
+
9ae3a8
+    /* Populate enough of the BDRVVHDXState to be able to use the
9ae3a8
+     * pre-existing BAT calculation, translation, and update functions */
9ae3a8
+    s = g_malloc0(sizeof(BDRVVHDXState));
9ae3a8
+
9ae3a8
+    s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) *
9ae3a8
+                     (uint64_t) sector_size / (uint64_t) block_size;
9ae3a8
+
9ae3a8
+    s->sectors_per_block = block_size / sector_size;
9ae3a8
+    s->virtual_disk_size = image_size;
9ae3a8
+    s->block_size = block_size;
9ae3a8
+    s->logical_sector_size = sector_size;
9ae3a8
+
9ae3a8
+    vhdx_set_shift_bits(s);
9ae3a8
+
9ae3a8
+    vhdx_calc_bat_entries(s);
9ae3a8
+
9ae3a8
+    /* At this point the VHDX state is populated enough for creation */
9ae3a8
+
9ae3a8
+    /* a single buffer is used so we can calculate the checksum over the
9ae3a8
+     * entire 64KB block */
9ae3a8
+    buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE);
9ae3a8
+    region_table = buffer;
9ae3a8
+    offset += sizeof(VHDXRegionTableHeader);
9ae3a8
+    rt_bat = buffer + offset;
9ae3a8
+    offset += sizeof(VHDXRegionTableEntry);
9ae3a8
+    rt_metadata  = buffer + offset;
9ae3a8
+
9ae3a8
+    region_table->signature = VHDX_REGION_SIGNATURE;
9ae3a8
+    region_table->entry_count = 2;   /* BAT and Metadata */
9ae3a8
+
9ae3a8
+    rt_bat->guid        = bat_guid;
9ae3a8
+    rt_bat->length      = ROUND_UP(s->bat_entries * sizeof(VHDXBatEntry), MiB);
9ae3a8
+    rt_bat->file_offset = ROUND_UP(VHDX_HEADER_SECTION_END + log_size, MiB);
9ae3a8
+    s->bat_offset = rt_bat->file_offset;
9ae3a8
+
9ae3a8
+    rt_metadata->guid        = metadata_guid;
9ae3a8
+    rt_metadata->file_offset = ROUND_UP(rt_bat->file_offset + rt_bat->length,
9ae3a8
+                                        MiB);
9ae3a8
+    rt_metadata->length      = 1 * MiB; /* min size, and more than enough */
9ae3a8
+    *metadata_offset = rt_metadata->file_offset;
9ae3a8
+
9ae3a8
+    vhdx_update_checksum(buffer, VHDX_HEADER_BLOCK_SIZE,
9ae3a8
+                         offsetof(VHDXRegionTableHeader, checksum));
9ae3a8
+
9ae3a8
+
9ae3a8
+    /* The region table gives us the data we need to create the BAT,
9ae3a8
+     * so do that now */
9ae3a8
+    ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks, rt_bat);
9ae3a8
+
9ae3a8
+    /* Now write out the region headers to disk */
9ae3a8
+    vhdx_region_header_le_export(region_table);
9ae3a8
+    vhdx_region_entry_le_export(rt_bat);
9ae3a8
+    vhdx_region_entry_le_export(rt_metadata);
9ae3a8
+
9ae3a8
+    ret = bdrv_pwrite(bs, VHDX_REGION_TABLE_OFFSET, buffer,
9ae3a8
+                      VHDX_HEADER_BLOCK_SIZE);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = bdrv_pwrite(bs, VHDX_REGION_TABLE2_OFFSET, buffer,
9ae3a8
+                      VHDX_HEADER_BLOCK_SIZE);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    g_free(s);
9ae3a8
+    g_free(buffer);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* We need to create the following elements:
9ae3a8
+ *
9ae3a8
+ *    .-----------------------------------------------------------------.
9ae3a8
+ *    |   (A)    |   (B)    |    (C)    |     (D)       |     (E)       |
9ae3a8
+ *    |  File ID |  Header1 |  Header 2 |  Region Tbl 1 |  Region Tbl 2 |
9ae3a8
+ *    |          |          |           |               |               |
9ae3a8
+ *    .-----------------------------------------------------------------.
9ae3a8
+ *    0         64KB      128KB       192KB           256KB           320KB
9ae3a8
+ *
9ae3a8
+ *
9ae3a8
+ *    .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
9ae3a8
+ *    |     (F)     |     (G)       |    (H)    |                        |
9ae3a8
+ *    | Journal Log |  BAT / Bitmap |  Metadata |  .... data ......      |
9ae3a8
+ *    |             |               |           |                        |
9ae3a8
+ *    .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
9ae3a8
+ *   1MB
9ae3a8
+ */
9ae3a8
+static int vhdx_create(const char *filename, QEMUOptionParameter *options,
9ae3a8
+                       Error **errp)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint64_t image_size = (uint64_t) 2 * GiB;
9ae3a8
+    uint32_t log_size   = 1 * MiB;
9ae3a8
+    uint32_t block_size = 0;
9ae3a8
+    uint64_t signature;
9ae3a8
+    uint64_t metadata_offset;
9ae3a8
+    bool use_zero_blocks = false;
9ae3a8
+
9ae3a8
+    gunichar2 *creator = NULL;
9ae3a8
+    glong creator_items;
9ae3a8
+    BlockDriverState *bs;
9ae3a8
+    const char *type = NULL;
9ae3a8
+    VHDXImageType image_type;
9ae3a8
+    Error *local_err = NULL;
9ae3a8
+
9ae3a8
+    while (options && options->name) {
9ae3a8
+        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
9ae3a8
+            image_size = options->value.n;
9ae3a8
+        } else if (!strcmp(options->name, VHDX_BLOCK_OPT_LOG_SIZE)) {
9ae3a8
+            log_size = options->value.n;
9ae3a8
+        } else if (!strcmp(options->name, VHDX_BLOCK_OPT_BLOCK_SIZE)) {
9ae3a8
+            block_size = options->value.n;
9ae3a8
+        } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
9ae3a8
+            type = options->value.s;
9ae3a8
+        } else if (!strcmp(options->name, VHDX_BLOCK_OPT_ZERO)) {
9ae3a8
+            use_zero_blocks = options->value.n != 0;
9ae3a8
+        }
9ae3a8
+        options++;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (image_size > VHDX_MAX_IMAGE_SIZE) {
9ae3a8
+        error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB");
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (type == NULL) {
9ae3a8
+        type = "dynamic";
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (!strcmp(type, "dynamic")) {
9ae3a8
+        image_type = VHDX_TYPE_DYNAMIC;
9ae3a8
+    } else if (!strcmp(type, "fixed")) {
9ae3a8
+        image_type = VHDX_TYPE_FIXED;
9ae3a8
+    } else if (!strcmp(type, "differencing")) {
9ae3a8
+        error_setg_errno(errp, ENOTSUP,
9ae3a8
+                         "Differencing files not yet supported");
9ae3a8
+        ret = -ENOTSUP;
9ae3a8
+        goto exit;
9ae3a8
+    } else {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* These are pretty arbitrary, and mainly designed to keep the BAT
9ae3a8
+     * size reasonable to load into RAM */
9ae3a8
+    if (block_size == 0) {
9ae3a8
+        if (image_size > 32 * TiB) {
9ae3a8
+            block_size = 64 * MiB;
9ae3a8
+        } else if (image_size > (uint64_t) 100 * GiB) {
9ae3a8
+            block_size = 32 * MiB;
9ae3a8
+        } else if (image_size > 1 * GiB) {
9ae3a8
+            block_size = 16 * MiB;
9ae3a8
+        } else {
9ae3a8
+            block_size = 8 * MiB;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+    /* make the log size close to what was specified, but must be
9ae3a8
+     * min 1MB, and multiple of 1MB */
9ae3a8
+    log_size = ROUND_UP(log_size, MiB);
9ae3a8
+
9ae3a8
+    block_size = ROUND_UP(block_size, MiB);
9ae3a8
+    block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX :
9ae3a8
+                                                    block_size;
9ae3a8
+
9ae3a8
+    ret = bdrv_create_file(filename, options, &local_err);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        error_propagate(errp, local_err);
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        error_propagate(errp, local_err);
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* Create (A) */
9ae3a8
+
9ae3a8
+    /* The creator field is optional, but may be useful for
9ae3a8
+     * debugging / diagnostics */
9ae3a8
+    creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
9ae3a8
+                              &creator_items, NULL);
9ae3a8
+    signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
9ae3a8
+    bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto delete_and_exit;
9ae3a8
+    }
9ae3a8
+    if (creator) {
9ae3a8
+        bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), creator,
9ae3a8
+                    creator_items * sizeof(gunichar2));
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto delete_and_exit;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+    /* Creates (B),(C) */
9ae3a8
+    ret = vhdx_create_new_headers(bs, image_size, log_size);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto delete_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* Creates (D),(E),(G) explicitly. (F) created as by-product */
9ae3a8
+    ret = vhdx_create_new_region_table(bs, image_size, block_size, 512,
9ae3a8
+                                       log_size, use_zero_blocks, image_type,
9ae3a8
+                                       &metadata_offset);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto delete_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* Creates (H) */
9ae3a8
+    ret = vhdx_create_new_metadata(bs, image_size, block_size, 512,
9ae3a8
+                                   metadata_offset, image_type);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto delete_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+
9ae3a8
+delete_and_exit:
9ae3a8
+    bdrv_delete(bs);
9ae3a8
+exit:
9ae3a8
+    g_free(creator);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+static QEMUOptionParameter vhdx_create_options[] = {
9ae3a8
+    {
9ae3a8
+        .name = BLOCK_OPT_SIZE,
9ae3a8
+        .type = OPT_SIZE,
9ae3a8
+        .help = "Virtual disk size; max of 64TB."
9ae3a8
+    },
9ae3a8
+    {
9ae3a8
+        .name = VHDX_BLOCK_OPT_LOG_SIZE,
9ae3a8
+        .type = OPT_SIZE,
9ae3a8
+        .value.n = 1 * MiB,
9ae3a8
+        .help = "Log size; min 1MB."
9ae3a8
+    },
9ae3a8
+    {
9ae3a8
+        .name = VHDX_BLOCK_OPT_BLOCK_SIZE,
9ae3a8
+        .type = OPT_SIZE,
9ae3a8
+        .value.n = 0,
9ae3a8
+        .help = "Block Size; min 1MB, max 256MB. " \
9ae3a8
+                "0 means auto-calculate based on image size."
9ae3a8
+    },
9ae3a8
+    {
9ae3a8
+        .name = BLOCK_OPT_SUBFMT,
9ae3a8
+        .type = OPT_STRING,
9ae3a8
+        .help = "VHDX format type, can be either 'dynamic' or 'fixed'. "\
9ae3a8
+                "Default is 'dynamic'."
9ae3a8
+    },
9ae3a8
+    {
9ae3a8
+        .name = VHDX_BLOCK_OPT_ZERO,
9ae3a8
+        .type = OPT_FLAG,
9ae3a8
+        .help = "Force use of payload blocks of type 'ZERO'.  Non-standard."
9ae3a8
+    },
9ae3a8
+    { NULL }
9ae3a8
+};
9ae3a8
+
9ae3a8
 static BlockDriver bdrv_vhdx = {
9ae3a8
     .format_name            = "vhdx",
9ae3a8
     .instance_size          = sizeof(BDRVVHDXState),
9ae3a8
@@ -1342,6 +1897,8 @@ static BlockDriver bdrv_vhdx = {
9ae3a8
     .bdrv_co_readv          = vhdx_co_readv,
9ae3a8
     .bdrv_co_writev         = vhdx_co_writev,
9ae3a8
     .bdrv_get_info          = vhdx_get_info,
9ae3a8
+    .bdrv_create            = vhdx_create,
9ae3a8
+    .create_options         = vhdx_create_options,
9ae3a8
 };
9ae3a8
 
9ae3a8
 static void bdrv_vhdx_init(void)
9ae3a8
diff --git a/block/vhdx.h b/block/vhdx.h
9ae3a8
index 245547b..365eca0 100644
9ae3a8
--- a/block/vhdx.h
9ae3a8
+++ b/block/vhdx.h
9ae3a8
@@ -18,6 +18,11 @@
9ae3a8
 #ifndef BLOCK_VHDX_H
9ae3a8
 #define BLOCK_VHDX_H
9ae3a8
 
9ae3a8
+#define KiB              (1 * 1024)
9ae3a8
+#define MiB            (KiB * 1024)
9ae3a8
+#define GiB            (MiB * 1024)
9ae3a8
+#define TiB ((uint64_t) GiB * 1024)
9ae3a8
+
9ae3a8
 /* Structures and fields present in the VHDX file */
9ae3a8
 
9ae3a8
 /* The header section has the following blocks,
9ae3a8
@@ -36,8 +41,9 @@
9ae3a8
 #define VHDX_HEADER1_OFFSET         (VHDX_HEADER_BLOCK_SIZE * 1)
9ae3a8
 #define VHDX_HEADER2_OFFSET         (VHDX_HEADER_BLOCK_SIZE * 2)
9ae3a8
 #define VHDX_REGION_TABLE_OFFSET    (VHDX_HEADER_BLOCK_SIZE * 3)
9ae3a8
+#define VHDX_REGION_TABLE2_OFFSET   (VHDX_HEADER_BLOCK_SIZE * 4)
9ae3a8
 
9ae3a8
-
9ae3a8
+#define VHDX_HEADER_SECTION_END     (1 * MiB)
9ae3a8
 /*
9ae3a8
  * A note on the use of MS-GUID fields.  For more details on the GUID,
9ae3a8
  * please see: https://en.wikipedia.org/wiki/Globally_unique_identifier.
9ae3a8
@@ -55,6 +61,7 @@
9ae3a8
 /* These structures are ones that are defined in the VHDX specification
9ae3a8
  * document */
9ae3a8
 
9ae3a8
+#define VHDX_FILE_SIGNATURE 0x656C696678646876  /* "vhdxfile" in ASCII */
9ae3a8
 typedef struct VHDXFileIdentifier {
9ae3a8
     uint64_t    signature;              /* "vhdxfile" in ASCII */
9ae3a8
     uint16_t    creator[256];           /* optional; utf-16 string to identify
9ae3a8
@@ -85,6 +92,7 @@ typedef struct QEMU_PACKED MSGUID {
9ae3a8
 /* The full header is 4KB, although the actual header data is much smaller.
9ae3a8
  * But for the checksum calculation, it is over the entire 4KB structure,
9ae3a8
  * not just the defined portion of it */
9ae3a8
+#define VHDX_HEADER_SIGNATURE 0x64616568
9ae3a8
 typedef struct QEMU_PACKED VHDXHeader {
9ae3a8
     uint32_t    signature;              /* "head" in ASCII */
9ae3a8
     uint32_t    checksum;               /* CRC-32C hash of the whole header */
9ae3a8
@@ -125,6 +133,7 @@ typedef struct QEMU_PACKED VHDXHeader {
9ae3a8
 } VHDXHeader;
9ae3a8
 
9ae3a8
 /* Header for the region table block */
9ae3a8
+#define VHDX_REGION_SIGNATURE  0x69676572  /* "regi" in ASCII */
9ae3a8
 typedef struct QEMU_PACKED VHDXRegionTableHeader {
9ae3a8
     uint32_t    signature;              /* "regi" in ASCII */
9ae3a8
     uint32_t    checksum;               /* CRC-32C hash of the 64KB table */
9ae3a8
@@ -238,6 +247,7 @@ typedef uint64_t VHDXBatEntry;
9ae3a8
 #define VHDX_METADATA_MAX_ENTRIES 2047  /* not including the header */
9ae3a8
 #define VHDX_METADATA_TABLE_MAX_SIZE \
9ae3a8
     (VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1))
9ae3a8
+#define VHDX_METADATA_SIGNATURE 0x617461646174656D  /* "metadata" in ASCII */
9ae3a8
 typedef struct QEMU_PACKED VHDXMetadataTableHeader {
9ae3a8
     uint64_t    signature;              /* "metadata" in ASCII */
9ae3a8
     uint16_t    reserved;
9ae3a8
@@ -267,6 +277,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
9ae3a8
                                                    If set indicates a fixed
9ae3a8
                                                    size VHDX file */
9ae3a8
 #define VHDX_PARAMS_HAS_PARENT           0x02    /* has parent / backing file */
9ae3a8
+#define VHDX_BLOCK_SIZE_MIN             (1   * MiB)
9ae3a8
+#define VHDX_BLOCK_SIZE_MAX             (256 * MiB)
9ae3a8
 typedef struct QEMU_PACKED VHDXFileParameters {
9ae3a8
     uint32_t    block_size;             /* size of each payload block, always
9ae3a8
                                            power of 2, <= 256MB and >= 1MB. */
9ae3a8
@@ -274,6 +286,7 @@ typedef struct QEMU_PACKED VHDXFileParameters {
9ae3a8
                                            the rest are reserved (see above) */
9ae3a8
 } VHDXFileParameters;
9ae3a8
 
9ae3a8
+#define VHDX_MAX_IMAGE_SIZE  ((uint64_t) 64 * TiB)
9ae3a8
 typedef struct QEMU_PACKED VHDXVirtualDiskSize {
9ae3a8
     uint64_t    virtual_disk_size;      /* Size of the virtual disk, in bytes.
9ae3a8
                                            Must be multiple of the sector size,
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8