Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-07 Thread Jeff Cody
On Thu, Aug 01, 2013 at 03:44:41PM +0200, Stefan Hajnoczi wrote:
 On Wed, Jul 31, 2013 at 11:23:47PM -0400, Jeff Cody wrote:
  @@ -212,6 +242,24 @@ 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).
  + *
  + * We can do this with uuid_generate if uuid.h is present,
  + * however not all systems have uuid and the generation is
  + * pretty straightforward for the DCE + random usage case
 
 The talk of uuid.h not being available confuses me.  The code has no
 #ifdef and we do not define uuid_generate() ourselves.
 
 Is this an outdated comment?
 
  +/* 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 
  rw)
 
 rw should be named generate_file_write_guid.  If you call
 vhdx_update_header() again later you do not need to update FileWriteGuid
 again according to the spec.  There's probably no harm in doing so but
 the spec explicitly says If this is the first header update within the
 session, use a new value for the FileWriteGuid.
 
 This means that future calls to vhdx_update_header() in the same session
 should set generate_file_write_guid to false.  Therefore rw is not
 the right name.
 

Reading through your comments again made me realize there is a
misunderstanding (a good sign in itself that 'rw' should be renamed).
The 'rw' is not to generate the FileWriteGuid, but rather the
DataWriteGuid.  FileWriteGuid is copied from our session_guid, so it
is only generated once and used through the entire open session.  The
s-session_guid field is generated upon vhdx_open(), for usage if the
file is opened BDRV_O_RDWR.

The DataWriteGuid is generated before any user-visible writes
(anything observable via a virtual disk read).  And that DataWriteGuid
is what the 'rw' field is used for, and the function
vhdx_user_visible_write() handles calling vhdx_update_headers() with
that field set to true.

I'll rename 'rw' to '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 generate before any file write, including
 
 s/generate/generated/
 
  + * headers */
  +memcpy(inactive_header-file_write_guid, s-session_guid,
  +   sizeof(MSGUID));
  +
  +/* a new data guid only needs to be generate before any guest-visible
  + * writes, so update it if the image is opened r/w. */
  +if (rw) {
  +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, 4);
  +vhdx_header_le_export(inactive_header, header_le);
  +bdrv_pwrite_sync(bs-file, header_offset, header_le, 
  sizeof(VHDXHeader));
 
 This function can fail and it's a good indicator that future I/Os will
 also be in trouble.  Please propagate the error return.
 
  @@ -801,8 +955,7 @@ static int vhdx_open(BlockDriverState *bs, QDict 
  *options, int flags)
   }
   
   if (flags  BDRV_O_RDWR) {
  -ret = -ENOTSUP;
  -goto fail;
  +vhdx_update_headers(bs, s, false);
 
 Error handling is missing.  At this point it looks like we cannot write
 to the file.  Propagating the error seems reasonable.



Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-06 Thread Kevin Wolf
Am 05.08.2013 um 17:09 hat Jeff Cody geschrieben:
 On Mon, Aug 05, 2013 at 05:05:36PM +0200, Kevin Wolf wrote:
  Am 01.08.2013 um 05:23 hat Jeff Cody geschrieben:
   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 jc...@redhat.com
  
  I knew I'd forget to mention something I had seen...
  
   +/* overwrite the actual VHDXHeader portion */
   +memcpy(buffer, inactive_header, sizeof(VHDXHeader));
   +inactive_header-checksum = vhdx_update_checksum(buffer,
   + VHDX_HEADER_SIZE, 
   4);
  
  This I would prefer as offsetof(VHDXHeader, checksum) instead of a magic
  number 4.
 
 Who doesn't like a little bit of magic? :)

I think I would have accepted a properly implemented qemu_rand(). :-)

Kevin



Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-05 Thread Kevin Wolf
Am 01.08.2013 um 05:23 hat Jeff Cody geschrieben:
 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 jc...@redhat.com

 diff --git a/configure b/configure
 index 877a821..821b790 100755
 --- a/configure
 +++ b/configure
 @@ -244,6 +244,7 @@ gtk=
  gtkabi=2.0
  tpm=no
  libssh2=
 +vhdx=yes
  
  # parse CC options first
  for opt do
 @@ -950,6 +951,11 @@ for opt do
;;
--enable-libssh2) libssh2=yes
;;
 +  --enable-vhdx) vhdx=yes ;
 + uuid=yes
 +  ;;

What's this? Shouldn't auto-detecting uuid and checking later whether
it's available do the right thing?

Test cases:

1. configure --disable-uuid --enable-vhdx
   This should result in an error because the options contradict each
   other. With this patch, the disable flag is overridden.

2. No libuuid headers available; ./configure
   Should build without VHDX. With this patch, uuid is left out, vhdx is
   configured anyway, the build fails.

Kevin



Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-05 Thread Kevin Wolf
Am 01.08.2013 um 05:23 hat Jeff Cody geschrieben:
 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 jc...@redhat.com
 ---
  block/Makefile.objs |   2 +-
  block/vhdx.c| 157 
 +++-
  block/vhdx.h|  12 +++-
  configure   |  13 +
  4 files changed, 180 insertions(+), 4 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..9b50442 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 + 4));

sizeof(crc) for consistency.

 +
 +memset(buf + crc_offset, 0, sizeof(crc));
 +crc =  crc32c(0x, 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,24 @@ 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).
 + *
 + * We can do this with uuid_generate if uuid.h is present,
 + * however not all systems have uuid and the generation is
 + * pretty straightforward for the DCE + random usage case
 + *
 + */
 +void vhdx_guid_generate(MSGUID *guid)
 +{
 +uuid_t uuid;
 +assert(guid != NULL);
 +
 +uuid_generate(uuid);
 +memcpy(guid, uuid, 16);
 +}

Should MSGUID be QEMU_PACKED to make sure that this works? Also
sizeof(guid) instead of 16?

 +
 +/*
   * 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 +297,107 @@ 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);
 +
 +memcpy(new_h-file_write_guid, orig_h-file_write_guid, 
 sizeof(MSGUID));
 +memcpy(new_h-data_write_guid, orig_h-data_write_guid, 
 sizeof(MSGUID));
 +memcpy(new_h-log_guid,orig_h-log_guid,
 sizeof(MSGUID));

Can we avoid memcpy? I think this should work:

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);
 +

Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-05 Thread Jeff Cody
On Mon, Aug 05, 2013 at 01:42:32PM +0200, Kevin Wolf wrote:
 Am 01.08.2013 um 05:23 hat Jeff Cody geschrieben:
  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 jc...@redhat.com
 
  diff --git a/configure b/configure
  index 877a821..821b790 100755
  --- a/configure
  +++ b/configure
  @@ -244,6 +244,7 @@ gtk=
   gtkabi=2.0
   tpm=no
   libssh2=
  +vhdx=yes
   
   # parse CC options first
   for opt do
  @@ -950,6 +951,11 @@ for opt do
 ;;
 --enable-libssh2) libssh2=yes
 ;;
  +  --enable-vhdx) vhdx=yes ;
  + uuid=yes
  +  ;;
 
 What's this? Shouldn't auto-detecting uuid and checking later whether
 it's available do the right thing?


You are right, thanks.  Will fix for v3.

 Test cases:
 
 1. configure --disable-uuid --enable-vhdx
This should result in an error because the options contradict each
other. With this patch, the disable flag is overridden.
 
 2. No libuuid headers available; ./configure
Should build without VHDX. With this patch, uuid is left out, vhdx is
configured anyway, the build fails.
 
 Kevin



Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-05 Thread Jeff Cody
On Mon, Aug 05, 2013 at 04:45:02PM +0200, Kevin Wolf wrote:
 Am 01.08.2013 um 05:23 hat Jeff Cody geschrieben:
  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 jc...@redhat.com
  ---
   block/Makefile.objs |   2 +-
   block/vhdx.c| 157 
  +++-
   block/vhdx.h|  12 +++-
   configure   |  13 +
   4 files changed, 180 insertions(+), 4 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..9b50442 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 + 4));
 
 sizeof(crc) for consistency.


OK

  +
  +memset(buf + crc_offset, 0, sizeof(crc));
  +crc =  crc32c(0x, 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,24 @@ 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).
  + *
  + * We can do this with uuid_generate if uuid.h is present,
  + * however not all systems have uuid and the generation is
  + * pretty straightforward for the DCE + random usage case
  + *
  + */
  +void vhdx_guid_generate(MSGUID *guid)
  +{
  +uuid_t uuid;
  +assert(guid != NULL);
  +
  +uuid_generate(uuid);
  +memcpy(guid, uuid, 16);
  +}
 
 Should MSGUID be QEMU_PACKED to make sure that this works? Also
 sizeof(guid) instead of 16?


Yikes, yes it definitely should be packed.

  +
  +/*
* 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 +297,107 @@ 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);
  +
  +memcpy(new_h-file_write_guid, orig_h-file_write_guid, 
  sizeof(MSGUID));
  +memcpy(new_h-data_write_guid, orig_h-data_write_guid, 
  sizeof(MSGUID));
  +memcpy(new_h-log_guid,orig_h-log_guid,
  sizeof(MSGUID));
 
 Can we avoid memcpy? I think this should work:
 
 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;
 

Yes, that should work fine as well -  I'll change it to that.

  +
  +

Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-05 Thread Kevin Wolf
Am 01.08.2013 um 05:23 hat Jeff Cody geschrieben:
 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 jc...@redhat.com

I knew I'd forget to mention something I had seen...

 +/* overwrite the actual VHDXHeader portion */
 +memcpy(buffer, inactive_header, sizeof(VHDXHeader));
 +inactive_header-checksum = vhdx_update_checksum(buffer,
 + VHDX_HEADER_SIZE, 4);

This I would prefer as offsetof(VHDXHeader, checksum) instead of a magic
number 4.

Kevin



Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-05 Thread Jeff Cody
On Mon, Aug 05, 2013 at 05:05:36PM +0200, Kevin Wolf wrote:
 Am 01.08.2013 um 05:23 hat Jeff Cody geschrieben:
  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 jc...@redhat.com
 
 I knew I'd forget to mention something I had seen...
 
  +/* overwrite the actual VHDXHeader portion */
  +memcpy(buffer, inactive_header, sizeof(VHDXHeader));
  +inactive_header-checksum = vhdx_update_checksum(buffer,
  + VHDX_HEADER_SIZE, 4);
 
 This I would prefer as offsetof(VHDXHeader, checksum) instead of a magic
 number 4.

Who doesn't like a little bit of magic? :)  OK, I'll use offsetof()
(I'll also look through the rest of my patches for similar occurrences
as well).

Jeff



Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-01 Thread Stefan Hajnoczi
On Wed, Jul 31, 2013 at 11:23:47PM -0400, Jeff Cody wrote:
 @@ -212,6 +242,24 @@ 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).
 + *
 + * We can do this with uuid_generate if uuid.h is present,
 + * however not all systems have uuid and the generation is
 + * pretty straightforward for the DCE + random usage case

The talk of uuid.h not being available confuses me.  The code has no
#ifdef and we do not define uuid_generate() ourselves.

Is this an outdated comment?

 +/* 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 
 rw)

rw should be named generate_file_write_guid.  If you call
vhdx_update_header() again later you do not need to update FileWriteGuid
again according to the spec.  There's probably no harm in doing so but
the spec explicitly says If this is the first header update within the
session, use a new value for the FileWriteGuid.

This means that future calls to vhdx_update_header() in the same session
should set generate_file_write_guid to false.  Therefore rw is not
the right name.

 +{
 +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 generate before any file write, including

s/generate/generated/

 + * headers */
 +memcpy(inactive_header-file_write_guid, s-session_guid,
 +   sizeof(MSGUID));
 +
 +/* a new data guid only needs to be generate before any guest-visible
 + * writes, so update it if the image is opened r/w. */
 +if (rw) {
 +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, 4);
 +vhdx_header_le_export(inactive_header, header_le);
 +bdrv_pwrite_sync(bs-file, header_offset, header_le, 
 sizeof(VHDXHeader));

This function can fail and it's a good indicator that future I/Os will
also be in trouble.  Please propagate the error return.

 @@ -801,8 +955,7 @@ static int vhdx_open(BlockDriverState *bs, QDict 
 *options, int flags)
  }
  
  if (flags  BDRV_O_RDWR) {
 -ret = -ENOTSUP;
 -goto fail;
 +vhdx_update_headers(bs, s, false);

Error handling is missing.  At this point it looks like we cannot write
to the file.  Propagating the error seems reasonable.



Re: [Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-08-01 Thread Jeff Cody
On Thu, Aug 01, 2013 at 03:44:41PM +0200, Stefan Hajnoczi wrote:
 On Wed, Jul 31, 2013 at 11:23:47PM -0400, Jeff Cody wrote:
  @@ -212,6 +242,24 @@ 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).
  + *
  + * We can do this with uuid_generate if uuid.h is present,
  + * however not all systems have uuid and the generation is
  + * pretty straightforward for the DCE + random usage case
 
 The talk of uuid.h not being available confuses me.  The code has no
 #ifdef and we do not define uuid_generate() ourselves.
 
 Is this an outdated comment?

Yes, the comment is outdated from when I had my own UUID generation in
the patch - I missed removing the comment.
 
  +/* 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 
  rw)
 
 rw should be named generate_file_write_guid.  If you call
 vhdx_update_header() again later you do not need to update FileWriteGuid
 again according to the spec.  There's probably no harm in doing so but
 the spec explicitly says If this is the first header update within the
 session, use a new value for the FileWriteGuid.
 
 This means that future calls to vhdx_update_header() in the same session
 should set generate_file_write_guid to false.  Therefore rw is not
 the right name.
 

Yes, it is a bit misnamed.  I'll change the name. However, redundant
generation is protected against with via vhdx_user_visible_write(),
introduced in patch 6, which is the only placed that calls it with rw
= true (soon to be generate_file_write_guid = true).

  +{
  +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 generate before any file write, including
 
 s/generate/generated/
 

Thanks

  + * headers */
  +memcpy(inactive_header-file_write_guid, s-session_guid,
  +   sizeof(MSGUID));
  +
  +/* a new data guid only needs to be generate before any guest-visible
  + * writes, so update it if the image is opened r/w. */
  +if (rw) {
  +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, 4);
  +vhdx_header_le_export(inactive_header, header_le);
  +bdrv_pwrite_sync(bs-file, header_offset, header_le, 
  sizeof(VHDXHeader));
 
 This function can fail and it's a good indicator that future I/Os will
 also be in trouble.  Please propagate the error return.
 

You are right, that was unintentional.

  @@ -801,8 +955,7 @@ static int vhdx_open(BlockDriverState *bs, QDict 
  *options, int flags)
   }
   
   if (flags  BDRV_O_RDWR) {
  -ret = -ENOTSUP;
  -goto fail;
  +vhdx_update_headers(bs, s, false);
 
 Error handling is missing.  At this point it looks like we cannot write
 to the file.  Propagating the error seems reasonable.

OK, I move the above change into when the write support is actually in
place.



[Qemu-devel] [PATCH v2 2/9] block: vhdx - add header update capability.

2013-07-31 Thread Jeff Cody
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 jc...@redhat.com
---
 block/Makefile.objs |   2 +-
 block/vhdx.c| 157 +++-
 block/vhdx.h|  12 +++-
 configure   |  13 +
 4 files changed, 180 insertions(+), 4 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..9b50442 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 + 4));
+
+memset(buf + crc_offset, 0, sizeof(crc));
+crc =  crc32c(0x, 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,24 @@ 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).
+ *
+ * We can do this with uuid_generate if uuid.h is present,
+ * however not all systems have uuid and the generation is
+ * pretty straightforward for the DCE + random usage case
+ *
+ */
+void vhdx_guid_generate(MSGUID *guid)
+{
+uuid_t uuid;
+assert(guid != NULL);
+
+uuid_generate(uuid);
+memcpy(guid, uuid, 16);
+}
+
+/*
  * 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 +297,107 @@ 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);
+
+memcpy(new_h-file_write_guid, orig_h-file_write_guid, sizeof(MSGUID));
+memcpy(new_h-data_write_guid, orig_h-data_write_guid, sizeof(MSGUID));
+memcpy(new_h-log_guid,orig_h-log_guid,sizeof(MSGUID));
+
+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 rw)
+{
+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