All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v5 00/16]  VHDX log replay and write support, .bdrv_create()
@ 2013-09-03 19:53 Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 01/16] block: vhdx - minor comments and typo correction Jeff Cody
                   ` (15 more replies)
  0 siblings, 16 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This patch series contains the initial VHDX log parsing, replay,
and write support.  (New with v4: VHDX image file creation)

=== v5 changes ===

v5 is also available for testing from:
https://github.com/codyprime/qemu-kvm-jtc/tree/vhdx-write-v5-upstream

Most of the patches from v4 -> v5 are the same, but there are a few differences
and a few new patches.  Here is a summary of which patches are different and/or
new:

$ ~/work/github/git-scripts/git-series-diff -u vhdx-write-v4-upstream -r qemu/master..HEAD
Key:
[----] : patches are identical
[####] : number of functional differences between patches in -u and -r series
[ new] : patch is new in the range given by -r
The flags [FC] indicate (F)unctional and (C)ontextual differences, respectively

001/16:[----] [--] 'block: vhdx - minor comments and typo correction.'
002/16:[----] [--] 'block: vhdx - add header update capability.'
003/16:[----] [--] 'block: vhdx code movement - VHDXMetadataEntries and BDRVVHDXState to header.'
004/16:[----] [--] 'block: vhdx - log support struct and defines'
005/16:[----] [--] 'block: vhdx - break endian translation functions out'
006/16:[----] [--] 'block: vhdx - update log guid in header, and first write tracker'
007/16:[ new] 'block: vhdx code movement - move vhdx_close() above vhdx_open()'
008/16:[0070] [FC] 'block: vhdx - log parsing, replay, and flush support'
009/16:[ new] 'block: vhdx - add region overlap detection for image files'
010/16:[0003] [FC] 'block: vhdx - add log write support'
011/16:[----] [--] 'block: vhdx write support'
012/16:[ new] 'block: vhdx - remove BAT file offset bit shifting'
013/16:[----] [-C] 'block: vhdx - move more endian translations to vhdx-endian.c'
014/16:[----] [-C] 'block: vhdx - break out code operations to functions'
015/16:[----] [--] 'block: vhdx - fix comment typos in header, fix incorrect struct fields'
016/16:[----] [-C] 'block: vhdx - add .bdrv_create() support'


Patch highlights:

Patch 7  just some minor code movement, in prep for changes in patch 8

Patch 8  incorporates review feedback from Stefan, for the previous Patch 7
         in v4.

Patch 9  adds region checking for log, region table, and metadata tables, per
         suggestion from Stefan.

Patch 10 minor change from changes made in 8/16 (vhdx_guid_is_zero() is gone)

Patch 12 is just some minor housekeeping, to get rid of bit shifting that
         doesn't need to happen.



=== v4 changes ===  

v4 patches are available from github as well, on branch vhdx-write-v4-upstream:
https://github.com/codyprime/qemu-kvm-jtc/tree/vhdx-write-v4-upstream
https://github.com/codyprime/qemu-kvm-jtc.git

Those in the midst of reviewing v3, don't fear - the only changes with v4 is
the addition of patches on the end of the series (patches 10-13).  These
patches enable creating VHDX images.  Image files created have been
(briefly & lightly) tested on Hyper-V running on Windows Server 2012.

Some of the new patches could be squashed with earlier patches in the series,
but I refrained from doing so, since some of the patches have already been
reviewed, and others are in the midst of review.  I want to make it as easy
as possible on those currently reviewing. There is nothing critical
that needs to be pushed into the earlier patches.

New patches:

Patch 10:  Breaks out some more endian translation functions
            (likely squashable into patch 5)

Patch 11:  Break out some operations into seperate helper functions

Patch 12:  More comment typos and header fixes in vhdx.h
            (likely squashable into patch 1)

Patch 13:  Adds .bdrv_create() for vhdx.  VHDX images are can be created for
           Fixed or Dynamic images.

Patches 1-9 are unchanged.

=== end v4 changelog ===

=== v3 changes ===  

Thank you Kevin & Stefan for the feedback; incoporated in v3:

Patch 1: --- nil ---

Patch 2: * use sizeof(crc) instead of 4
         * remove outdated comment
         * use sizeof(MSGUID) instead of 16
         * direct assignment of guid structs rather than memcpy
         * rename 'rw' to 'generate_data_write_guid'
         * use offsetof() instead of 4
         * comment typos
         * add missing error checking
         * MSGUID is now QEMU_PACKED
         * configure enable for vhdx is now correct and not braindead

Patch 3: --- nil ---

Patch 4: * code style fixes
         * removed unused struct (VHDXLogEntryInfo)

Patch 5: * more direct assignment of guid rather than memcpy
         * order of operation in export/import the same now
         * became less generous with newlines (bah-humbug!)

Patch 6: * more direct assignment of guid rather than memcpy     
         * add error check in vhdx_user_visible_write(), now returns int

Patch 7: * check error return now of vhdx_user_visible_write()

Patch 8: * check error return now of vhdx_user_visible_write()
         * vhdx_log_write_sectors() uses bdrv_pwrite() vs bdrv_pwrite_sync()
         * more direct assignment of guid rather than memcpy     
         * use offsetof() instead of 4

Patch 9: --- nil ---

=== end v3 changelog ===

v2 changes:  Incorporated Fam's review feedback - Thank you Fam for the feedback

=== end v2 changelog ===

This will allow an existing log in a VHDX image to be replayed (e.g., a VHDX
image from a Hyper-V host that crashed).  In addition, metadata writes are
enabled through the log.  This allows write support to be enabled for VHDX,
as the BAT can be updated safely via the log journal.

These exact patches are available from github, for testing:
https://github.com/codyprime/qemu-kvm-jtc/tree/vhdx-write-v2-upstream

The latest vhdx work (including anything beyond these patches, such as
backing/parent file support) can be found at:
https://github.com/codyprime/qemu-kvm-jtc/tree/jtc-vhdx-latest

Jeff Cody (16):
  block: vhdx - minor comments and typo correction.
  block: vhdx - add header update capability.
  block: vhdx code movement - VHDXMetadataEntries and BDRVVHDXState to
    header.
  block: vhdx - log support struct and defines
  block: vhdx - break endian translation functions out
  block: vhdx - update log guid in header, and first write tracker
  block: vhdx code movement - move vhdx_close() above vhdx_open()
  block: vhdx - log parsing, replay, and flush support
  block: vhdx - add region overlap detection for image files
  block: vhdx - add log write support
  block: vhdx write support
  block: vhdx - remove BAT file offset bit shifting
  block: vhdx - move more endian translations to vhdx-endian.c
  block: vhdx - break out code operations to functions
  block: vhdx - fix comment typos in header, fix incorrect struct fields
  block: vhdx - add .bdrv_create() support

 block/Makefile.objs |    2 +-
 block/vhdx-endian.c |  216 ++++++++++
 block/vhdx-log.c    |  999 +++++++++++++++++++++++++++++++++++++++++++++
 block/vhdx.c        | 1134 ++++++++++++++++++++++++++++++++++++++++++++-------
 block/vhdx.h        |  176 ++++++--
 configure           |   24 ++
 6 files changed, 2375 insertions(+), 176 deletions(-)
 create mode 100644 block/vhdx-endian.c
 create mode 100644 block/vhdx-log.c

-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 01/16] block: vhdx - minor comments and typo correction.
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 02/16] block: vhdx - add header update capability Jeff Cody
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

Just a couple of minor comments to help note where allocated
buffers are freed, and a typo fix.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 6 ++++--
 block/vhdx.h | 6 +++---
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index e9704b1..56bc88e 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -6,9 +6,9 @@
  * Authors:
  *  Jeff Cody <jcody@redhat.com>
  *
- *  This is based on the "VHDX Format Specification v0.95", published 4/12/2012
+ *  This is based on the "VHDX Format Specification v1.00", published 8/25/2012
  *  by Microsoft:
- *      https://www.microsoft.com/en-us/download/details.aspx?id=29681
+ *      https://www.microsoft.com/en-us/download/details.aspx?id=34750
  *
  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  * See the COPYING.LIB file in the top-level directory.
@@ -262,6 +262,7 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
     uint64_t h2_seq = 0;
     uint8_t *buffer;
 
+    /* header1 & header2 are freed in vhdx_close() */
     header1 = qemu_blockalign(bs, sizeof(VHDXHeader));
     header2 = qemu_blockalign(bs, sizeof(VHDXHeader));
 
@@ -787,6 +788,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
         goto fail;
     }
 
+    /* s->bat is freed in vhdx_close() */
     s->bat = qemu_blockalign(bs, s->bat_rt.length);
 
     ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length);
diff --git a/block/vhdx.h b/block/vhdx.h
index fb687ed..9eb6b97 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -6,9 +6,9 @@
  * Authors:
  *  Jeff Cody <jcody@redhat.com>
  *
- *  This is based on the "VHDX Format Specification v0.95", published 4/12/2012
+ *  This is based on the "VHDX Format Specification v1.00", published 8/25/2012
  *  by Microsoft:
- *      https://www.microsoft.com/en-us/download/details.aspx?id=29681
+ *      https://www.microsoft.com/en-us/download/details.aspx?id=34750
  *
  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  * See the COPYING.LIB file in the top-level directory.
@@ -116,7 +116,7 @@ typedef struct QEMU_PACKED VHDXHeader {
                                            valid. */
     uint16_t    log_version;            /* version of the log format. Mustn't be
                                            zero, unless log_guid is also zero */
-    uint16_t    version;                /* version of th evhdx file.  Currently,
+    uint16_t    version;                /* version of the vhdx file.  Currently,
                                            only supported version is "1" */
     uint32_t    log_length;             /* length of the log.  Must be multiple
                                            of 1MB */
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 02/16] block: vhdx - add header update capability.
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 01/16] block: vhdx - minor comments and typo correction Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 03/16] block: vhdx code movement - VHDXMetadataEntries and BDRVVHDXState to header Jeff Cody
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This adds the ability to update the headers in a VHDX image, including
generating a new MS-compatible GUID.

As VHDX depends on uuid.h, VHDX is now a configurable build option.  If
VHDX support is enabled, that will also enable uuid as well.  The
default is to have VHDX enabled.

To enable/disable VHDX:  --enable-vhdx, --disable-vhdx

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/Makefile.objs |   2 +-
 block/vhdx.c        | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 block/vhdx.h        |  14 ++++-
 configure           |  24 ++++++++
 4 files changed, 196 insertions(+), 5 deletions(-)

diff --git a/block/Makefile.objs b/block/Makefile.objs
index 4cf9aa4..e5e54e6 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -2,7 +2,7 @@ block-obj-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat
 block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
 block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
 block-obj-y += qed-check.o
-block-obj-y += vhdx.o
+block-obj-$(CONFIG_VHDX) += vhdx.o
 block-obj-y += parallels.o blkdebug.o blkverify.o
 block-obj-y += snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
diff --git a/block/vhdx.c b/block/vhdx.c
index 56bc88e..7bd7c12 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -21,6 +21,7 @@
 #include "qemu/crc32c.h"
 #include "block/vhdx.h"
 
+#include <uuid/uuid.h>
 
 /* Several metadata and region table data entries are identified by
  * guids in  a MS-specific GUID format. */
@@ -156,11 +157,40 @@ typedef struct BDRVVHDXState {
     VHDXBatEntry *bat;
     uint64_t bat_offset;
 
+    MSGUID session_guid;
+
+
     VHDXParentLocatorHeader parent_header;
     VHDXParentLocatorEntry *parent_entries;
 
 } BDRVVHDXState;
 
+/* Calculates new checksum.
+ *
+ * Zero is substituted during crc calculation for the original crc field
+ * crc_offset: byte offset in buf of the buffer crc
+ * buf: buffer pointer
+ * size: size of buffer (must be > crc_offset+4)
+ *
+ * Note: The resulting checksum is in the CPU endianness, not necessarily
+ *       in the file format endianness (LE).  Any header export to disk should
+ *       make sure that vhdx_header_le_export() is used to convert to the
+ *       correct endianness
+ */
+uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset)
+{
+    uint32_t crc;
+
+    assert(buf != NULL);
+    assert(size > (crc_offset + sizeof(crc)));
+
+    memset(buf + crc_offset, 0, sizeof(crc));
+    crc =  crc32c(0xffffffff, buf, size);
+    memcpy(buf + crc_offset, &crc, sizeof(crc));
+
+    return crc;
+}
+
 uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
                             int crc_offset)
 {
@@ -212,6 +242,19 @@ bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset)
 
 
 /*
+ * This generates a UUID that is compliant with the MS GUIDs used
+ * in the VHDX spec (and elsewhere).
+ */
+void vhdx_guid_generate(MSGUID *guid)
+{
+    uuid_t uuid;
+    assert(guid != NULL);
+
+    uuid_generate(uuid);
+    memcpy(guid, uuid, sizeof(MSGUID));
+}
+
+/*
  * Per the MS VHDX Specification, for every VHDX file:
  *      - The header section is fixed size - 1 MB
  *      - The header section is always the first "object"
@@ -249,6 +292,113 @@ static void vhdx_header_le_import(VHDXHeader *h)
     le64_to_cpus(&h->log_offset);
 }
 
+/* All VHDX structures on disk are little endian */
+static void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
+{
+    assert(orig_h != NULL);
+    assert(new_h != NULL);
+
+    new_h->signature       = cpu_to_le32(orig_h->signature);
+    new_h->checksum        = cpu_to_le32(orig_h->checksum);
+    new_h->sequence_number = cpu_to_le64(orig_h->sequence_number);
+
+    new_h->file_write_guid = orig_h->file_write_guid;
+    new_h->data_write_guid = orig_h->data_write_guid;
+    new_h->log_guid        = orig_h->log_guid;
+
+    cpu_to_leguids(&new_h->file_write_guid);
+    cpu_to_leguids(&new_h->data_write_guid);
+    cpu_to_leguids(&new_h->log_guid);
+
+    new_h->log_version     = cpu_to_le16(orig_h->log_version);
+    new_h->version         = cpu_to_le16(orig_h->version);
+    new_h->log_length      = cpu_to_le32(orig_h->log_length);
+    new_h->log_offset      = cpu_to_le64(orig_h->log_offset);
+}
+
+/* Update the VHDX headers
+ *
+ * This follows the VHDX spec procedures for header updates.
+ *
+ *  - non-current header is updated with largest sequence number
+ */
+static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
+                              bool generate_data_write_guid)
+{
+    int ret = 0;
+    int hdr_idx = 0;
+    uint64_t header_offset = VHDX_HEADER1_OFFSET;
+
+    VHDXHeader *active_header;
+    VHDXHeader *inactive_header;
+    VHDXHeader header_le;
+    uint8_t *buffer;
+
+    /* operate on the non-current header */
+    if (s->curr_header == 0) {
+        hdr_idx = 1;
+        header_offset = VHDX_HEADER2_OFFSET;
+    }
+
+    active_header   = s->headers[s->curr_header];
+    inactive_header = s->headers[hdr_idx];
+
+    inactive_header->sequence_number = active_header->sequence_number + 1;
+
+    /* a new file guid must be generated before any file write, including
+     * headers */
+    inactive_header->file_write_guid = s->session_guid;
+
+    /* a new data guid only needs to be generated before any guest-visible
+     * writes (i.e. something observable via virtual disk read) */
+    if (generate_data_write_guid) {
+        vhdx_guid_generate(&inactive_header->data_write_guid);
+    }
+
+    /* the header checksum is not over just the packed size of VHDXHeader,
+     * but rather over the entire 'reserved' range for the header, which is
+     * 4KB (VHDX_HEADER_SIZE). */
+
+    buffer = qemu_blockalign(bs, VHDX_HEADER_SIZE);
+    /* we can't assume the extra reserved bytes are 0 */
+    ret = bdrv_pread(bs->file, header_offset, buffer, VHDX_HEADER_SIZE);
+    if (ret < 0) {
+        goto exit;
+    }
+    /* overwrite the actual VHDXHeader portion */
+    memcpy(buffer, inactive_header, sizeof(VHDXHeader));
+    inactive_header->checksum =
+                        vhdx_update_checksum(buffer, VHDX_HEADER_SIZE,
+                                             offsetof(VHDXHeader, checksum));
+    vhdx_header_le_export(inactive_header, &header_le);
+    ret = bdrv_pwrite_sync(bs->file, header_offset, &header_le,
+                           sizeof(VHDXHeader));
+    if (ret < 0) {
+        goto exit;
+    }
+    s->curr_header = hdr_idx;
+
+exit:
+    qemu_vfree(buffer);
+    return ret;
+}
+
+/*
+ * The VHDX spec calls for header updates to be performed twice, so that both
+ * the current and non-current header have valid info
+ */
+static int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s,
+                               bool generate_data_write_guid)
+{
+    int ret;
+
+    ret = vhdx_update_header(bs, s, generate_data_write_guid);
+    if (ret < 0) {
+        return ret;
+    }
+    ret = vhdx_update_header(bs, s, generate_data_write_guid);
+    return ret;
+}
 
 /* opens the specified header block from the VHDX file header section */
 static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
@@ -739,6 +889,11 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
         goto fail;
     }
 
+    /* This is used for any header updates, for the file_write_guid.
+     * The spec dictates that a new value should be used for the first
+     * header update */
+    vhdx_guid_generate(&s->session_guid);
+
     ret = vhdx_parse_header(bs, s);
     if (ret) {
         goto fail;
@@ -801,8 +956,10 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
     }
 
     if (flags & BDRV_O_RDWR) {
-        ret = -ENOTSUP;
-        goto fail;
+        ret = vhdx_update_headers(bs, s, false);
+        if (ret < 0) {
+            goto fail;
+        }
     }
 
     /* TODO: differencing files, write */
diff --git a/block/vhdx.h b/block/vhdx.h
index 9eb6b97..403f766 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -67,7 +67,7 @@ typedef struct VHDXFileIdentifier {
  * Microsoft is not just 16 bytes though - it is a structure that is defined,
  * so we need to follow it here so that endianness does not trip us up */
 
-typedef struct MSGUID {
+typedef struct QEMU_PACKED MSGUID {
     uint32_t  data1;
     uint16_t  data2;
     uint16_t  data3;
@@ -309,17 +309,27 @@ typedef struct QEMU_PACKED VHDXParentLocatorEntry {
 /* ----- END VHDX SPECIFICATION STRUCTURES ---- */
 
 
+void vhdx_guid_generate(MSGUID *guid);
+
+uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset);
 uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
                             int crc_offset);
 
 bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset);
 
 
-static void leguid_to_cpus(MSGUID *guid)
+static inline void leguid_to_cpus(MSGUID *guid)
 {
     le32_to_cpus(&guid->data1);
     le16_to_cpus(&guid->data2);
     le16_to_cpus(&guid->data3);
 }
 
+static inline void cpu_to_leguids(MSGUID *guid)
+{
+    cpu_to_le32s(&guid->data1);
+    cpu_to_le16s(&guid->data2);
+    cpu_to_le16s(&guid->data3);
+}
+
 #endif
diff --git a/configure b/configure
index 0a55c20..86a74e3 100755
--- a/configure
+++ b/configure
@@ -243,6 +243,7 @@ gtk=""
 gtkabi="2.0"
 tpm="no"
 libssh2=""
+vhdx=""
 
 # parse CC options first
 for opt do
@@ -945,6 +946,10 @@ for opt do
   ;;
   --enable-libssh2) libssh2="yes"
   ;;
+  --enable-vhdx) vhdx="yes"
+  ;;
+  --disable-vhdx) vhdx="no"
+  ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
@@ -1159,6 +1164,8 @@ echo "  --gcov=GCOV              use specified gcov [$gcov_tool]"
 echo "  --enable-tpm             enable TPM support"
 echo "  --disable-libssh2        disable ssh block device support"
 echo "  --enable-libssh2         enable ssh block device support"
+echo "  --disable-vhdx           disables support for the Microsoft VHDX image format"
+echo "  --enable-vhdx            enable support for the Microsoft VHDX image format"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -1970,6 +1977,18 @@ EOF
   fi
 fi
 
+if test "$vhdx" = "yes" ; then
+    if test "$uuid" = "no" ; then
+        error_exit "uuid required for VHDX support"
+    fi
+elif test "$vhdx" != "no" ; then
+    if test "$uuid" = "yes" ; then
+        vhdx=yes
+    else
+        vhdx=no
+    fi
+fi
+
 ##########################################
 # xfsctl() probe, used for raw-posix
 if test "$xfs" != "no" ; then
@@ -3644,6 +3663,7 @@ echo "TPM support       $tpm"
 echo "libssh2 support   $libssh2"
 echo "TPM passthrough   $tpm_passthrough"
 echo "QOM debugging     $qom_cast_debug"
+echo "vhdx              $vhdx"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -4040,6 +4060,10 @@ if test "$virtio_blk_data_plane" = "yes" ; then
   echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak
 fi
 
+if test "$vhdx" = "yes" ; then
+  echo "CONFIG_VHDX=y" >> $config_host_mak
+fi
+
 # USB host support
 case "$usb" in
 linux)
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 03/16] block: vhdx code movement - VHDXMetadataEntries and BDRVVHDXState to header.
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 01/16] block: vhdx - minor comments and typo correction Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 02/16] block: vhdx - add header update capability Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 04/16] block: vhdx - log support struct and defines Jeff Cody
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

In preparation for VHDX log support, move these structures to the
header.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 51 ---------------------------------------------------
 block/vhdx.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 51 deletions(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index 7bd7c12..68648e1 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -104,16 +104,6 @@ static const MSGUID parent_vhdx_guid = { .data1 = 0xb04aefb7,
      META_PAGE_83_PRESENT | META_LOGICAL_SECTOR_SIZE_PRESENT | \
      META_PHYS_SECTOR_SIZE_PRESENT)
 
-typedef struct VHDXMetadataEntries {
-    VHDXMetadataTableEntry file_parameters_entry;
-    VHDXMetadataTableEntry virtual_disk_size_entry;
-    VHDXMetadataTableEntry page83_data_entry;
-    VHDXMetadataTableEntry logical_sector_size_entry;
-    VHDXMetadataTableEntry phys_sector_size_entry;
-    VHDXMetadataTableEntry parent_locator_entry;
-    uint16_t present;
-} VHDXMetadataEntries;
-
 
 typedef struct VHDXSectorInfo {
     uint32_t bat_idx;       /* BAT entry index */
@@ -124,47 +114,6 @@ typedef struct VHDXSectorInfo {
     uint64_t block_offset;  /* block offset, in bytes */
 } VHDXSectorInfo;
 
-
-
-typedef struct BDRVVHDXState {
-    CoMutex lock;
-
-    int curr_header;
-    VHDXHeader *headers[2];
-
-    VHDXRegionTableHeader rt;
-    VHDXRegionTableEntry bat_rt;         /* region table for the BAT */
-    VHDXRegionTableEntry metadata_rt;    /* region table for the metadata */
-
-    VHDXMetadataTableHeader metadata_hdr;
-    VHDXMetadataEntries metadata_entries;
-
-    VHDXFileParameters params;
-    uint32_t block_size;
-    uint32_t block_size_bits;
-    uint32_t sectors_per_block;
-    uint32_t sectors_per_block_bits;
-
-    uint64_t virtual_disk_size;
-    uint32_t logical_sector_size;
-    uint32_t physical_sector_size;
-
-    uint64_t chunk_ratio;
-    uint32_t chunk_ratio_bits;
-    uint32_t logical_sector_size_bits;
-
-    uint32_t bat_entries;
-    VHDXBatEntry *bat;
-    uint64_t bat_offset;
-
-    MSGUID session_guid;
-
-
-    VHDXParentLocatorHeader parent_header;
-    VHDXParentLocatorEntry *parent_entries;
-
-} BDRVVHDXState;
-
 /* Calculates new checksum.
  *
  * Zero is substituted during crc calculation for the original crc field
diff --git a/block/vhdx.h b/block/vhdx.h
index 403f766..74b2d5d 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -308,6 +308,53 @@ typedef struct QEMU_PACKED VHDXParentLocatorEntry {
 
 /* ----- END VHDX SPECIFICATION STRUCTURES ---- */
 
+typedef struct VHDXMetadataEntries {
+    VHDXMetadataTableEntry file_parameters_entry;
+    VHDXMetadataTableEntry virtual_disk_size_entry;
+    VHDXMetadataTableEntry page83_data_entry;
+    VHDXMetadataTableEntry logical_sector_size_entry;
+    VHDXMetadataTableEntry phys_sector_size_entry;
+    VHDXMetadataTableEntry parent_locator_entry;
+    uint16_t present;
+} VHDXMetadataEntries;
+
+typedef struct BDRVVHDXState {
+    CoMutex lock;
+
+    int curr_header;
+    VHDXHeader *headers[2];
+
+    VHDXRegionTableHeader rt;
+    VHDXRegionTableEntry bat_rt;         /* region table for the BAT */
+    VHDXRegionTableEntry metadata_rt;    /* region table for the metadata */
+
+    VHDXMetadataTableHeader metadata_hdr;
+    VHDXMetadataEntries metadata_entries;
+
+    VHDXFileParameters params;
+    uint32_t block_size;
+    uint32_t block_size_bits;
+    uint32_t sectors_per_block;
+    uint32_t sectors_per_block_bits;
+
+    uint64_t virtual_disk_size;
+    uint32_t logical_sector_size;
+    uint32_t physical_sector_size;
+
+    uint64_t chunk_ratio;
+    uint32_t chunk_ratio_bits;
+    uint32_t logical_sector_size_bits;
+
+    uint32_t bat_entries;
+    VHDXBatEntry *bat;
+    uint64_t bat_offset;
+
+    MSGUID session_guid;
+
+    VHDXParentLocatorHeader parent_header;
+    VHDXParentLocatorEntry *parent_entries;
+
+} BDRVVHDXState;
 
 void vhdx_guid_generate(MSGUID *guid);
 
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 04/16] block: vhdx - log support struct and defines
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (2 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 03/16] block: vhdx code movement - VHDXMetadataEntries and BDRVVHDXState to header Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 05/16] block: vhdx - break endian translation functions out Jeff Cody
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This adds some magic number defines, and internal structure definitions
for VHDX log replay support.  The struct VHDXLogEntries does not reflect
an on-disk data structure, and thus does not need to be packed.

Some minor code style fixes are applied as well.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.h | 46 ++++++++++++++++++++++++++++++----------------
 1 file changed, 30 insertions(+), 16 deletions(-)

diff --git a/block/vhdx.h b/block/vhdx.h
index 74b2d5d..0ab8bf3 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -30,12 +30,12 @@
  * 0.........64KB...........128KB........192KB..........256KB................1MB
  */
 
-#define VHDX_HEADER_BLOCK_SIZE      (64*1024)
+#define VHDX_HEADER_BLOCK_SIZE      (64 * 1024)
 
 #define VHDX_FILE_ID_OFFSET         0
-#define VHDX_HEADER1_OFFSET         (VHDX_HEADER_BLOCK_SIZE*1)
-#define VHDX_HEADER2_OFFSET         (VHDX_HEADER_BLOCK_SIZE*2)
-#define VHDX_REGION_TABLE_OFFSET    (VHDX_HEADER_BLOCK_SIZE*3)
+#define VHDX_HEADER1_OFFSET         (VHDX_HEADER_BLOCK_SIZE * 1)
+#define VHDX_HEADER2_OFFSET         (VHDX_HEADER_BLOCK_SIZE * 2)
+#define VHDX_REGION_TABLE_OFFSET    (VHDX_HEADER_BLOCK_SIZE * 3)
 
 
 /*
@@ -77,10 +77,10 @@ typedef struct QEMU_PACKED MSGUID {
 #define guid_eq(a, b) \
     (memcmp(&(a), &(b), sizeof(MSGUID)) == 0)
 
-#define VHDX_HEADER_SIZE (4*1024)   /* although the vhdx_header struct in disk
-                                       is only 582 bytes, for purposes of crc
-                                       the header is the first 4KB of the 64KB
-                                       block */
+#define VHDX_HEADER_SIZE (4 * 1024)   /* although the vhdx_header struct in disk
+                                         is only 582 bytes, for purposes of crc
+                                         the header is the first 4KB of the 64KB
+                                         block */
 
 /* The full header is 4KB, although the actual header data is much smaller.
  * But for the checksum calculation, it is over the entire 4KB structure,
@@ -92,7 +92,7 @@ typedef struct QEMU_PACKED VHDXHeader {
                                            VHDX file has 2 of these headers,
                                            and only the header with the highest
                                            sequence number is valid */
-    MSGUID      file_write_guid;       /* 128 bit unique identifier. Must be
+    MSGUID      file_write_guid;        /* 128 bit unique identifier. Must be
                                            updated to new, unique value before
                                            the first modification is made to
                                            file */
@@ -151,7 +151,10 @@ typedef struct QEMU_PACKED VHDXRegionTableEntry {
 
 
 /* ---- LOG ENTRY STRUCTURES ---- */
+#define VHDX_LOG_MIN_SIZE (1024 * 1024)
+#define VHDX_LOG_SECTOR_SIZE 4096
 #define VHDX_LOG_HDR_SIZE 64
+#define VHDX_LOG_SIGNATURE 0x65676f6c
 typedef struct QEMU_PACKED VHDXLogEntryHeader {
     uint32_t    signature;              /* "loge" in ASCII */
     uint32_t    checksum;               /* CRC-32C hash of the 64KB table */
@@ -174,7 +177,8 @@ typedef struct QEMU_PACKED VHDXLogEntryHeader {
 } VHDXLogEntryHeader;
 
 #define VHDX_LOG_DESC_SIZE 32
-
+#define VHDX_LOG_DESC_SIGNATURE 0x63736564
+#define VHDX_LOG_ZERO_SIGNATURE 0x6f72657a
 typedef struct QEMU_PACKED VHDXLogDescriptor {
     uint32_t    signature;              /* "zero" or "desc" in ASCII */
     union  {
@@ -194,6 +198,7 @@ typedef struct QEMU_PACKED VHDXLogDescriptor {
                                            vhdx_log_entry_header */
 } VHDXLogDescriptor;
 
+#define VHDX_LOG_DATA_SIGNATURE 0x61746164
 typedef struct QEMU_PACKED VHDXLogDataSector {
     uint32_t    data_signature;         /* "data" in ASCII */
     uint32_t    sequence_high;          /* 4 MSB of 8 byte sequence_number */
@@ -219,12 +224,12 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
 #define SB_BLOCK_PRESENT        6
 
 /* per the spec */
-#define VHDX_MAX_SECTORS_PER_BLOCK  (1<<23)
+#define VHDX_MAX_SECTORS_PER_BLOCK  (1 << 23)
 
 /* upper 44 bits are the file offset in 1MB units lower 3 bits are the state
    other bits are reserved */
 #define VHDX_BAT_STATE_BIT_MASK 0x07
-#define VHDX_BAT_FILE_OFF_BITS (64-44)
+#define VHDX_BAT_FILE_OFF_BITS (64 - 44)
 typedef uint64_t VHDXBatEntry;
 
 /* ---- METADATA REGION STRUCTURES ---- */
@@ -252,8 +257,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
                                            metadata region */
                                         /* note: if length = 0, so is offset */
     uint32_t    length;                 /* length of metadata. <= 1MB. */
-    uint32_t    data_bits;      /* least-significant 3 bits are flags, the
-                                   rest are reserved (see above) */
+    uint32_t    data_bits;              /* least-significant 3 bits are flags,
+                                           the rest are reserved (see above) */
     uint32_t    reserved2;
 } VHDXMetadataTableEntry;
 
@@ -265,8 +270,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
 typedef struct QEMU_PACKED VHDXFileParameters {
     uint32_t    block_size;             /* size of each payload block, always
                                            power of 2, <= 256MB and >= 1MB. */
-    uint32_t data_bits;     /* least-significant 2 bits are flags, the rest
-                               are reserved (see above) */
+    uint32_t data_bits;                 /* least-significant 2 bits are flags,
+                                           the rest are reserved (see above) */
 } VHDXFileParameters;
 
 typedef struct QEMU_PACKED VHDXVirtualDiskSize {
@@ -318,6 +323,13 @@ typedef struct VHDXMetadataEntries {
     uint16_t present;
 } VHDXMetadataEntries;
 
+typedef struct VHDXLogEntries {
+    uint64_t offset;
+    uint64_t length;
+    uint32_t head;
+    uint32_t tail;
+} VHDXLogEntries;
+
 typedef struct BDRVVHDXState {
     CoMutex lock;
 
@@ -351,6 +363,8 @@ typedef struct BDRVVHDXState {
 
     MSGUID session_guid;
 
+    VHDXLogEntries log;
+
     VHDXParentLocatorHeader parent_header;
     VHDXParentLocatorEntry *parent_entries;
 
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 05/16] block: vhdx - break endian translation functions out
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (3 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 04/16] block: vhdx - log support struct and defines Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 06/16] block: vhdx - update log guid in header, and first write tracker Jeff Cody
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This moves the endian translation functions out from the vhdx.c source,
into a separate source file. In addition to the previously defined
endian functions, new endian translation functions for log support are
added as well.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/Makefile.objs |   2 +-
 block/vhdx-endian.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/vhdx.c        |  43 ----------------
 block/vhdx.h        |   8 +++
 4 files changed, 150 insertions(+), 44 deletions(-)
 create mode 100644 block/vhdx-endian.c

diff --git a/block/Makefile.objs b/block/Makefile.objs
index e5e54e6..e6f5d33 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -2,7 +2,7 @@ block-obj-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat
 block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
 block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
 block-obj-y += qed-check.o
-block-obj-$(CONFIG_VHDX) += vhdx.o
+block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o
 block-obj-y += parallels.o blkdebug.o blkverify.o
 block-obj-y += snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c
new file mode 100644
index 0000000..3e93e63
--- /dev/null
+++ b/block/vhdx-endian.c
@@ -0,0 +1,141 @@
+/*
+ * Block driver for Hyper-V VHDX Images
+ *
+ * Copyright (c) 2013 Red Hat, Inc.,
+ *
+ * Authors:
+ *  Jeff Cody <jcody@redhat.com>
+ *
+ *  This is based on the "VHDX Format Specification v1.00", published 8/25/2012
+ *  by Microsoft:
+ *      https://www.microsoft.com/en-us/download/details.aspx?id=34750
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "block/block_int.h"
+#include "block/vhdx.h"
+
+#include <uuid/uuid.h>
+
+
+/*
+ * All the VHDX formats on disk are little endian - the following
+ * are helper import/export functions to correctly convert
+ * endianness from disk read to native cpu format, and back again.
+ */
+
+
+/* VHDX File Header */
+
+
+void vhdx_header_le_import(VHDXHeader *h)
+{
+    assert(h != NULL);
+
+    le32_to_cpus(&h->signature);
+    le32_to_cpus(&h->checksum);
+    le64_to_cpus(&h->sequence_number);
+
+    leguid_to_cpus(&h->file_write_guid);
+    leguid_to_cpus(&h->data_write_guid);
+    leguid_to_cpus(&h->log_guid);
+
+    le16_to_cpus(&h->log_version);
+    le16_to_cpus(&h->version);
+    le32_to_cpus(&h->log_length);
+    le64_to_cpus(&h->log_offset);
+}
+
+void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
+{
+    assert(orig_h != NULL);
+    assert(new_h != NULL);
+
+    new_h->signature       = cpu_to_le32(orig_h->signature);
+    new_h->checksum        = cpu_to_le32(orig_h->checksum);
+    new_h->sequence_number = cpu_to_le64(orig_h->sequence_number);
+
+    new_h->file_write_guid = orig_h->file_write_guid;
+    new_h->data_write_guid = orig_h->data_write_guid;
+    new_h->log_guid        = orig_h->log_guid;
+
+    cpu_to_leguids(&new_h->file_write_guid);
+    cpu_to_leguids(&new_h->data_write_guid);
+    cpu_to_leguids(&new_h->log_guid);
+
+    new_h->log_version     = cpu_to_le16(orig_h->log_version);
+    new_h->version         = cpu_to_le16(orig_h->version);
+    new_h->log_length      = cpu_to_le32(orig_h->log_length);
+    new_h->log_offset      = cpu_to_le64(orig_h->log_offset);
+}
+
+
+/* VHDX Log Headers */
+
+
+void vhdx_log_desc_le_import(VHDXLogDescriptor *d)
+{
+    assert(d != NULL);
+
+    le32_to_cpus(&d->signature);
+    le32_to_cpus(&d->trailing_bytes);
+    le64_to_cpus(&d->leading_bytes);
+    le64_to_cpus(&d->file_offset);
+    le64_to_cpus(&d->sequence_number);
+}
+
+void vhdx_log_desc_le_export(VHDXLogDescriptor *d)
+{
+    assert(d != NULL);
+
+    cpu_to_le32s(&d->signature);
+    cpu_to_le32s(&d->trailing_bytes);
+    cpu_to_le64s(&d->leading_bytes);
+    cpu_to_le64s(&d->file_offset);
+    cpu_to_le64s(&d->sequence_number);
+}
+
+void vhdx_log_data_le_export(VHDXLogDataSector *d)
+{
+    assert(d != NULL);
+
+    cpu_to_le32s(&d->data_signature);
+    cpu_to_le32s(&d->sequence_high);
+    cpu_to_le32s(&d->sequence_low);
+}
+
+void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr)
+{
+    assert(hdr != NULL);
+
+    le32_to_cpus(&hdr->signature);
+    le32_to_cpus(&hdr->checksum);
+    le32_to_cpus(&hdr->entry_length);
+    le32_to_cpus(&hdr->tail);
+    le64_to_cpus(&hdr->sequence_number);
+    le32_to_cpus(&hdr->descriptor_count);
+    leguid_to_cpus(&hdr->log_guid);
+    le64_to_cpus(&hdr->flushed_file_offset);
+    le64_to_cpus(&hdr->last_file_offset);
+}
+
+void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr)
+{
+    assert(hdr != NULL);
+
+    cpu_to_le32s(&hdr->signature);
+    cpu_to_le32s(&hdr->checksum);
+    cpu_to_le32s(&hdr->entry_length);
+    cpu_to_le32s(&hdr->tail);
+    cpu_to_le64s(&hdr->sequence_number);
+    cpu_to_le32s(&hdr->descriptor_count);
+    cpu_to_leguids(&hdr->log_guid);
+    cpu_to_le64s(&hdr->flushed_file_offset);
+    cpu_to_le64s(&hdr->last_file_offset);
+}
+
+
diff --git a/block/vhdx.c b/block/vhdx.c
index 68648e1..15a4d1d 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -222,49 +222,6 @@ static int vhdx_probe(const uint8_t *buf, int buf_size, const char *filename)
     return 0;
 }
 
-/* All VHDX structures on disk are little endian */
-static void vhdx_header_le_import(VHDXHeader *h)
-{
-    assert(h != NULL);
-
-    le32_to_cpus(&h->signature);
-    le32_to_cpus(&h->checksum);
-    le64_to_cpus(&h->sequence_number);
-
-    leguid_to_cpus(&h->file_write_guid);
-    leguid_to_cpus(&h->data_write_guid);
-    leguid_to_cpus(&h->log_guid);
-
-    le16_to_cpus(&h->log_version);
-    le16_to_cpus(&h->version);
-    le32_to_cpus(&h->log_length);
-    le64_to_cpus(&h->log_offset);
-}
-
-/* All VHDX structures on disk are little endian */
-static void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
-{
-    assert(orig_h != NULL);
-    assert(new_h != NULL);
-
-    new_h->signature       = cpu_to_le32(orig_h->signature);
-    new_h->checksum        = cpu_to_le32(orig_h->checksum);
-    new_h->sequence_number = cpu_to_le64(orig_h->sequence_number);
-
-    new_h->file_write_guid = orig_h->file_write_guid;
-    new_h->data_write_guid = orig_h->data_write_guid;
-    new_h->log_guid        = orig_h->log_guid;
-
-    cpu_to_leguids(&new_h->file_write_guid);
-    cpu_to_leguids(&new_h->data_write_guid);
-    cpu_to_leguids(&new_h->log_guid);
-
-    new_h->log_version     = cpu_to_le16(orig_h->log_version);
-    new_h->version         = cpu_to_le16(orig_h->version);
-    new_h->log_length      = cpu_to_le32(orig_h->log_length);
-    new_h->log_offset      = cpu_to_le64(orig_h->log_offset);
-}
-
 /* Update the VHDX headers
  *
  * This follows the VHDX spec procedures for header updates.
diff --git a/block/vhdx.h b/block/vhdx.h
index 0ab8bf3..89d9a78 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -393,4 +393,12 @@ static inline void cpu_to_leguids(MSGUID *guid)
     cpu_to_le16s(&guid->data3);
 }
 
+void vhdx_header_le_import(VHDXHeader *h);
+void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h);
+void vhdx_log_desc_le_import(VHDXLogDescriptor *d);
+void vhdx_log_desc_le_export(VHDXLogDescriptor *d);
+void vhdx_log_data_le_export(VHDXLogDataSector *d);
+void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr);
+void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr);
+
 #endif
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 06/16] block: vhdx - update log guid in header, and first write tracker
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (4 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 05/16] block: vhdx - break endian translation functions out Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 07/16] block: vhdx code movement - move vhdx_close() above vhdx_open() Jeff Cody
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

Allow tracking of first file write in the VHDX image, as well as
the ability to update the GUID in the header.  This is in preparation
for log support.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 30 ++++++++++++++++++++++++------
 block/vhdx.h |  6 ++++++
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index 15a4d1d..4dc056b 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -229,7 +229,7 @@ static int vhdx_probe(const uint8_t *buf, int buf_size, const char *filename)
  *  - non-current header is updated with largest sequence number
  */
 static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
-                              bool generate_data_write_guid)
+                              bool generate_data_write_guid, MSGUID *log_guid)
 {
     int ret = 0;
     int hdr_idx = 0;
@@ -261,6 +261,11 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
         vhdx_guid_generate(&inactive_header->data_write_guid);
     }
 
+    /* update the log guid if present */
+    if (log_guid) {
+        inactive_header->log_guid = *log_guid;
+    }
+
     /* the header checksum is not over just the packed size of VHDXHeader,
      * but rather over the entire 'reserved' range for the header, which is
      * 4KB (VHDX_HEADER_SIZE). */
@@ -293,16 +298,16 @@ exit:
  * The VHDX spec calls for header updates to be performed twice, so that both
  * the current and non-current header have valid info
  */
-static int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s,
-                               bool generate_data_write_guid)
+int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s,
+                        bool generate_data_write_guid, MSGUID *log_guid)
 {
     int ret;
 
-    ret = vhdx_update_header(bs, s, generate_data_write_guid);
+    ret = vhdx_update_header(bs, s, generate_data_write_guid, log_guid);
     if (ret < 0) {
         return ret;
     }
-    ret = vhdx_update_header(bs, s, generate_data_write_guid);
+    ret = vhdx_update_header(bs, s, generate_data_write_guid, log_guid);
     return ret;
 }
 
@@ -782,6 +787,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
 
 
     s->bat = NULL;
+    s->first_visible_write = true;
 
     qemu_co_mutex_init(&s->lock);
 
@@ -862,7 +868,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
     }
 
     if (flags & BDRV_O_RDWR) {
-        ret = vhdx_update_headers(bs, s, false);
+        ret = vhdx_update_headers(bs, s, false, NULL);
         if (ret < 0) {
             goto fail;
         }
@@ -1002,6 +1008,18 @@ exit:
 
 
 
+/* Per the spec, on the first write of guest-visible data to the file the
+ * data write guid must be updated in the header */
+int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s)
+{
+    int ret = 0;
+    if (s->first_visible_write) {
+        s->first_visible_write = false;
+        ret = vhdx_update_headers(bs, s, true, NULL);
+    }
+    return ret;
+}
+
 static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
                                       int nb_sectors, QEMUIOVector *qiov)
 {
diff --git a/block/vhdx.h b/block/vhdx.h
index 89d9a78..8c61bfd 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -361,6 +361,7 @@ typedef struct BDRVVHDXState {
     VHDXBatEntry *bat;
     uint64_t bat_offset;
 
+    bool first_visible_write;
     MSGUID session_guid;
 
     VHDXLogEntries log;
@@ -372,6 +373,9 @@ typedef struct BDRVVHDXState {
 
 void vhdx_guid_generate(MSGUID *guid);
 
+int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw,
+                        MSGUID *log_guid);
+
 uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset);
 uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
                             int crc_offset);
@@ -401,4 +405,6 @@ void vhdx_log_data_le_export(VHDXLogDataSector *d);
 void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr);
 void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr);
 
+int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s);
+
 #endif
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 07/16] block: vhdx code movement - move vhdx_close() above vhdx_open()
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (5 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 06/16] block: vhdx - update log guid in header, and first write tracker Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 08/16] block: vhdx - log parsing, replay, and flush support Jeff Cody
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha


Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index 4dc056b..9db6531 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -777,6 +777,15 @@ exit:
 }
 
 
+static void vhdx_close(BlockDriverState *bs)
+{
+    BDRVVHDXState *s = bs->opaque;
+    qemu_vfree(s->headers[0]);
+    qemu_vfree(s->headers[1]);
+    qemu_vfree(s->bat);
+    qemu_vfree(s->parent_entries);
+}
+
 static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
 {
     BDRVVHDXState *s = bs->opaque;
@@ -1027,15 +1036,6 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
 }
 
 
-static void vhdx_close(BlockDriverState *bs)
-{
-    BDRVVHDXState *s = bs->opaque;
-    qemu_vfree(s->headers[0]);
-    qemu_vfree(s->headers[1]);
-    qemu_vfree(s->bat);
-    qemu_vfree(s->parent_entries);
-}
-
 static BlockDriver bdrv_vhdx = {
     .format_name            = "vhdx",
     .instance_size          = sizeof(BDRVVHDXState),
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 08/16] block: vhdx - log parsing, replay, and flush support
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (6 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 07/16] block: vhdx code movement - move vhdx_close() above vhdx_open() Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 09/16] block: vhdx - add region overlap detection for image files Jeff Cody
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This adds support for VHDX v0 logs, as specified in Microsoft's
VHDX Specification Format v1.00:
https://www.microsoft.com/en-us/download/details.aspx?id=34750

The following support is added:

* Log parsing, and validation - validate that an existing log
  is correct.

* Log search - search through an existing log, to find any valid
  sequence of entries.

* Log replay and flush - replay an existing log, and flush/clear
  the log when complete.

The VHDX log is a circular buffer, with elements (sectors) of 4KB.

A log entry is a variably-length number of sectors, that is
comprised of a header and 'descriptors', that describe each sector.

A log may contain multiple entries, know as a log sequence.  In a log
sequence, each log entry immediately follows the previous entry, with an
incrementing sequence number.  There can only ever be one active and
valid sequence in the log.

Each log entry must match the file log GUID in order to be valid (along
with other criteria).  Once we have flushed all valid log entries, we
marked the file log GUID to be zero, which indicates a buffer with no
valid entries.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/Makefile.objs |   2 +-
 block/vhdx-log.c    | 723 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/vhdx.c        |  69 ++---
 block/vhdx.h        |   7 +-
 4 files changed, 750 insertions(+), 51 deletions(-)
 create mode 100644 block/vhdx-log.c

diff --git a/block/Makefile.objs b/block/Makefile.objs
index e6f5d33..2fbd79a 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -2,7 +2,7 @@ block-obj-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat
 block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
 block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
 block-obj-y += qed-check.o
-block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o
+block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
 block-obj-y += parallels.o blkdebug.o blkverify.o
 block-obj-y += snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
new file mode 100644
index 0000000..10a87cc
--- /dev/null
+++ b/block/vhdx-log.c
@@ -0,0 +1,723 @@
+/*
+ * Block driver for Hyper-V VHDX Images
+ *
+ * Copyright (c) 2013 Red Hat, Inc.,
+ *
+ * Authors:
+ *  Jeff Cody <jcody@redhat.com>
+ *
+ *  This is based on the "VHDX Format Specification v1.00", published 8/25/2012
+ *  by Microsoft:
+ *      https://www.microsoft.com/en-us/download/details.aspx?id=34750
+ *
+ * This file covers the functionality of the metadata log writing, parsing, and
+ * replay.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#include "qemu-common.h"
+#include "block/block_int.h"
+#include "qemu/module.h"
+#include "block/vhdx.h"
+
+
+typedef struct VHDXLogSequence {
+    bool valid;
+    uint32_t count;
+    VHDXLogEntries log;
+    VHDXLogEntryHeader hdr;
+} VHDXLogSequence;
+
+typedef struct VHDXLogDescEntries {
+    VHDXLogEntryHeader hdr;
+    VHDXLogDescriptor desc[];
+} VHDXLogDescEntries;
+
+static const MSGUID zero_guid = { 0 };
+
+/* The log located on the disk is circular buffer containing
+ * sectors of 4096 bytes each.
+ *
+ * It is assumed for the read/write functions below that the
+ * circular buffer scheme uses a 'one sector open' to indicate
+ * the buffer is full.  Given the validation methods used for each
+ * sector, this method should be compatible with other methods that
+ * do not waste a sector.
+ */
+
+
+/* Allow peeking at the hdr entry at the beginning of the current
+ * read index, without advancing the read index */
+static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log,
+                             VHDXLogEntryHeader *hdr)
+{
+    int ret = 0;
+    uint64_t offset;
+    uint32_t read;
+
+    assert(hdr != NULL);
+
+    /* peek is only supported on sector boundaries */
+    if (log->read % VHDX_LOG_SECTOR_SIZE) {
+        ret = -EFAULT;
+        goto exit;
+    }
+
+    read = log->read;
+    /* we are guaranteed that a) log sectors are 4096 bytes,
+     * and b) the log length is a multiple of 1MB. So, there
+     * is always a round number of sectors in the buffer */
+    if ((read + sizeof(VHDXLogEntryHeader)) > log->length) {
+        read = 0;
+    }
+
+    if (read == log->write) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    offset = log->offset + read;
+
+    ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader));
+    if (ret < 0) {
+        goto exit;
+    }
+
+exit:
+    return ret;
+}
+
+/* Index increment for log, based on sector boundaries */
+static int vhdx_log_inc_idx(uint32_t idx, uint64_t length)
+{
+    idx += VHDX_LOG_SECTOR_SIZE;
+    /* we are guaranteed that a) log sectors are 4096 bytes,
+     * and b) the log length is a multiple of 1MB. So, there
+     * is always a round number of sectors in the buffer */
+    return idx >= length ? 0 : idx;
+}
+
+
+/* Reset the log to empty */
+static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s)
+{
+    MSGUID guid = { 0 };
+    s->log.read = s->log.write = 0;
+    /* a log guid of 0 indicates an empty log to any parser of v0
+     * VHDX logs */
+    vhdx_update_headers(bs, s, false, &guid);
+}
+
+/* Reads num_sectors from the log (all log sectors are 4096 bytes),
+ * into buffer 'buffer'.  Upon return, *sectors_read will contain
+ * the number of sectors successfully read.
+ *
+ * It is assumed that 'buffer' is already allocated, and of sufficient
+ * size (i.e. >= 4096*num_sectors).
+ *
+ * If 'peek' is true, then the tail (read) pointer for the circular buffer is
+ * not modified.
+ *
+ * 0 is returned on success, -errno otherwise.  */
+static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log,
+                                 uint32_t *sectors_read, void *buffer,
+                                 uint32_t num_sectors, bool peek)
+{
+    int ret = 0;
+    uint64_t offset;
+    uint32_t read;
+
+    read = log->read;
+
+    *sectors_read = 0;
+    while (num_sectors) {
+        if (read == log->write) {
+            /* empty */
+            break;
+        }
+        offset = log->offset + read;
+
+        ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE);
+        if (ret < 0) {
+            goto exit;
+        }
+        read = vhdx_log_inc_idx(read, log->length);
+
+        *sectors_read = *sectors_read + 1;
+        num_sectors--;
+    }
+
+exit:
+    if (!peek) {
+        log->read = read;
+    }
+    return ret;
+}
+
+/* Validates a log entry header */
+static bool vhdx_log_hdr_is_valid(VHDXLogEntries *log, VHDXLogEntryHeader *hdr,
+                                  BDRVVHDXState *s)
+{
+    int valid = false;
+
+    if (memcmp(&hdr->signature, "loge", 4)) {
+        goto exit;
+    }
+
+    /* if the individual entry length is larger than the whole log
+     * buffer, that is obviously invalid */
+    if (log->length < hdr->entry_length) {
+        goto exit;
+    }
+
+    /* length of entire entry must be in units of 4KB (log sector size) */
+    if (hdr->entry_length % (VHDX_LOG_SECTOR_SIZE)) {
+        goto exit;
+    }
+
+    /* per spec, sequence # must be > 0 */
+    if (hdr->sequence_number == 0) {
+        goto exit;
+    }
+
+    /* log entries are only valid if they match the file-wide log guid
+     * found in the active header */
+    if (!guid_eq(hdr->log_guid, s->headers[s->curr_header]->log_guid)) {
+        goto exit;
+    }
+
+    valid = true;
+
+exit:
+    return valid;
+}
+
+/*
+ * Given a log header, this will validate that the descriptors and the
+ * corresponding data sectors (if applicable)
+ *
+ * Validation consists of:
+ *      1. Making sure the sequence numbers matches the entry header
+ *      2. Verifying a valid signature ('zero' or 'desc' for descriptors)
+ *      3. File offset field is a multiple of 4KB
+ *      4. If a data descriptor, the corresponding data sector
+ *         has its signature ('data') and matching sequence number
+ *
+ * @desc: the data buffer containing the descriptor
+ * @hdr:  the log entry header
+ *
+ * Returns true if valid
+ */
+static bool vhdx_log_desc_is_valid(VHDXLogDescriptor *desc,
+                                   VHDXLogEntryHeader *hdr)
+{
+    bool ret = false;
+
+    if (desc->sequence_number != hdr->sequence_number) {
+        goto exit;
+    }
+    if (desc->file_offset % VHDX_LOG_SECTOR_SIZE) {
+        goto exit;
+    }
+
+    if (!memcmp(&desc->signature, "zero", 4)) {
+        if (desc->zero_length % VHDX_LOG_SECTOR_SIZE == 0) {
+            /* valid */
+            ret = true;
+        }
+    } else if (!memcmp(&desc->signature, "desc", 4)) {
+            /* valid */
+            ret = true;
+    }
+
+exit:
+    return ret;
+}
+
+
+/* Prior to sector data for a log entry, there is the header
+ * and the descriptors referenced in the header:
+ *
+ * [] = 4KB sector
+ *
+ * [ hdr, desc ][   desc   ][ ... ][ data ][ ... ]
+ *
+ * The first sector in a log entry has a 64 byte header, and
+ * up to 126 32-byte descriptors.  If more descriptors than
+ * 126 are required, then subsequent sectors can have up to 128
+ * descriptors.  Each sector is 4KB.  Data follows the descriptor
+ * sectors.
+ *
+ * This will return the number of sectors needed to encompass
+ * the passed number of descriptors in desc_cnt.
+ *
+ * This will never return 0, even if desc_cnt is 0.
+ */
+static int vhdx_compute_desc_sectors(uint32_t desc_cnt)
+{
+    uint32_t desc_sectors;
+
+    desc_cnt += 2; /* account for header in first sector */
+    desc_sectors = desc_cnt / 128;
+    if (desc_cnt % 128) {
+        desc_sectors++;
+    }
+
+    return desc_sectors;
+}
+
+
+/* Reads the log header, and subsequent descriptors (if any).  This
+ * will allocate all the space for buffer, which must be NULL when
+ * passed into this function. Each descriptor will also be validated,
+ * and error returned if any are invalid. */
+static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s,
+                              VHDXLogEntries *log, VHDXLogDescEntries **buffer)
+{
+    int ret = 0;
+    uint32_t desc_sectors;
+    uint32_t sectors_read;
+    VHDXLogEntryHeader hdr;
+    VHDXLogDescEntries *desc_entries = NULL;
+    int i;
+
+    assert(*buffer == NULL);
+
+    ret = vhdx_log_peek_hdr(bs, log, &hdr);
+    if (ret < 0) {
+        goto exit;
+    }
+    vhdx_log_entry_hdr_le_import(&hdr);
+    if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count);
+    desc_entries = qemu_blockalign(bs, desc_sectors * VHDX_LOG_SECTOR_SIZE);
+
+    ret = vhdx_log_read_sectors(bs, log, &sectors_read, desc_entries,
+                                desc_sectors, false);
+    if (ret < 0) {
+        goto free_and_exit;
+    }
+    if (sectors_read != desc_sectors) {
+        ret = -EINVAL;
+        goto free_and_exit;
+    }
+
+    /* put in proper endianness, and validate each desc */
+    for (i = 0; i < hdr.descriptor_count; i++) {
+        vhdx_log_desc_le_import(&desc_entries->desc[i]);
+        if (vhdx_log_desc_is_valid(&desc_entries->desc[i], &hdr) == false) {
+            ret = -EINVAL;
+            goto free_and_exit;
+        }
+    }
+
+    *buffer = desc_entries;
+    goto exit;
+
+free_and_exit:
+    qemu_vfree(desc_entries);
+exit:
+    return ret;
+}
+
+
+/* Flushes the descriptor described by desc to the VHDX image file.
+ * If the descriptor is a data descriptor, than 'data' must be non-NULL,
+ * and >= 4096 bytes (VHDX_LOG_SECTOR_SIZE), containing the data to be
+ * written.
+ *
+ * Verification is performed to make sure the sequence numbers of a data
+ * descriptor match the sequence number in the desc.
+ *
+ * For a zero descriptor, it may describe multiple sectors to fill with zeroes.
+ * In this case, it should be noted that zeroes are written to disk, and the
+ * image file is not extended as a sparse file.  */
+static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc,
+                               VHDXLogDataSector *data)
+{
+    int ret = 0;
+    uint64_t seq, file_offset;
+    uint32_t offset = 0;
+    void *buffer = NULL;
+    uint64_t count = 1;
+    int i;
+
+    buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
+
+    if (!memcmp(&desc->signature, "desc", 4)) {
+        /* data sector */
+        if (data == NULL) {
+            ret = -EFAULT;
+            goto exit;
+        }
+
+        /* The sequence number of the data sector must match that
+         * in the descriptor */
+        seq = data->sequence_high;
+        seq <<= 32;
+        seq |= data->sequence_low & 0xffffffff;
+
+        if (seq != desc->sequence_number) {
+            ret = -EINVAL;
+            goto exit;
+        }
+
+        /* Each data sector is in total 4096 bytes, however the first
+         * 8 bytes, and last 4 bytes, are located in the descriptor */
+        memcpy(buffer, &desc->leading_bytes, 8);
+        offset += 8;
+
+        memcpy(buffer+offset, data->data, 4084);
+        offset += 4084;
+
+        memcpy(buffer+offset, &desc->trailing_bytes, 4);
+
+    } else if (!memcmp(&desc->signature, "zero", 4)) {
+        /* write 'count' sectors of sector */
+        memset(buffer, 0, VHDX_LOG_SECTOR_SIZE);
+        count = desc->zero_length / VHDX_LOG_SECTOR_SIZE;
+    }
+
+    file_offset = desc->file_offset;
+
+    /* count is only > 1 if we are writing zeroes */
+    for (i = 0; i < count; i++) {
+        ret = bdrv_pwrite_sync(bs->file, file_offset, buffer,
+                               VHDX_LOG_SECTOR_SIZE);
+        if (ret < 0) {
+            goto exit;
+        }
+        file_offset += VHDX_LOG_SECTOR_SIZE;
+    }
+
+exit:
+    qemu_vfree(buffer);
+    return ret;
+}
+
+/* Flush the entire log (as described by 'logs') to the VHDX image
+ * file, and then set the log to 'empty' status once complete.
+ *
+ * The log entries should be validate prior to flushing */
+static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
+                          VHDXLogSequence *logs)
+{
+    int ret = 0;
+    int i;
+    uint32_t cnt, sectors_read;
+    uint64_t new_file_size;
+    void *data = NULL;
+    VHDXLogDescEntries *desc_entries = NULL;
+    VHDXLogEntryHeader hdr_tmp = { 0 };
+
+    cnt = logs->count;
+
+    data = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
+
+    ret = vhdx_user_visible_write(bs, s);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    /* each iteration represents one log sequence, which may span multiple
+     * sectors */
+    while (cnt--) {
+        ret = vhdx_log_peek_hdr(bs, &logs->log, &hdr_tmp);
+        if (ret < 0) {
+            goto exit;
+        }
+        /* if the log shows a FlushedFileOffset larger than our current file
+         * size, then that means the file has been truncated / corrupted, and
+         * we must refused to open it / use it */
+        if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file)) {
+            ret = -EINVAL;
+            goto exit;
+        }
+
+        ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries);
+        if (ret < 0) {
+            goto exit;
+        }
+
+        for (i = 0; i < desc_entries->hdr.descriptor_count; i++) {
+            if (!memcmp(&desc_entries->desc[i].signature, "desc", 4)) {
+                /* data sector, so read a sector to flush */
+                ret = vhdx_log_read_sectors(bs, &logs->log, &sectors_read,
+                                            data, 1, false);
+                if (ret < 0) {
+                    goto exit;
+                }
+                if (sectors_read != 1) {
+                    ret = -EINVAL;
+                    goto exit;
+                }
+            }
+
+            ret = vhdx_log_flush_desc(bs, &desc_entries->desc[i], data);
+            if (ret < 0) {
+                goto exit;
+            }
+        }
+        if (bdrv_getlength(bs->file) < desc_entries->hdr.last_file_offset) {
+            new_file_size = desc_entries->hdr.last_file_offset;
+            if (new_file_size % (1024*1024)) {
+                /* round up to nearest 1MB boundary */
+                new_file_size = ((new_file_size >> 20) + 1) << 20;
+                bdrv_truncate(bs->file, new_file_size);
+            }
+        }
+        qemu_vfree(desc_entries);
+        desc_entries = NULL;
+    }
+
+    /* once the log is fully flushed, indicate that we have an empty log
+     * now.  This also sets the log guid to 0, to indicate an empty log */
+    vhdx_log_reset(bs, s);
+
+exit:
+    qemu_vfree(data);
+    qemu_vfree(desc_entries);
+    return ret;
+}
+
+static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s,
+                                   VHDXLogEntries *log, uint64_t seq,
+                                   bool *valid, VHDXLogEntryHeader *entry)
+{
+    int ret = 0;
+    VHDXLogEntryHeader hdr;
+    void *buffer = NULL;
+    uint32_t i, desc_sectors, total_sectors, crc;
+    uint32_t sectors_read = 0;
+    VHDXLogDescEntries *desc_buffer = NULL;
+
+    *valid = false;
+
+    ret = vhdx_log_peek_hdr(bs, log, &hdr);
+    if (ret < 0) {
+        goto inc_and_exit;
+    }
+
+    vhdx_log_entry_hdr_le_import(&hdr);
+
+
+    if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) {
+        goto inc_and_exit;
+    }
+
+    if (seq > 0) {
+        if (hdr.sequence_number != seq + 1) {
+            goto inc_and_exit;
+        }
+    }
+
+    desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count);
+
+    /* Read desc sectors, and calculate log checksum */
+
+    total_sectors = hdr.entry_length / VHDX_LOG_SECTOR_SIZE;
+
+
+    /* read_desc() will incrememnt the read idx */
+    ret = vhdx_log_read_desc(bs, s, log, &desc_buffer);
+    if (ret < 0) {
+        goto free_and_exit;
+    }
+
+    crc = vhdx_checksum_calc(0xffffffff, (void *)desc_buffer,
+                            desc_sectors * VHDX_LOG_SECTOR_SIZE, 4);
+    crc ^= 0xffffffff;
+
+    buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
+    if (total_sectors > desc_sectors) {
+        for (i = 0; i < total_sectors - desc_sectors; i++) {
+            sectors_read = 0;
+            ret = vhdx_log_read_sectors(bs, log, &sectors_read, buffer,
+                                        1, false);
+            if (ret < 0 || sectors_read != 1) {
+                goto free_and_exit;
+            }
+            crc = vhdx_checksum_calc(crc, buffer, VHDX_LOG_SECTOR_SIZE, -1);
+            crc ^= 0xffffffff;
+        }
+    }
+    crc ^= 0xffffffff;
+    if (crc != desc_buffer->hdr.checksum) {
+        goto free_and_exit;
+    }
+
+    *valid = true;
+    *entry = hdr;
+    goto free_and_exit;
+
+inc_and_exit:
+    log->read = vhdx_log_inc_idx(log->read, log->length);
+
+free_and_exit:
+    qemu_vfree(buffer);
+    qemu_vfree(desc_buffer);
+    return ret;
+}
+
+/* Search through the log circular buffer, and find the valid, active
+ * log sequence, if any exists
+ * */
+static int vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s,
+                           VHDXLogSequence *logs)
+{
+    int ret = 0;
+    uint32_t tail;
+    bool seq_valid = false;
+    VHDXLogSequence candidate = { 0 };
+    VHDXLogEntryHeader hdr = { 0 };
+    VHDXLogEntries curr_log;
+
+    memcpy(&curr_log, &s->log, sizeof(VHDXLogEntries));
+    curr_log.write = curr_log.length;   /* assume log is full */
+    curr_log.read = 0;
+
+
+    /* now we will go through the whole log sector by sector, until
+     * we find a valid, active log sequence, or reach the end of the
+     * log buffer */
+    for (;;) {
+        uint64_t curr_seq = 0;
+        VHDXLogSequence current = { 0 };
+
+        tail = curr_log.read;
+
+        ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq,
+                                      &seq_valid, &hdr);
+        if (ret < 0) {
+            goto exit;
+        }
+
+        if (seq_valid) {
+            current.valid     = true;
+            current.log       = curr_log;
+            current.log.read  = tail;
+            current.log.write = curr_log.read;
+            current.count     = 1;
+            current.hdr       = hdr;
+
+
+            for (;;) {
+                ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq,
+                                              &seq_valid, &hdr);
+                if (ret < 0) {
+                    goto exit;
+                }
+                if (seq_valid == false) {
+                    break;
+                }
+                current.log.write = curr_log.read;
+                current.count++;
+
+                curr_seq = hdr.sequence_number;
+            }
+        }
+
+        if (current.valid) {
+            if (candidate.valid == false ||
+                current.hdr.sequence_number > candidate.hdr.sequence_number) {
+                candidate = current;
+            }
+        }
+
+        if (curr_log.read < tail) {
+            break;
+        }
+    }
+
+    *logs = candidate;
+
+    if (candidate.valid) {
+        /* this is the next sequence number, for writes */
+        s->log.sequence = candidate.hdr.sequence_number + 1;
+    }
+
+
+exit:
+    return ret;
+}
+
+/* Parse the replay log.  Per the VHDX spec, if the log is present
+ * it must be replayed prior to opening the file, even read-only.
+ *
+ * If read-only, we must replay the log in RAM (or refuse to open
+ * a dirty VHDX file read-only */
+int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed)
+{
+    int ret = 0;
+    VHDXHeader *hdr;
+    VHDXLogSequence logs = { 0 };
+
+    hdr = s->headers[s->curr_header];
+
+    *flushed = false;
+
+    /* s->log.hdr is freed in vhdx_close() */
+    if (s->log.hdr == NULL) {
+        s->log.hdr = qemu_blockalign(bs, sizeof(VHDXLogEntryHeader));
+    }
+
+    s->log.offset = hdr->log_offset;
+    s->log.length = hdr->log_length;
+
+    if (s->log.offset < VHDX_LOG_MIN_SIZE ||
+        s->log.offset % VHDX_LOG_MIN_SIZE) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    /* per spec, only log version of 0 is supported */
+    if (hdr->log_version != 0) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    /* If either the log guid, or log length is zero,
+     * then a replay log is not present */
+    if (guid_eq(hdr->log_guid, zero_guid)) {
+        goto exit;
+    }
+
+    if (hdr->log_length == 0) {
+        goto exit;
+    }
+
+    if (hdr->log_length % VHDX_LOG_MIN_SIZE) {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+
+    /* The log is present, we need to find if and where there is an active
+     * sequence of valid entries present in the log.  */
+
+    ret = vhdx_log_search(bs, s, &logs);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    if (logs.valid) {
+        /* now flush the log */
+        ret = vhdx_log_flush(bs, s, &logs);
+        if (ret < 0) {
+            goto exit;
+        }
+        *flushed = true;
+    }
+
+
+exit:
+    return ret;
+}
+
+
diff --git a/block/vhdx.c b/block/vhdx.c
index 9db6531..d0499ba 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -734,48 +734,6 @@ exit:
     return ret;
 }
 
-/* Parse the replay log.  Per the VHDX spec, if the log is present
- * it must be replayed prior to opening the file, even read-only.
- *
- * If read-only, we must replay the log in RAM (or refuse to open
- * a dirty VHDX file read-only */
-static int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s)
-{
-    int ret = 0;
-    int i;
-    VHDXHeader *hdr;
-
-    hdr = s->headers[s->curr_header];
-
-    /* either the log guid, or log length is zero,
-     * then a replay log is present */
-    for (i = 0; i < sizeof(hdr->log_guid.data4); i++) {
-        ret |= hdr->log_guid.data4[i];
-    }
-    if (hdr->log_guid.data1 == 0 &&
-        hdr->log_guid.data2 == 0 &&
-        hdr->log_guid.data3 == 0 &&
-        ret == 0) {
-        goto exit;
-    }
-
-    /* per spec, only log version of 0 is supported */
-    if (hdr->log_version != 0) {
-        ret = -EINVAL;
-        goto exit;
-    }
-
-    if (hdr->log_length == 0) {
-        goto exit;
-    }
-
-    /* We currently do not support images with logs to replay */
-    ret = -ENOTSUP;
-
-exit:
-    return ret;
-}
-
 
 static void vhdx_close(BlockDriverState *bs)
 {
@@ -784,6 +742,7 @@ static void vhdx_close(BlockDriverState *bs)
     qemu_vfree(s->headers[1]);
     qemu_vfree(s->bat);
     qemu_vfree(s->parent_entries);
+    qemu_vfree(s->log.hdr);
 }
 
 static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
@@ -793,10 +752,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
     uint32_t i;
     uint64_t signature;
     uint32_t data_blocks_cnt, bitmap_blocks_cnt;
+    bool log_flushed = false;
 
 
     s->bat = NULL;
     s->first_visible_write = true;
+    s->log.write = s->log.read = 0;
 
     qemu_co_mutex_init(&s->lock);
 
@@ -820,20 +781,33 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
         goto fail;
     }
 
-    ret = vhdx_parse_log(bs, s);
+    ret = vhdx_open_region_tables(bs, s);
     if (ret) {
         goto fail;
     }
 
-    ret = vhdx_open_region_tables(bs, s);
+    ret = vhdx_parse_metadata(bs, s);
     if (ret) {
         goto fail;
     }
 
-    ret = vhdx_parse_metadata(bs, s);
+    ret = vhdx_parse_log(bs, s, &log_flushed);
     if (ret) {
         goto fail;
     }
+
+    if (log_flushed) {
+        /* if we replayed log entries, reopen and parse all the metadata */
+        ret = vhdx_open_region_tables(bs, s);
+        if (ret) {
+            goto fail;
+        }
+        ret = vhdx_parse_metadata(bs, s);
+        if (ret) {
+            goto fail;
+        }
+    }
+
     s->block_size = s->params.block_size;
 
     /* the VHDX spec dictates that virtual_disk_size is always a multiple of
@@ -887,10 +861,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
 
     return 0;
 fail:
-    qemu_vfree(s->headers[0]);
-    qemu_vfree(s->headers[1]);
-    qemu_vfree(s->bat);
-    qemu_vfree(s->parent_entries);
+    vhdx_close(bs);
     return ret;
 }
 
diff --git a/block/vhdx.h b/block/vhdx.h
index 8c61bfd..dfb3ed9 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -326,7 +326,11 @@ typedef struct VHDXMetadataEntries {
 typedef struct VHDXLogEntries {
     uint64_t offset;
     uint64_t length;
-    uint32_t head;
+    uint32_t write;
+    uint32_t read;
+    VHDXLogEntryHeader *hdr;
+    void *desc_buffer;
+    uint64_t sequence;
     uint32_t tail;
 } VHDXLogEntries;
 
@@ -382,6 +386,7 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
 
 bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset);
 
+int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed);
 
 static inline void leguid_to_cpus(MSGUID *guid)
 {
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 09/16] block: vhdx - add region overlap detection for image files
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (7 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 08/16] block: vhdx - log parsing, replay, and flush support Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 10/16] block: vhdx - add log write support Jeff Cody
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

Regions in the image file cannot overlap - the log, region tables,
and metdata must all be unique and non-overlapping.

This adds region checking by means of a QLIST; there can be a variable
number of regions and metadata (there may be metadata or region tables
that we do not recognize / know about, but are not required).

This adds the capability to register a region for later checking, and
to check against registered regions for any overlap.

Also, if neither the BAT or Metadata region tables are found, return
error.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/vhdx.h |  8 ++++++
 2 files changed, 91 insertions(+)

diff --git a/block/vhdx.c b/block/vhdx.c
index d0499ba..b15d6e5 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -203,6 +203,51 @@ void vhdx_guid_generate(MSGUID *guid)
     memcpy(guid, uuid, sizeof(MSGUID));
 }
 
+/* Check for region overlaps inside the VHDX image */
+static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length)
+{
+    int ret = 0;
+    uint64_t end;
+    VHDXRegionEntry *r;
+
+    end = start + length;
+    QLIST_FOREACH(r, &s->regions, entries) {
+        if ((start >= r->start && start <  r->end) ||
+            (end   >  r->start && end   <= r->end)) {
+            ret = -EINVAL;
+            goto exit;
+        }
+    }
+
+exit:
+    return ret;
+}
+
+/* Register a region for future checks */
+static void vhdx_region_register(BDRVVHDXState *s,
+                                 uint64_t start, uint64_t length)
+{
+    VHDXRegionEntry *r;
+
+    r = g_malloc0(sizeof(*r));
+
+    r->start = start;
+    r->end = start + length;
+
+    QLIST_INSERT_HEAD(&s->regions, r, entries);
+}
+
+/* Free all registered regions */
+static void vhdx_region_unregister_all(BDRVVHDXState *s)
+{
+    VHDXRegionEntry *r, *r_next;
+
+    QLIST_FOREACH_SAFE(r, &s->regions, entries, r_next) {
+        QLIST_REMOVE(r, entries);
+        g_free(r);
+    }
+}
+
 /*
  * Per the MS VHDX Specification, for every VHDX file:
  *      - The header section is fixed size - 1 MB
@@ -388,6 +433,9 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
         }
     }
 
+    vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
+                            s->headers[s->curr_header]->log_length);
+
     ret = 0;
 
     goto exit;
@@ -451,6 +499,15 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
         le32_to_cpus(&rt_entry.length);
         le32_to_cpus(&rt_entry.data_bits);
 
+        /* check for region overlap between these entries, and any
+         * other memory regions in the file */
+        ret = vhdx_region_check(s, rt_entry.file_offset, rt_entry.length);
+        if (ret < 0) {
+            goto fail;
+        }
+
+        vhdx_region_register(s, rt_entry.file_offset, rt_entry.length);
+
         /* see if we recognize the entry */
         if (guid_eq(rt_entry.guid, bat_guid)) {
             /* must be unique; if we have already found it this is invalid */
@@ -481,6 +538,12 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
             goto fail;
         }
     }
+
+    if (!bat_rt_found || !metadata_rt_found) {
+        ret = -EINVAL;
+        goto fail;
+    }
+
     ret = 0;
 
 fail:
@@ -743,6 +806,7 @@ static void vhdx_close(BlockDriverState *bs)
     qemu_vfree(s->bat);
     qemu_vfree(s->parent_entries);
     qemu_vfree(s->log.hdr);
+    vhdx_region_unregister_all(s);
 }
 
 static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
@@ -760,6 +824,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
     s->log.write = s->log.read = 0;
 
     qemu_co_mutex_init(&s->lock);
+    QLIST_INIT(&s->regions);
 
     /* validate the file signature */
     ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t));
@@ -846,8 +911,26 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
         goto fail;
     }
 
+    uint64_t payblocks = s->chunk_ratio;
+    /* endian convert, and verify populated BAT field file offsets against
+     * region table and log entries */
     for (i = 0; i < s->bat_entries; i++) {
         le64_to_cpus(&s->bat[i]);
+        if (payblocks--) {
+            /* payload bat entries */
+            if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
+                    PAYLOAD_BLOCK_FULL_PRESENT) {
+                ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK,
+                                        s->block_size);
+                if (ret < 0) {
+                    goto fail;
+                }
+            }
+        } else {
+            payblocks = s->chunk_ratio;
+            /* Once differencing files are supported, verify sector bitmap
+             * blocks here */
+        }
     }
 
     if (flags & BDRV_O_RDWR) {
diff --git a/block/vhdx.h b/block/vhdx.h
index dfb3ed9..831aa13 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -230,6 +230,7 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
    other bits are reserved */
 #define VHDX_BAT_STATE_BIT_MASK 0x07
 #define VHDX_BAT_FILE_OFF_BITS (64 - 44)
+#define VHDX_BAT_FILE_OFF_MASK  0xFFFFFFFFFFF00000 /* upper 44 bits */
 typedef uint64_t VHDXBatEntry;
 
 /* ---- METADATA REGION STRUCTURES ---- */
@@ -334,6 +335,12 @@ typedef struct VHDXLogEntries {
     uint32_t tail;
 } VHDXLogEntries;
 
+typedef struct VHDXRegionEntry {
+    uint64_t start;
+    uint64_t end;
+    QLIST_ENTRY(VHDXRegionEntry) entries;
+} VHDXRegionEntry;
+
 typedef struct BDRVVHDXState {
     CoMutex lock;
 
@@ -373,6 +380,7 @@ typedef struct BDRVVHDXState {
     VHDXParentLocatorHeader parent_header;
     VHDXParentLocatorEntry *parent_entries;
 
+    QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions;
 } BDRVVHDXState;
 
 void vhdx_guid_generate(MSGUID *guid);
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 10/16] block: vhdx - add log write support
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (8 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 09/16] block: vhdx - add region overlap detection for image files Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 11/16] block: vhdx " Jeff Cody
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This adds support for writing to the VHDX log.

For spec details, see VHDX Specification Format v1.00:
https://www.microsoft.com/en-us/download/details.aspx?id=34750

There are a few limitations to this log support:
1.) There is no caching yet
2.) The log is flushed after each entry

The primary write interface, vhdx_log_write_and_flush(), performs a log
write followed by an immediate flush of the log.

As each log entry sector is a minimum of 4KB, partial sector writes are
filled in with data from the disk write destination.

If the current file log GUID is 0, a new GUID is generated and updated
in the header.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx-log.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/vhdx.h     |   3 +
 2 files changed, 279 insertions(+)

diff --git a/block/vhdx-log.c b/block/vhdx-log.c
index 10a87cc..09cb80b 100644
--- a/block/vhdx-log.c
+++ b/block/vhdx-log.c
@@ -156,6 +156,55 @@ exit:
     return ret;
 }
 
+/* Writes num_sectors to the log (all log sectors are 4096 bytes),
+ * from buffer 'buffer'.  Upon return, *sectors_written will contain
+ * the number of sectors successfully written.
+ *
+ * It is assumed that 'buffer' is at least 4096*num_sectors large.
+ *
+ * 0 is returned on success, -errno otherwise */
+static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log,
+                                  uint32_t *sectors_written, void *buffer,
+                                  uint32_t num_sectors)
+{
+    int ret = 0;
+    uint64_t offset;
+    uint32_t write;
+    void *buffer_tmp;
+    BDRVVHDXState *s = bs->opaque;
+
+    ret = vhdx_user_visible_write(bs, s);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    write = log->write;
+
+    buffer_tmp = buffer;
+    while (num_sectors) {
+
+        offset = log->offset + write;
+        write = vhdx_log_inc_idx(write, log->length);
+        if (write == log->read) {
+            /* full */
+            break;
+        }
+        ret = bdrv_pwrite(bs->file, offset, buffer_tmp, VHDX_LOG_SECTOR_SIZE);
+        if (ret < 0) {
+            goto exit;
+        }
+        buffer_tmp += VHDX_LOG_SECTOR_SIZE;
+
+        log->write = write;
+        *sectors_written = *sectors_written + 1;
+        num_sectors--;
+    }
+
+exit:
+    return ret;
+}
+
+
 /* Validates a log entry header */
 static bool vhdx_log_hdr_is_valid(VHDXLogEntries *log, VHDXLogEntryHeader *hdr,
                                   BDRVVHDXState *s)
@@ -721,3 +770,230 @@ exit:
 }
 
 
+
+static void vhdx_log_raw_to_le_sector(VHDXLogDescriptor *desc,
+                                      VHDXLogDataSector *sector, void *data,
+                                      uint64_t seq)
+{
+    /* 8 + 4084 + 4 = 4096, 1 log sector */
+    memcpy(&desc->leading_bytes, data, 8);
+    data += 8;
+    cpu_to_le64s(&desc->leading_bytes);
+    memcpy(sector->data, data, 4084);
+    data += 4084;
+    memcpy(&desc->trailing_bytes, data, 4);
+    cpu_to_le32s(&desc->trailing_bytes);
+    data += 4;
+
+    sector->sequence_high  = (uint32_t) (seq >> 32);
+    sector->sequence_low   = (uint32_t) (seq & 0xffffffff);
+    sector->data_signature = VHDX_LOG_DATA_SIGNATURE;
+
+    vhdx_log_desc_le_export(desc);
+    vhdx_log_data_le_export(sector);
+}
+
+
+static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
+                          void *data, uint32_t length, uint64_t offset)
+{
+    int ret = 0;
+    void *buffer = NULL;
+    void *merged_sector = NULL;
+    void *data_tmp, *sector_write;
+    unsigned int i;
+    int sector_offset;
+    uint32_t desc_sectors, sectors, total_length;
+    uint32_t sectors_written = 0;
+    uint32_t aligned_length;
+    uint32_t leading_length = 0;
+    uint32_t trailing_length = 0;
+    uint32_t partial_sectors = 0;
+    uint32_t bytes_written = 0;
+    uint64_t file_offset;
+    VHDXHeader *header;
+    VHDXLogEntryHeader new_hdr;
+    VHDXLogDescriptor *new_desc = NULL;
+    VHDXLogDataSector *data_sector = NULL;
+    MSGUID new_guid = { 0 };
+
+    header = s->headers[s->curr_header];
+
+    /* need to have offset read data, and be on 4096 byte boundary */
+
+    if (length > header->log_length) {
+        /* no log present.  we could create a log here instead of failing */
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    if (guid_eq(header->log_guid, zero_guid)) {
+        vhdx_guid_generate(&new_guid);
+        vhdx_update_headers(bs, s, false, &new_guid);
+    } else {
+        /* currently, we require that the log be flushed after
+         * every write. */
+        ret = -ENOTSUP;
+    }
+
+    /* 0 is an invalid sequence number, but may also represent the first
+     * log write (or a wrapped seq) */
+    if (s->log.sequence == 0) {
+        s->log.sequence = 1;
+    }
+
+    sector_offset = offset % VHDX_LOG_SECTOR_SIZE;
+    file_offset = (offset / VHDX_LOG_SECTOR_SIZE) * VHDX_LOG_SECTOR_SIZE;
+
+    aligned_length = length;
+
+    /* add in the unaligned head and tail bytes */
+    if (sector_offset) {
+        leading_length = (VHDX_LOG_SECTOR_SIZE - sector_offset);
+        leading_length = leading_length > length ? length : leading_length;
+        aligned_length -= leading_length;
+        partial_sectors++;
+    }
+
+    sectors = aligned_length / VHDX_LOG_SECTOR_SIZE;
+    trailing_length = aligned_length - (sectors * VHDX_LOG_SECTOR_SIZE);
+    if (trailing_length) {
+        partial_sectors++;
+    }
+
+    sectors += partial_sectors;
+
+    /* sectors is now how many sectors the data itself takes, not
+     * including the header and descriptor metadata */
+
+    new_hdr = (VHDXLogEntryHeader) {
+                .signature           = VHDX_LOG_SIGNATURE,
+                .tail                = s->log.tail,
+                .sequence_number     = s->log.sequence,
+                .descriptor_count    = sectors,
+                .reserved            = 0,
+                .flushed_file_offset = bdrv_getlength(bs->file),
+                .last_file_offset    = bdrv_getlength(bs->file),
+              };
+
+    new_hdr.log_guid = header->log_guid;
+
+    desc_sectors = vhdx_compute_desc_sectors(new_hdr.descriptor_count);
+
+    total_length = (desc_sectors + sectors) * VHDX_LOG_SECTOR_SIZE;
+    new_hdr.entry_length = total_length;
+
+    vhdx_log_entry_hdr_le_export(&new_hdr);
+
+    buffer = qemu_blockalign(bs, total_length);
+    memcpy(buffer, &new_hdr, sizeof(new_hdr));
+
+    new_desc = (VHDXLogDescriptor *) (buffer + sizeof(new_hdr));
+    data_sector = buffer + (desc_sectors * VHDX_LOG_SECTOR_SIZE);
+    data_tmp = data;
+
+    /* All log sectors are 4KB, so for any partial sectors we must
+     * merge the data with preexisting data from the final file
+     * destination */
+    merged_sector = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
+
+    for (i = 0; i < sectors; i++) {
+        new_desc->signature       = VHDX_LOG_DESC_SIGNATURE;
+        new_desc->sequence_number = s->log.sequence;
+        new_desc->file_offset     = file_offset;
+
+        if (i == 0 && leading_length) {
+            /* partial sector at the front of the buffer */
+            ret = bdrv_pread(bs->file, file_offset, merged_sector,
+                             VHDX_LOG_SECTOR_SIZE);
+            if (ret < 0) {
+                goto exit;
+            }
+            memcpy(merged_sector + sector_offset, data_tmp, leading_length);
+            bytes_written = leading_length;
+            sector_write = merged_sector;
+        } else if (i == sectors - 1 && trailing_length) {
+            /* partial sector at the end of the buffer */
+            ret = bdrv_pread(bs->file,
+                            file_offset,
+                            merged_sector + trailing_length,
+                            VHDX_LOG_SECTOR_SIZE - trailing_length);
+            if (ret < 0) {
+                goto exit;
+            }
+            memcpy(merged_sector, data_tmp, trailing_length);
+            bytes_written = trailing_length;
+            sector_write = merged_sector;
+        } else {
+            bytes_written = VHDX_LOG_SECTOR_SIZE;
+            sector_write = data_tmp;
+        }
+
+        /* populate the raw sector data into the proper structures,
+         * as well as update the descriptor, and convert to proper
+         * endianness */
+        vhdx_log_raw_to_le_sector(new_desc, data_sector, sector_write,
+                                  s->log.sequence);
+
+        data_tmp += bytes_written;
+        data_sector++;
+        new_desc++;
+        file_offset += VHDX_LOG_SECTOR_SIZE;
+    }
+
+    /* checksum covers entire entry, from the log header through the
+     * last data sector */
+    vhdx_update_checksum(buffer, total_length,
+                         offsetof(VHDXLogEntryHeader, checksum));
+    cpu_to_le32s((uint32_t *)(buffer + 4));
+
+    /* now write to the log */
+    vhdx_log_write_sectors(bs, &s->log, &sectors_written, buffer,
+                           desc_sectors + sectors);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    if (sectors_written != desc_sectors + sectors) {
+        /* instead of failing, we could flush the log here */
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    s->log.sequence++;
+    /* write new tail */
+    s->log.tail = s->log.write;
+
+exit:
+    qemu_vfree(buffer);
+    qemu_vfree(merged_sector);
+    return ret;
+}
+
+/* Perform a log write, and then immediately flush the entire log */
+int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
+                             void *data, uint32_t length, uint64_t offset)
+{
+    int ret = 0;
+    VHDXLogSequence logs = { .valid = true,
+                             .count = 1,
+                             .hdr = { 0 } };
+
+
+    ret = vhdx_log_write(bs, s, data, length, offset);
+    if (ret < 0) {
+        goto exit;
+    }
+    logs.log = s->log;
+
+    ret = vhdx_log_flush(bs, s, &logs);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    s->log = logs.log;
+
+exit:
+    return ret;
+}
+
diff --git a/block/vhdx.h b/block/vhdx.h
index 831aa13..4f32c01 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -396,6 +396,9 @@ bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset);
 
 int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed);
 
+int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
+                             void *data, uint32_t length, uint64_t offset);
+
 static inline void leguid_to_cpus(MSGUID *guid)
 {
     le32_to_cpus(&guid->data1);
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 11/16] block: vhdx write support
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (9 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 10/16] block: vhdx - add log write support Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 12/16] block: vhdx - remove BAT file offset bit shifting Jeff Cody
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This adds support for writing to VHDX image files, using coroutines.
Writes into the BAT table goes through the VHDX log.  Currently, BAT
table writes occur when expanding a dynamic VHDX file, and allocating a
new BAT entry.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 148 insertions(+), 2 deletions(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index b15d6e5..69a06a9 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -940,7 +940,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
         }
     }
 
-    /* TODO: differencing files, write */
+    /* TODO: differencing files */
 
     return 0;
 fail:
@@ -1069,7 +1069,43 @@ exit:
     return ret;
 }
 
+/*
+ * Allocate a new payload block at the end of the file.
+ *
+ * Allocation will happen at 1MB alignment inside the file
+ *
+ * Returns the file offset start of the new payload block
+ */
+static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s,
+                                    uint64_t *new_offset)
+{
+    *new_offset = bdrv_getlength(bs->file);
+
+    /* per the spec, the address for a block is in units of 1MB */
+    *new_offset = ROUND_UP(*new_offset, 1024*1024);
+
+    return bdrv_truncate(bs->file, *new_offset + s->block_size);
+}
+
+/*
+ * Update the BAT tablet entry with the new file offset, and the new entry
+ * state */
+static void vhdx_update_bat_table_entry(BlockDriverState *bs, BDRVVHDXState *s,
+                                       VHDXSectorInfo *sinfo,
+                                       uint64_t *bat_entry,
+                                       uint64_t *bat_offset, int state)
+{
+    /* The BAT entry is a uint64, with 44 bits for the file offset in units of
+     * 1MB, and 3 bits for the block state. */
+    s->bat[sinfo->bat_idx]  = ((sinfo->file_offset>>20) <<
+                               VHDX_BAT_FILE_OFF_BITS);
 
+    s->bat[sinfo->bat_idx] |= state & VHDX_BAT_STATE_BIT_MASK;
+
+    *bat_entry = cpu_to_le64(s->bat[sinfo->bat_idx]);
+    *bat_offset = s->bat_offset + sinfo->bat_idx * sizeof(VHDXBatEntry);
+
+}
 
 /* Per the spec, on the first write of guest-visible data to the file the
  * data write guid must be updated in the header */
@@ -1086,7 +1122,117 @@ int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s)
 static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
                                       int nb_sectors, QEMUIOVector *qiov)
 {
-    return -ENOTSUP;
+    int ret = -ENOTSUP;
+    BDRVVHDXState *s = bs->opaque;
+    VHDXSectorInfo sinfo;
+    uint64_t bytes_done = 0;
+    uint64_t bat_entry = 0;
+    uint64_t bat_entry_offset = 0;
+    bool bat_update;
+    QEMUIOVector hd_qiov;
+
+    qemu_iovec_init(&hd_qiov, qiov->niov);
+
+    qemu_co_mutex_lock(&s->lock);
+
+    ret = vhdx_user_visible_write(bs, s);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    while (nb_sectors > 0) {
+        if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) {
+            /* not supported yet */
+            ret = -ENOTSUP;
+            goto exit;
+        } else {
+            bat_update = false;
+            vhdx_block_translate(s, sector_num, nb_sectors, &sinfo);
+
+            qemu_iovec_reset(&hd_qiov);
+            qemu_iovec_concat(&hd_qiov, qiov,  bytes_done, sinfo.bytes_avail);
+            /* check the payload block state */
+            switch (s->bat[sinfo.bat_idx] & VHDX_BAT_STATE_BIT_MASK) {
+            case PAYLOAD_BLOCK_ZERO:
+                /* in this case, we need to preserve zero writes for
+                 * data that is not part of this write, so we must pad
+                 * the rest of the buffer to zeroes */
+
+                /* if we are on a posix system with ftruncate() that extends
+                 * a file, then it is zero-filled for us.  On Win32, the raw
+                 * layer uses SetFilePointer and SetFileEnd, which does not
+                 * zero fill AFAIK */
+
+                /* TODO: queue another write of zero buffers if the host OS does
+                 * not zero-fill on file extension */
+
+                /* fall through */
+            case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */
+            case PAYLOAD_BLOCK_UNMAPPED:    /* fall through */
+            case PAYLOAD_BLOCK_UNDEFINED:   /* fall through */
+                ret = vhdx_allocate_block(bs, s, &sinfo.file_offset);
+                if (ret < 0) {
+                    goto exit;
+                }
+                /* once we support differencing files, this may also be
+                 * partially present */
+                /* update block state to the newly specified state */
+                vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry,
+                                            &bat_entry_offset,
+                                            PAYLOAD_BLOCK_FULL_PRESENT);
+                bat_update = true;
+                /* since we just allocated a block, file_offset is the
+                 * beginning of the payload block. It needs to be the
+                 * write address, which includes the offset into the block */
+                sinfo.file_offset += sinfo.block_offset;
+                /* fall through */
+            case PAYLOAD_BLOCK_FULL_PRESENT:
+                /* if the file offset address is in the header zone,
+                 * there is a problem */
+                if (sinfo.file_offset < (1024*1024)) {
+                    ret = -EFAULT;
+                    goto exit;
+                }
+                /* block exists, so we can just overwrite it */
+                qemu_co_mutex_unlock(&s->lock);
+                ret = bdrv_co_writev(bs->file,
+                                    sinfo.file_offset>>BDRV_SECTOR_BITS,
+                                    sinfo.sectors_avail, &hd_qiov);
+                qemu_co_mutex_lock(&s->lock);
+                if (ret < 0) {
+                    goto exit;
+                }
+                break;
+            case PAYLOAD_BLOCK_PARTIALLY_PRESENT:
+                /* we don't yet support difference files, fall through
+                 * to error */
+            default:
+                ret = -EIO;
+                goto exit;
+                break;
+            }
+
+            if (bat_update) {
+                /* this will update the BAT entry into the log journal, and
+                 * then flush the log journal out to disk */
+                ret =  vhdx_log_write_and_flush(bs, s, &bat_entry,
+                                                sizeof(VHDXBatEntry),
+                                                bat_entry_offset);
+                if (ret < 0) {
+                    goto exit;
+                }
+            }
+
+            nb_sectors -= sinfo.sectors_avail;
+            sector_num += sinfo.sectors_avail;
+            bytes_done += sinfo.bytes_avail;
+
+        }
+    }
+
+exit:
+    qemu_co_mutex_unlock(&s->lock);
+    return ret;
 }
 
 
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 12/16] block: vhdx - remove BAT file offset bit shifting
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (10 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 11/16] block: vhdx " Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 13/16] block: vhdx - move more endian translations to vhdx-endian.c Jeff Cody
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

Bit shifting can be fun, but in this case it was unnecessary.  The
upper 44 bits of the 64-bit BAT entry is specifies the File Offset,
so we shifted the bits to get access to the value.

However, per the spec the value is in MB.  So we dutifully shifted back
to the left by 20 bits, to convert to a true uint64_t file offset.

This replaces those steps with just a bit mask, to get rid of the lower
20 bits instead.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 6 ++----
 block/vhdx.h | 1 -
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index 69a06a9..8cba312 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -984,7 +984,7 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num,
 
     sinfo->bytes_avail = sinfo->sectors_avail << s->logical_sector_size_bits;
 
-    sinfo->file_offset = s->bat[sinfo->bat_idx] >> VHDX_BAT_FILE_OFF_BITS;
+    sinfo->file_offset = s->bat[sinfo->bat_idx] & VHDX_BAT_FILE_OFF_MASK;
 
     sinfo->block_offset = block_offset << s->logical_sector_size_bits;
 
@@ -998,7 +998,6 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num,
      * in the block, and add in the payload data block offset
      * in the file, in bytes, to get the final read address */
 
-    sinfo->file_offset <<= 20;  /* now in bytes, rather than 1MB units */
     sinfo->file_offset += sinfo->block_offset;
 }
 
@@ -1097,8 +1096,7 @@ static void vhdx_update_bat_table_entry(BlockDriverState *bs, BDRVVHDXState *s,
 {
     /* The BAT entry is a uint64, with 44 bits for the file offset in units of
      * 1MB, and 3 bits for the block state. */
-    s->bat[sinfo->bat_idx]  = ((sinfo->file_offset>>20) <<
-                               VHDX_BAT_FILE_OFF_BITS);
+    s->bat[sinfo->bat_idx]  = sinfo->file_offset;
 
     s->bat[sinfo->bat_idx] |= state & VHDX_BAT_STATE_BIT_MASK;
 
diff --git a/block/vhdx.h b/block/vhdx.h
index 4f32c01..42089d3 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -229,7 +229,6 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
 /* upper 44 bits are the file offset in 1MB units lower 3 bits are the state
    other bits are reserved */
 #define VHDX_BAT_STATE_BIT_MASK 0x07
-#define VHDX_BAT_FILE_OFF_BITS (64 - 44)
 #define VHDX_BAT_FILE_OFF_MASK  0xFFFFFFFFFFF00000 /* upper 44 bits */
 typedef uint64_t VHDXBatEntry;
 
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 13/16] block: vhdx - move more endian translations to vhdx-endian.c
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (11 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 12/16] block: vhdx - remove BAT file offset bit shifting Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 14/16] block: vhdx - break out code operations to functions Jeff Cody
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

In preperation for vhdx_create(), move more endian translation
functions out to vhdx-endian.c.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx-endian.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/vhdx.c        | 20 +++-----------
 block/vhdx.h        |  9 ++++++-
 3 files changed, 87 insertions(+), 17 deletions(-)

diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c
index 3e93e63..fe879ed 100644
--- a/block/vhdx-endian.c
+++ b/block/vhdx-endian.c
@@ -139,3 +139,78 @@ void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr)
 }
 
 
+/* Region table entries */
+void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr)
+{
+    assert(hdr != NULL);
+
+    le32_to_cpus(&hdr->signature);
+    le32_to_cpus(&hdr->checksum);
+    le32_to_cpus(&hdr->entry_count);
+}
+
+void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr)
+{
+    assert(hdr != NULL);
+
+    cpu_to_le32s(&hdr->signature);
+    cpu_to_le32s(&hdr->checksum);
+    cpu_to_le32s(&hdr->entry_count);
+}
+
+void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
+{
+    assert(e != NULL);
+
+    leguid_to_cpus(&e->guid);
+    le64_to_cpus(&e->file_offset);
+    le32_to_cpus(&e->length);
+    le32_to_cpus(&e->data_bits);
+}
+
+void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
+{
+    assert(e != NULL);
+
+    cpu_to_leguids(&e->guid);
+    cpu_to_le64s(&e->file_offset);
+    cpu_to_le32s(&e->length);
+    cpu_to_le32s(&e->data_bits);
+}
+
+
+/* Metadata headers & table */
+void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr)
+{
+    assert(hdr != NULL);
+
+    le64_to_cpus(&hdr->signature);
+    le16_to_cpus(&hdr->entry_count);
+}
+
+void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr)
+{
+    assert(hdr != NULL);
+
+    cpu_to_le64s(&hdr->signature);
+    cpu_to_le16s(&hdr->entry_count);
+}
+
+void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
+{
+    assert(e != NULL);
+
+    leguid_to_cpus(&e->item_id);
+    le32_to_cpus(&e->offset);
+    le32_to_cpus(&e->length);
+    le32_to_cpus(&e->data_bits);
+}
+void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e)
+{
+    assert(e != NULL);
+
+    cpu_to_leguids(&e->item_id);
+    cpu_to_le32s(&e->offset);
+    cpu_to_le32s(&e->length);
+    cpu_to_le32s(&e->data_bits);
+}
diff --git a/block/vhdx.c b/block/vhdx.c
index 8cba312..2944093 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -472,10 +472,7 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
         goto fail;
     }
     memcpy(&s->rt, buffer, sizeof(s->rt));
-    le32_to_cpus(&s->rt.signature);
-    le32_to_cpus(&s->rt.checksum);
-    le32_to_cpus(&s->rt.entry_count);
-    le32_to_cpus(&s->rt.reserved);
+    vhdx_region_header_le_import(&s->rt);
     offset += sizeof(s->rt);
 
     if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4) ||
@@ -494,10 +491,7 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
         memcpy(&rt_entry, buffer + offset, sizeof(rt_entry));
         offset += sizeof(rt_entry);
 
-        leguid_to_cpus(&rt_entry.guid);
-        le64_to_cpus(&rt_entry.file_offset);
-        le32_to_cpus(&rt_entry.length);
-        le32_to_cpus(&rt_entry.data_bits);
+        vhdx_region_entry_le_import(&rt_entry);
 
         /* check for region overlap between these entries, and any
          * other memory regions in the file */
@@ -587,9 +581,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
     memcpy(&s->metadata_hdr, buffer, sizeof(s->metadata_hdr));
     offset += sizeof(s->metadata_hdr);
 
-    le64_to_cpus(&s->metadata_hdr.signature);
-    le16_to_cpus(&s->metadata_hdr.reserved);
-    le16_to_cpus(&s->metadata_hdr.entry_count);
+    vhdx_metadata_header_le_import(&s->metadata_hdr);
 
     if (memcmp(&s->metadata_hdr.signature, "metadata", 8)) {
         ret = -EINVAL;
@@ -608,11 +600,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
         memcpy(&md_entry, buffer + offset, sizeof(md_entry));
         offset += sizeof(md_entry);
 
-        leguid_to_cpus(&md_entry.item_id);
-        le32_to_cpus(&md_entry.offset);
-        le32_to_cpus(&md_entry.length);
-        le32_to_cpus(&md_entry.data_bits);
-        le32_to_cpus(&md_entry.reserved2);
+        vhdx_metadata_entry_le_import(&md_entry);
 
         if (guid_eq(md_entry.item_id, file_param_guid)) {
             if (s->metadata_entries.present & META_FILE_PARAMETER_PRESENT) {
diff --git a/block/vhdx.h b/block/vhdx.h
index 42089d3..d35345d 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -419,7 +419,14 @@ void vhdx_log_desc_le_export(VHDXLogDescriptor *d);
 void vhdx_log_data_le_export(VHDXLogDataSector *d);
 void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr);
 void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr);
-
+void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr);
+void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr);
+void vhdx_region_entry_le_import(VHDXRegionTableEntry *e);
+void vhdx_region_entry_le_export(VHDXRegionTableEntry *e);
+void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr);
+void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr);
+void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e);
+void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e);
 int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s);
 
 #endif
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 14/16] block: vhdx - break out code operations to functions
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (12 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 13/16] block: vhdx - move more endian translations to vhdx-endian.c Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 15/16] block: vhdx - fix comment typos in header, fix incorrect struct fields Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 16/16] block: vhdx - add .bdrv_create() support Jeff Cody
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This is preperation for vhdx_create().  The ability to write headers,
and calculate the number of BAT entries will be needed within the
create() functions, so move this relevant code into helper functions.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 121 +++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 80 insertions(+), 41 deletions(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index 2944093..94fa84f 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -248,6 +248,14 @@ static void vhdx_region_unregister_all(BDRVVHDXState *s)
     }
 }
 
+static void vhdx_set_shift_bits(BDRVVHDXState *s)
+{
+    s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size);
+    s->sectors_per_block_bits =   31 - clz32(s->sectors_per_block);
+    s->chunk_ratio_bits =         63 - clz64(s->chunk_ratio);
+    s->block_size_bits =          31 - clz32(s->block_size);
+}
+
 /*
  * Per the MS VHDX Specification, for every VHDX file:
  *      - The header section is fixed size - 1 MB
@@ -267,6 +275,50 @@ static int vhdx_probe(const uint8_t *buf, int buf_size, const char *filename)
     return 0;
 }
 
+/*
+ * Writes the header to the specified offset.
+ *
+ * This will optionally read in buffer data from disk (otherwise zero-fill),
+ * and then update the header checksum.  Header is converted to proper
+ * endianness before being written to the specified file offset
+ */
+static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr,
+                             uint64_t offset, bool read)
+{
+    uint8_t *buffer = NULL;
+    int ret;
+    VHDXHeader header_le;
+
+    assert(bs_file != NULL);
+    assert(hdr != NULL);
+
+    /* the header checksum is not over just the packed size of VHDXHeader,
+     * but rather over the entire 'reserved' range for the header, which is
+     * 4KB (VHDX_HEADER_SIZE). */
+
+    buffer = qemu_blockalign(bs_file, VHDX_HEADER_SIZE);
+    if (read) {
+        /* if true, we can't assume the extra reserved bytes are 0 */
+        ret = bdrv_pread(bs_file, offset, buffer, VHDX_HEADER_SIZE);
+        if (ret < 0) {
+            goto exit;
+        }
+    } else {
+        memset(buffer, 0, VHDX_HEADER_SIZE);
+    }
+
+    /* overwrite the actual VHDXHeader portion */
+    memcpy(buffer, hdr, sizeof(VHDXHeader));
+    hdr->checksum = vhdx_update_checksum(buffer, VHDX_HEADER_SIZE,
+                                         offsetof(VHDXHeader, checksum));
+    vhdx_header_le_export(hdr, &header_le);
+    ret = bdrv_pwrite_sync(bs_file, offset, &header_le, sizeof(VHDXHeader));
+
+exit:
+    qemu_vfree(buffer);
+    return ret;
+}
+
 /* Update the VHDX headers
  *
  * This follows the VHDX spec procedures for header updates.
@@ -282,8 +334,6 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
 
     VHDXHeader *active_header;
     VHDXHeader *inactive_header;
-    VHDXHeader header_le;
-    uint8_t *buffer;
 
     /* operate on the non-current header */
     if (s->curr_header == 0) {
@@ -311,31 +361,13 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s,
         inactive_header->log_guid = *log_guid;
     }
 
-    /* the header checksum is not over just the packed size of VHDXHeader,
-     * but rather over the entire 'reserved' range for the header, which is
-     * 4KB (VHDX_HEADER_SIZE). */
-
-    buffer = qemu_blockalign(bs, VHDX_HEADER_SIZE);
-    /* we can't assume the extra reserved bytes are 0 */
-    ret = bdrv_pread(bs->file, header_offset, buffer, VHDX_HEADER_SIZE);
-    if (ret < 0) {
-        goto exit;
-    }
-    /* overwrite the actual VHDXHeader portion */
-    memcpy(buffer, inactive_header, sizeof(VHDXHeader));
-    inactive_header->checksum =
-                        vhdx_update_checksum(buffer, VHDX_HEADER_SIZE,
-                                             offsetof(VHDXHeader, checksum));
-    vhdx_header_le_export(inactive_header, &header_le);
-    ret = bdrv_pwrite_sync(bs->file, header_offset, &header_le,
-                           sizeof(VHDXHeader));
+    vhdx_write_header(bs->file, inactive_header, header_offset, true);
     if (ret < 0) {
         goto exit;
     }
     s->curr_header = hdr_idx;
 
 exit:
-    qemu_vfree(buffer);
     return ret;
 }
 
@@ -773,10 +805,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
         goto exit;
     }
 
-    s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size);
-    s->sectors_per_block_bits =   31 - clz32(s->sectors_per_block);
-    s->chunk_ratio_bits =         63 - clz64(s->chunk_ratio);
-    s->block_size_bits =          31 - clz32(s->block_size);
+    vhdx_set_shift_bits(s);
 
     ret = 0;
 
@@ -785,6 +814,31 @@ exit:
     return ret;
 }
 
+/*
+ * Calculate the number of BAT entries, including sector
+ * bitmap entries.
+ */
+static void vhdx_calc_bat_entries(BDRVVHDXState *s)
+{
+    uint32_t data_blocks_cnt, bitmap_blocks_cnt;
+
+    data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits;
+    if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) {
+        data_blocks_cnt++;
+    }
+    bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits;
+    if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) {
+        bitmap_blocks_cnt++;
+    }
+
+    if (s->parent_entries) {
+        s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
+    } else {
+        s->bat_entries = data_blocks_cnt +
+                         ((data_blocks_cnt - 1) >> s->chunk_ratio_bits);
+    }
+
+}
 
 static void vhdx_close(BlockDriverState *bs)
 {
@@ -803,7 +857,6 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
     int ret = 0;
     uint32_t i;
     uint64_t signature;
-    uint32_t data_blocks_cnt, bitmap_blocks_cnt;
     bool log_flushed = false;
 
 
@@ -867,21 +920,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
      * logical_sector_size */
     bs->total_sectors = s->virtual_disk_size >> s->logical_sector_size_bits;
 
-    data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits;
-    if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) {
-        data_blocks_cnt++;
-    }
-    bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits;
-    if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) {
-        bitmap_blocks_cnt++;
-    }
-
-    if (s->parent_entries) {
-        s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
-    } else {
-        s->bat_entries = data_blocks_cnt +
-                         ((data_blocks_cnt - 1) >> s->chunk_ratio_bits);
-    }
+    vhdx_calc_bat_entries(s);
 
     s->bat_offset = s->bat_rt.file_offset;
 
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 15/16] block: vhdx - fix comment typos in header, fix incorrect struct fields
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (13 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 14/16] block: vhdx - break out code operations to functions Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 16/16] block: vhdx - add .bdrv_create() support Jeff Cody
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

VHDXPage83Data and VHDXParentLocatorHeader both incorrectly had their
MSGUID fields set as arrays of 16.  This is incorrect (it stems from
an early version where those fields were uint_8 arrays).  Those fields
were, up to this patch, unused.

Also, there were a couple of typos and incorrect wording in comments,
and those have been fixed up as well.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/block/vhdx.h b/block/vhdx.h
index d35345d..c2ba697 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -58,7 +58,7 @@
 typedef struct VHDXFileIdentifier {
     uint64_t    signature;              /* "vhdxfile" in ASCII */
     uint16_t    creator[256];           /* optional; utf-16 string to identify
-                                           the vhdx file creator.  Diagnotistic
+                                           the vhdx file creator.  Diagnostic
                                            only */
 } VHDXFileIdentifier;
 
@@ -114,8 +114,8 @@ typedef struct QEMU_PACKED VHDXHeader {
                                            there is no valid log. If non-zero,
                                            log entries with this guid are
                                            valid. */
-    uint16_t    log_version;            /* version of the log format. Mustn't be
-                                           zero, unless log_guid is also zero */
+    uint16_t    log_version;            /* version of the log format. Must be
+                                           set to zero */
     uint16_t    version;                /* version of the vhdx file.  Currently,
                                            only supported version is "1" */
     uint32_t    log_length;             /* length of the log.  Must be multiple
@@ -281,7 +281,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskSize {
 } VHDXVirtualDiskSize;
 
 typedef struct QEMU_PACKED VHDXPage83Data {
-    MSGUID      page_83_data[16];       /* unique id for scsi devices that
+    MSGUID      page_83_data;           /* unique id for scsi devices that
                                            support page 0x83 */
 } VHDXPage83Data;
 
@@ -296,7 +296,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskPhysicalSectorSize {
 } VHDXVirtualDiskPhysicalSectorSize;
 
 typedef struct QEMU_PACKED VHDXParentLocatorHeader {
-    MSGUID      locator_type[16];       /* type of the parent virtual disk. */
+    MSGUID      locator_type;           /* type of the parent virtual disk. */
     uint16_t    reserved;
     uint16_t    key_value_count;        /* number of key/value pairs for this
                                            locator */
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Qemu-devel] [PATCH v5 16/16] block: vhdx - add .bdrv_create() support
  2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
                   ` (14 preceding siblings ...)
  2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 15/16] block: vhdx - fix comment typos in header, fix incorrect struct fields Jeff Cody
@ 2013-09-03 19:53 ` Jeff Cody
  15 siblings, 0 replies; 17+ messages in thread
From: Jeff Cody @ 2013-09-03 19:53 UTC (permalink / raw
  To: qemu-devel; +Cc: kwolf, famz, stefanha

This adds support for VHDX image creation, for images of type "Fixed"
and "Dynamic".  "Differencing" types (i.e., VHDX images with backing
files) are currently not supported.

Options for image creation include:
    * log size:
        The size of the journaling log for VHDX.  Minimum is 1MB,
        and it must be a multiple of 1MB. Invalid log sizes will be
        silently fixed by rounding up to the nearest MB.

        Default is 1MB.

    * block size:
        This is the size of a payload block.  The range is 1MB to 256MB,
        inclusive, and must be a multiple of 1MB as well.  Invalid sizes
        and multiples will be silently fixed.  If '0' is passed, then
        a sane size is chosen (depending on virtual image size).

        Default is 0 (Auto-select).

    * subformat:
        - "dynamic"
            An image without data pre-allocated.
        - "fixed"
            An image with data pre-allocated.

        Default is "dynamic"

When creating the image file, the lettered sections are created:

-----------------------------------------------------------------.
|   (A)    |   (B)    |    (C)    |     (D)       |     (E)
|  File ID |  Header1 |  Header 2 |  Region Tbl 1 |  Region Tbl 2
|          |          |           |               |
.-----------------------------------------------------------------.
0         64KB      128KB       192KB           256KB          320KB

.---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
|     (F)     |     (G)       |    (H)    |
| Journal Log |  BAT / Bitmap |  Metadata |  .... data ......
|             |               |           |
.---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
1MB         (var.)          (var.)      (var.)

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 block/vhdx.c | 532 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/vhdx.h |  15 +-
 2 files changed, 546 insertions(+), 1 deletion(-)

diff --git a/block/vhdx.c b/block/vhdx.c
index 94fa84f..4998707 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -22,6 +22,18 @@
 #include "block/vhdx.h"
 
 #include <uuid/uuid.h>
+#include <glib.h>
+
+/* Options for VHDX creation */
+
+#define VHDX_BLOCK_OPT_LOG_SIZE   "log_size"
+#define VHDX_BLOCK_OPT_BLOCK_SIZE "block_size"
+
+typedef enum VHDXImageType {
+    VHDX_TYPE_DYNAMIC = 0,
+    VHDX_TYPE_FIXED,
+    VHDX_TYPE_DIFFERENCING,   /* Currently unsupported */
+} VHDXImageType;
 
 /* Several metadata and region table data entries are identified by
  * guids in  a MS-specific GUID format. */
@@ -1261,6 +1273,523 @@ exit:
 }
 
 
+
+/*
+ * Create VHDX Headers
+ *
+ * There are 2 headers, and the highest sequence number will represent
+ * the active header
+ */
+static int vhdx_create_new_headers(BlockDriverState *bs, uint64_t image_size,
+                                   uint32_t log_size)
+{
+    int ret = 0;
+    VHDXHeader *hdr = NULL;
+
+    hdr = g_malloc0(sizeof(VHDXHeader));
+
+    hdr->signature       = VHDX_HEADER_SIGNATURE;
+    hdr->sequence_number = g_random_int();
+    hdr->log_version     = 0;
+    hdr->version         = 1;
+    hdr->log_length      = log_size;
+    hdr->log_offset      = VHDX_HEADER_SECTION_END;
+    vhdx_guid_generate(&hdr->file_write_guid);
+    vhdx_guid_generate(&hdr->data_write_guid);
+
+    ret = vhdx_write_header(bs, hdr, VHDX_HEADER1_OFFSET, false);
+    if (ret < 0) {
+        goto exit;
+    }
+    hdr->sequence_number++;
+    ret = vhdx_write_header(bs, hdr, VHDX_HEADER2_OFFSET, false);
+    if (ret < 0) {
+        goto exit;
+    }
+
+exit:
+    g_free(hdr);
+    return ret;
+}
+
+
+/*
+ * Create the Metadata entries.
+ *
+ * For more details on the entries, see section 3.5 (pg 29) in the
+ * VHDX 1.00 specification.
+ *
+ * We support 5 metadata entries (all required by spec):
+ *          File Parameters,
+ *          Virtual Disk Size,
+ *          Page 83 Data,
+ *          Logical Sector Size,
+ *          Physical Sector Size
+ *
+ * The first 64KB of the Metadata section is reserved for the metadata
+ * header and entries; beyond that, the metadata items themselves reside.
+ */
+static int vhdx_create_new_metadata(BlockDriverState *bs,
+                                    uint64_t image_size,
+                                    uint32_t block_size,
+                                    uint32_t sector_size,
+                                    uint64_t metadata_offset,
+                                    VHDXImageType type)
+{
+    int ret = 0;
+    uint32_t offset = 0;
+    void *buffer = NULL;
+    void *entry_buffer;
+    VHDXMetadataTableHeader *md_table;;
+    VHDXMetadataTableEntry  *md_table_entry;
+
+    /* Metadata entries */
+    VHDXFileParameters     *mt_file_params;
+    VHDXVirtualDiskSize    *mt_virtual_size;
+    VHDXPage83Data         *mt_page83;
+    VHDXVirtualDiskLogicalSectorSize  *mt_log_sector_size;
+    VHDXVirtualDiskPhysicalSectorSize *mt_phys_sector_size;
+
+    entry_buffer = g_malloc0(sizeof(VHDXFileParameters)               +
+                             sizeof(VHDXVirtualDiskSize)              +
+                             sizeof(VHDXPage83Data)                   +
+                             sizeof(VHDXVirtualDiskLogicalSectorSize) +
+                             sizeof(VHDXVirtualDiskPhysicalSectorSize));
+
+    mt_file_params = entry_buffer;
+    offset += sizeof(VHDXFileParameters);
+    mt_virtual_size = entry_buffer + offset;
+    offset += sizeof(VHDXVirtualDiskSize);
+    mt_page83 = entry_buffer + offset;
+    offset += sizeof(VHDXPage83Data);
+    mt_log_sector_size = entry_buffer + offset;
+    offset += sizeof(VHDXVirtualDiskLogicalSectorSize);
+    mt_phys_sector_size = entry_buffer + offset;
+
+    mt_file_params->block_size = cpu_to_le32(block_size);
+    if (type == VHDX_TYPE_FIXED) {
+        mt_file_params->data_bits |= VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED;
+        cpu_to_le32s(&mt_file_params->data_bits);
+    }
+
+    vhdx_guid_generate(&mt_page83->page_83_data);
+    cpu_to_leguids(&mt_page83->page_83_data);
+    mt_virtual_size->virtual_disk_size        = cpu_to_le64(image_size);
+    mt_log_sector_size->logical_sector_size   = cpu_to_le32(sector_size);
+    mt_phys_sector_size->physical_sector_size = cpu_to_le32(sector_size);
+
+    buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE);
+    md_table = buffer;
+
+    md_table->signature   = VHDX_METADATA_SIGNATURE;
+    md_table->entry_count = 5;
+    vhdx_metadata_header_le_export(md_table);
+
+
+    /* This will reference beyond the reserved table portion */
+    offset = 64 * KiB;
+
+    md_table_entry = buffer + sizeof(VHDXMetadataTableHeader);
+
+    md_table_entry[0].item_id = file_param_guid;
+    md_table_entry[0].offset  = offset;
+    md_table_entry[0].length  = sizeof(VHDXFileParameters);
+    md_table_entry[0].data_bits |= VHDX_META_FLAGS_IS_REQUIRED;
+    offset += md_table_entry[0].length;
+    vhdx_metadata_entry_le_export(&md_table_entry[0]);
+
+    md_table_entry[1].item_id = virtual_size_guid;
+    md_table_entry[1].offset  = offset;
+    md_table_entry[1].length  = sizeof(VHDXVirtualDiskSize);
+    md_table_entry[1].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
+    offset += md_table_entry[1].length;
+    vhdx_metadata_entry_le_export(&md_table_entry[1]);
+
+    md_table_entry[2].item_id = page83_guid;
+    md_table_entry[2].offset  = offset;
+    md_table_entry[2].length  = sizeof(VHDXPage83Data);
+    md_table_entry[2].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
+    offset += md_table_entry[2].length;
+    vhdx_metadata_entry_le_export(&md_table_entry[2]);
+
+    md_table_entry[3].item_id = logical_sector_guid;
+    md_table_entry[3].offset  = offset;
+    md_table_entry[3].length  = sizeof(VHDXVirtualDiskLogicalSectorSize);
+    md_table_entry[3].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
+    offset += md_table_entry[3].length;
+    vhdx_metadata_entry_le_export(&md_table_entry[3]);
+
+    md_table_entry[4].item_id = phys_sector_guid;
+    md_table_entry[4].offset  = offset;
+    md_table_entry[4].length  = sizeof(VHDXVirtualDiskPhysicalSectorSize);
+    md_table_entry[4].data_bits |= VHDX_META_FLAGS_IS_REQUIRED |
+                                   VHDX_META_FLAGS_IS_VIRTUAL_DISK;
+    vhdx_metadata_entry_le_export(&md_table_entry[4]);
+
+    ret = bdrv_pwrite(bs, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    ret = bdrv_pwrite(bs, metadata_offset + (64 * KiB), entry_buffer,
+                      VHDX_HEADER_BLOCK_SIZE);
+    if (ret < 0) {
+        goto exit;
+    }
+
+
+exit:
+    g_free(buffer);
+    g_free(entry_buffer);
+    return ret;
+}
+
+/* This create the actual BAT itself.  We currently only support
+ * 'Dynamic' and 'Fixed' image types.
+ *
+ *  Dynamic images: default state of the BAT is all zeroes.
+ *
+ *  Fixed images: default state of the BAT is fully populated, with
+ *                file offsets and state PAYLOAD_BLOCK_FULL_PRESENT.
+ */
+static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
+                           uint64_t image_size, VHDXImageType type,
+                           VHDXRegionTableEntry *rt_bat)
+{
+    int ret = 0;
+    uint64_t data_file_offset;
+    uint64_t total_sectors = 0;
+    uint64_t sector_num = 0;
+    uint64_t unused;
+    VHDXSectorInfo sinfo;
+
+    assert(s->bat == NULL);
+
+    /* this gives a data start after BAT/bitmap entries, and well
+     * past any metadata entries (with a 4 MB buffer for future
+     * expansion */
+    data_file_offset = rt_bat->file_offset + rt_bat->length + 5 * MiB;
+    total_sectors = image_size >> s->logical_sector_size_bits;
+
+    if (type == VHDX_TYPE_DYNAMIC) {
+        /* All zeroes, so we can just extend the file - the end of the BAT
+         * is the furthest thing we have written yet */
+        ret = bdrv_truncate(bs, data_file_offset);
+        if (ret < 0) {
+            goto exit;
+        }
+    } else if (type == VHDX_TYPE_FIXED) {
+        ret = bdrv_truncate(bs, data_file_offset + image_size);
+        if (ret < 0) {
+            goto exit;
+        }
+        /* for a fixed file, the default BAT entry is not zero */
+        s->bat = g_malloc0(rt_bat->length);
+        /* fill the BAT by emulating sector writes of sectors_per_block size */
+        while (sector_num < total_sectors) {
+            vhdx_block_translate(s, sector_num, s->sectors_per_block, &sinfo);
+            sinfo.file_offset = data_file_offset +
+                                (sector_num << s->logical_sector_size_bits);
+            sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB);
+            vhdx_update_bat_table_entry(bs, s, &sinfo, &unused, &unused,
+                                        PAYLOAD_BLOCK_FULL_PRESENT);
+            cpu_to_le64s(&s->bat[sinfo.bat_idx]);
+            sector_num += s->sectors_per_block;
+        }
+        ret = bdrv_pwrite(bs, rt_bat->file_offset, s->bat, rt_bat->length);
+        if (ret < 0) {
+            goto exit;
+        }
+    } else {
+        ret = -ENOTSUP;
+        goto exit;
+    }
+
+
+exit:
+    g_free(s->bat);
+    return ret;
+}
+
+/* Creates the region table header, and region table entries.
+ * There are 2 supported region table entries: BAT, and Metadata/
+ *
+ * As the calculations for the BAT region table are also needed
+ * to create the BAT itself, we will also cause the BAT to be
+ * created.
+ */
+static int vhdx_create_new_region_table(BlockDriverState *bs,
+                                        uint64_t image_size,
+                                        uint32_t block_size,
+                                        uint32_t sector_size,
+                                        uint32_t log_size,
+                                        VHDXImageType type,
+                                        uint64_t *metadata_offset)
+{
+    int ret = 0;
+    uint32_t offset = 0;
+    void *buffer = NULL;
+    BDRVVHDXState *s = NULL;
+    VHDXRegionTableHeader *region_table;
+    VHDXRegionTableEntry *rt_bat;
+    VHDXRegionTableEntry *rt_metadata;
+
+    assert(metadata_offset != NULL);
+
+    /* Populate enough of the BDRVVHDXState to be able to use the
+     * pre-existing BAT calculation, translation, and update functions */
+    s = g_malloc0(sizeof(BDRVVHDXState));
+
+    s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) *
+                     (uint64_t) sector_size / (uint64_t) block_size;
+
+    s->sectors_per_block = block_size / sector_size;
+    s->virtual_disk_size = image_size;
+    s->block_size = block_size;
+    s->logical_sector_size = sector_size;
+
+    vhdx_set_shift_bits(s);
+
+    vhdx_calc_bat_entries(s);
+
+    /* At this point the VHDX state is populated enough for creation */
+
+    /* a single buffer is used so we can calculate the checksum over the
+     * entire 64KB block */
+    buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE);
+    region_table = buffer;
+    offset += sizeof(VHDXRegionTableHeader);
+    rt_bat = buffer + offset;
+    offset += sizeof(VHDXRegionTableEntry);
+    rt_metadata  = buffer + offset;
+
+    region_table->signature = VHDX_REGION_SIGNATURE;
+    region_table->entry_count = 2;   /* BAT and Metadata */
+
+    rt_bat->guid        = bat_guid;
+    rt_bat->length      = ROUND_UP(s->bat_entries * sizeof(VHDXBatEntry), MiB);
+    rt_bat->file_offset = ROUND_UP(VHDX_HEADER_SECTION_END + log_size, MiB);
+    s->bat_offset = rt_bat->file_offset;
+
+    rt_metadata->guid        = metadata_guid;
+    rt_metadata->file_offset = ROUND_UP(rt_bat->file_offset + rt_bat->length,
+                                        MiB);
+    rt_metadata->length      = 1 * MiB; /* min size, and more than enough */
+    *metadata_offset = rt_metadata->file_offset;
+
+    vhdx_update_checksum(buffer, VHDX_HEADER_BLOCK_SIZE,
+                         offsetof(VHDXRegionTableHeader, checksum));
+
+
+    /* The region table gives us the data we need to create the BAT,
+     * so do that now */
+    ret = vhdx_create_bat(bs, s, image_size, type, rt_bat);
+
+    /* Now write out the region headers to disk */
+    vhdx_region_header_le_export(region_table);
+    vhdx_region_entry_le_export(rt_bat);
+    vhdx_region_entry_le_export(rt_metadata);
+
+    ret = bdrv_pwrite(bs, VHDX_REGION_TABLE_OFFSET, buffer,
+                      VHDX_HEADER_BLOCK_SIZE);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    ret = bdrv_pwrite(bs, VHDX_REGION_TABLE2_OFFSET, buffer,
+                      VHDX_HEADER_BLOCK_SIZE);
+    if (ret < 0) {
+        goto exit;
+    }
+
+
+exit:
+    g_free(s);
+    g_free(buffer);
+    return ret;
+}
+
+/* We need to create the following elements:
+ *
+ *    .-----------------------------------------------------------------.
+ *    |   (A)    |   (B)    |    (C)    |     (D)       |     (E)       |
+ *    |  File ID |  Header1 |  Header 2 |  Region Tbl 1 |  Region Tbl 2 |
+ *    |          |          |           |               |               |
+ *    .-----------------------------------------------------------------.
+ *    0         64KB      128KB       192KB           256KB           320KB
+ *
+ *
+ *    .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
+ *    |     (F)     |     (G)       |    (H)    |                        |
+ *    | Journal Log |  BAT / Bitmap |  Metadata |  .... data ......      |
+ *    |             |               |           |                        |
+ *    .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
+ *   1MB
+ */
+static int vhdx_create(const char *filename, QEMUOptionParameter *options)
+{
+    int ret = 0;
+    uint64_t image_size = (uint64_t) 2 * GiB;
+    uint32_t log_size   = 1 * MiB;
+    uint32_t block_size = 0;
+    uint64_t signature;
+    uint64_t metadata_offset;
+
+    gunichar2 *creator = NULL;
+    glong creator_items;
+    BlockDriverState *bs;
+    const char *type = NULL;
+    VHDXImageType image_type;
+
+    while (options && options->name) {
+        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+            image_size = options->value.n;
+        } else if (!strcmp(options->name, VHDX_BLOCK_OPT_LOG_SIZE)) {
+            log_size = options->value.n;
+        } else if (!strcmp(options->name, VHDX_BLOCK_OPT_BLOCK_SIZE)) {
+            block_size = options->value.n;
+        } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) {
+            type = options->value.s;
+        }
+        options++;
+    }
+
+    if (image_size > VHDX_MAX_IMAGE_SIZE) {
+        fprintf(stderr, "Image size too large; max of 64TB\n");
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    if (type == NULL) {
+        type = "dynamic";
+    }
+
+    if (!strcmp(type, "dynamic")) {
+        image_type = VHDX_TYPE_DYNAMIC;
+    } else if (!strcmp(type, "fixed")) {
+        image_type = VHDX_TYPE_FIXED;
+    } else if (!strcmp(type, "differencing")) {
+        fprintf(stderr, "Differencing files not yet supported\n");
+        ret = -ENOTSUP;
+        goto exit;
+    } else {
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    /* These are pretty arbitrary, and mainly designed to keep the BAT
+     * size reasonable to load into RAM */
+    if (block_size == 0) {
+        if (image_size > 32 * TiB) {
+            block_size = 64 * MiB;
+        } else if (image_size > (uint64_t) 100 * GiB) {
+            block_size = 32 * MiB;
+        } else if (image_size > 1 * GiB) {
+            block_size = 16 * MiB;
+        } else {
+            block_size = 8 * MiB;
+        }
+    }
+
+
+    /* make the log size close to what was specified, but must be
+     * min 1MB, and multiple of 1MB */
+    log_size = ROUND_UP(log_size, MiB);
+
+    block_size = ROUND_UP(block_size, MiB);
+    block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX :
+                                                    block_size;
+
+    ret = bdrv_create_file(filename, options);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR);
+    if (ret < 0) {
+        goto exit;
+    }
+
+    /* Create (A) */
+
+    /* The creator field is optional, but may be useful for
+     * debugging / diagnostics */
+    creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
+                              &creator_items, NULL);
+    signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
+    bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature));
+    if (ret < 0) {
+        goto delete_and_exit;
+    }
+    if (creator) {
+        bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), creator,
+                    creator_items * sizeof(gunichar2));
+        if (ret < 0) {
+            goto delete_and_exit;
+        }
+    }
+
+
+    /* Creates (B),(C) */
+    ret = vhdx_create_new_headers(bs, image_size, log_size);
+    if (ret < 0) {
+        goto delete_and_exit;
+    }
+
+    /* Creates (D),(E),(G) explicitly. (F) created as by-product */
+    ret = vhdx_create_new_region_table(bs, image_size, block_size, 512,
+                                       log_size, image_type, &metadata_offset);
+    if (ret < 0) {
+        goto delete_and_exit;
+    }
+
+    /* Creates (H) */
+    ret = vhdx_create_new_metadata(bs, image_size, block_size, 512,
+                                   metadata_offset, image_type);
+    if (ret < 0) {
+        goto delete_and_exit;
+    }
+
+
+
+delete_and_exit:
+    bdrv_delete(bs);
+exit:
+    g_free(creator);
+    return ret;
+}
+
+static QEMUOptionParameter vhdx_create_options[] = {
+    {
+        .name = BLOCK_OPT_SIZE,
+        .type = OPT_SIZE,
+        .help = "Virtual disk size; max of 64TB."
+    },
+    {
+        .name = VHDX_BLOCK_OPT_LOG_SIZE,
+        .type = OPT_SIZE,
+        .value.n = 1 * MiB,
+        .help = "Log size; min 1MB."
+    },
+    {
+        .name = VHDX_BLOCK_OPT_BLOCK_SIZE,
+        .type = OPT_SIZE,
+        .value.n = 0,
+        .help = "Block Size; min 1MB, max 256MB. " \
+                "0 means auto-calculate based on image size."
+    },
+    {
+        .name = BLOCK_OPT_SUBFMT,
+        .type = OPT_STRING,
+        .help = "VHDX format type, can be either 'dynamic' or 'fixed'. "\
+                "Default is 'dynamic'."
+    },
+    { NULL }
+};
+
 static BlockDriver bdrv_vhdx = {
     .format_name            = "vhdx",
     .instance_size          = sizeof(BDRVVHDXState),
@@ -1270,6 +1799,9 @@ static BlockDriver bdrv_vhdx = {
     .bdrv_reopen_prepare    = vhdx_reopen_prepare,
     .bdrv_co_readv          = vhdx_co_readv,
     .bdrv_co_writev         = vhdx_co_writev,
+    .bdrv_create            = vhdx_create,
+
+    .create_options         = vhdx_create_options,
 };
 
 static void bdrv_vhdx_init(void)
diff --git a/block/vhdx.h b/block/vhdx.h
index c2ba697..86e75f5 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -18,6 +18,11 @@
 #ifndef BLOCK_VHDX_H
 #define BLOCK_VHDX_H
 
+#define KiB              (1 * 1024)
+#define MiB            (KiB * 1024)
+#define GiB            (MiB * 1024)
+#define TiB ((uint64_t) GiB * 1024)
+
 /* Structures and fields present in the VHDX file */
 
 /* The header section has the following blocks,
@@ -36,8 +41,9 @@
 #define VHDX_HEADER1_OFFSET         (VHDX_HEADER_BLOCK_SIZE * 1)
 #define VHDX_HEADER2_OFFSET         (VHDX_HEADER_BLOCK_SIZE * 2)
 #define VHDX_REGION_TABLE_OFFSET    (VHDX_HEADER_BLOCK_SIZE * 3)
+#define VHDX_REGION_TABLE2_OFFSET   (VHDX_HEADER_BLOCK_SIZE * 4)
 
-
+#define VHDX_HEADER_SECTION_END     (1 * MiB)
 /*
  * A note on the use of MS-GUID fields.  For more details on the GUID,
  * please see: https://en.wikipedia.org/wiki/Globally_unique_identifier.
@@ -55,6 +61,7 @@
 /* These structures are ones that are defined in the VHDX specification
  * document */
 
+#define VHDX_FILE_SIGNATURE 0x656C696678646876  /* "vhdxfile" in ASCII */
 typedef struct VHDXFileIdentifier {
     uint64_t    signature;              /* "vhdxfile" in ASCII */
     uint16_t    creator[256];           /* optional; utf-16 string to identify
@@ -85,6 +92,7 @@ typedef struct QEMU_PACKED MSGUID {
 /* The full header is 4KB, although the actual header data is much smaller.
  * But for the checksum calculation, it is over the entire 4KB structure,
  * not just the defined portion of it */
+#define VHDX_HEADER_SIGNATURE 0x64616568
 typedef struct QEMU_PACKED VHDXHeader {
     uint32_t    signature;              /* "head" in ASCII */
     uint32_t    checksum;               /* CRC-32C hash of the whole header */
@@ -125,6 +133,7 @@ typedef struct QEMU_PACKED VHDXHeader {
 } VHDXHeader;
 
 /* Header for the region table block */
+#define VHDX_REGION_SIGNATURE  0x69676572  /* "regi" in ASCII */
 typedef struct QEMU_PACKED VHDXRegionTableHeader {
     uint32_t    signature;              /* "regi" in ASCII */
     uint32_t    checksum;               /* CRC-32C hash of the 64KB table */
@@ -238,6 +247,7 @@ typedef uint64_t VHDXBatEntry;
 #define VHDX_METADATA_MAX_ENTRIES 2047  /* not including the header */
 #define VHDX_METADATA_TABLE_MAX_SIZE \
     (VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1))
+#define VHDX_METADATA_SIGNATURE 0x617461646174656D  /* "metadata" in ASCII */
 typedef struct QEMU_PACKED VHDXMetadataTableHeader {
     uint64_t    signature;              /* "metadata" in ASCII */
     uint16_t    reserved;
@@ -267,6 +277,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
                                                    If set indicates a fixed
                                                    size VHDX file */
 #define VHDX_PARAMS_HAS_PARENT           0x02    /* has parent / backing file */
+#define VHDX_BLOCK_SIZE_MIN             (1   * MiB)
+#define VHDX_BLOCK_SIZE_MAX             (256 * MiB)
 typedef struct QEMU_PACKED VHDXFileParameters {
     uint32_t    block_size;             /* size of each payload block, always
                                            power of 2, <= 256MB and >= 1MB. */
@@ -274,6 +286,7 @@ typedef struct QEMU_PACKED VHDXFileParameters {
                                            the rest are reserved (see above) */
 } VHDXFileParameters;
 
+#define VHDX_MAX_IMAGE_SIZE  ((uint64_t) 64 * TiB)
 typedef struct QEMU_PACKED VHDXVirtualDiskSize {
     uint64_t    virtual_disk_size;      /* Size of the virtual disk, in bytes.
                                            Must be multiple of the sector size,
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2013-09-03 19:54 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-09-03 19:53 [Qemu-devel] [PATCH v5 00/16] VHDX log replay and write support, .bdrv_create() Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 01/16] block: vhdx - minor comments and typo correction Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 02/16] block: vhdx - add header update capability Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 03/16] block: vhdx code movement - VHDXMetadataEntries and BDRVVHDXState to header Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 04/16] block: vhdx - log support struct and defines Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 05/16] block: vhdx - break endian translation functions out Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 06/16] block: vhdx - update log guid in header, and first write tracker Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 07/16] block: vhdx code movement - move vhdx_close() above vhdx_open() Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 08/16] block: vhdx - log parsing, replay, and flush support Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 09/16] block: vhdx - add region overlap detection for image files Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 10/16] block: vhdx - add log write support Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 11/16] block: vhdx " Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 12/16] block: vhdx - remove BAT file offset bit shifting Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 13/16] block: vhdx - move more endian translations to vhdx-endian.c Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 14/16] block: vhdx - break out code operations to functions Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 15/16] block: vhdx - fix comment typos in header, fix incorrect struct fields Jeff Cody
2013-09-03 19:53 ` [Qemu-devel] [PATCH v5 16/16] block: vhdx - add .bdrv_create() support Jeff Cody

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.