yeahuh / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone

Blame SOURCES/kvm-block-vhdx-log-parsing-replay-and-flush-support.patch

9ae3a8
From 457fd5fdb8a089eec09fc21e06ce3099633a248a Mon Sep 17 00:00:00 2001
9ae3a8
From: Jeffrey Cody <jcody@redhat.com>
9ae3a8
Date: Wed, 20 Nov 2013 19:43:57 +0100
9ae3a8
Subject: [PATCH 14/25] block: vhdx - log parsing, replay, and flush support
9ae3a8
9ae3a8
RH-Author: Jeffrey Cody <jcody@redhat.com>
9ae3a8
Message-id: <a64a5d0ec84826a20a9fb23dce611793afd19cd5.1384975172.git.jcody@redhat.com>
9ae3a8
Patchwork-id: 55807
9ae3a8
O-Subject: [RHEL7 qemu-kvm PATCH 14/26] block: vhdx - log parsing, replay, and flush support
9ae3a8
Bugzilla: 879234
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
9ae3a8
This adds support for VHDX v0 logs, as specified in Microsoft's
9ae3a8
VHDX Specification Format v1.00:
9ae3a8
https://www.microsoft.com/en-us/download/details.aspx?id=34750
9ae3a8
9ae3a8
The following support is added:
9ae3a8
9ae3a8
* Log parsing, and validation - validate that an existing log
9ae3a8
  is correct.
9ae3a8
9ae3a8
* Log search - search through an existing log, to find any valid
9ae3a8
  sequence of entries.
9ae3a8
9ae3a8
* Log replay and flush - replay an existing log, and flush/clear
9ae3a8
  the log when complete.
9ae3a8
9ae3a8
The VHDX log is a circular buffer, with elements (sectors) of 4KB.
9ae3a8
9ae3a8
A log entry is a variably-length number of sectors, that is
9ae3a8
comprised of a header and 'descriptors', that describe each sector.
9ae3a8
9ae3a8
A log may contain multiple entries, know as a log sequence.  In a log
9ae3a8
sequence, each log entry immediately follows the previous entry, with an
9ae3a8
incrementing sequence number.  There can only ever be one active and
9ae3a8
valid sequence in the log.
9ae3a8
9ae3a8
Each log entry must match the file log GUID in order to be valid (along
9ae3a8
with other criteria).  Once we have flushed all valid log entries, we
9ae3a8
marked the file log GUID to be zero, which indicates a buffer with no
9ae3a8
valid entries.
9ae3a8
9ae3a8
Signed-off-by: Jeff Cody <jcody@redhat.com>
9ae3a8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
(cherry picked from commit 0a43a1b5d7c33208120eeb2d98ebb9ab15dc2c87)
9ae3a8
---
9ae3a8
 block/Makefile.objs |   2 +-
9ae3a8
 block/vhdx-log.c    | 728 ++++++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/vhdx.c        |  65 +----
9ae3a8
 block/vhdx.h        |   7 +-
9ae3a8
 4 files changed, 749 insertions(+), 53 deletions(-)
9ae3a8
 create mode 100644 block/vhdx-log.c
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/Makefile.objs |    2 +-
9ae3a8
 block/vhdx-log.c    |  728 +++++++++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 block/vhdx.c        |   65 +----
9ae3a8
 block/vhdx.h        |    7 +-
9ae3a8
 4 files changed, 749 insertions(+), 53 deletions(-)
9ae3a8
 create mode 100644 block/vhdx-log.c
9ae3a8
9ae3a8
diff --git a/block/Makefile.objs b/block/Makefile.objs
9ae3a8
index a7b3b87..84dd57f 100644
9ae3a8
--- a/block/Makefile.objs
9ae3a8
+++ b/block/Makefile.objs
9ae3a8
@@ -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
9ae3a8
 block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
9ae3a8
 block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
9ae3a8
 block-obj-y += qed-check.o
9ae3a8
-block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o
9ae3a8
+block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
9ae3a8
 block-obj-y += parallels.o blkdebug.o blkverify.o
9ae3a8
 block-obj-y += snapshot.o qapi.o
9ae3a8
 block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
9ae3a8
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
9ae3a8
new file mode 100644
9ae3a8
index 0000000..0284729
9ae3a8
--- /dev/null
9ae3a8
+++ b/block/vhdx-log.c
9ae3a8
@@ -0,0 +1,728 @@
9ae3a8
+/*
9ae3a8
+ * Block driver for Hyper-V VHDX Images
9ae3a8
+ *
9ae3a8
+ * Copyright (c) 2013 Red Hat, Inc.,
9ae3a8
+ *
9ae3a8
+ * Authors:
9ae3a8
+ *  Jeff Cody <jcody@redhat.com>
9ae3a8
+ *
9ae3a8
+ *  This is based on the "VHDX Format Specification v1.00", published 8/25/2012
9ae3a8
+ *  by Microsoft:
9ae3a8
+ *      https://www.microsoft.com/en-us/download/details.aspx?id=34750
9ae3a8
+ *
9ae3a8
+ * This file covers the functionality of the metadata log writing, parsing, and
9ae3a8
+ * replay.
9ae3a8
+ *
9ae3a8
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
9ae3a8
+ * See the COPYING.LIB file in the top-level directory.
9ae3a8
+ *
9ae3a8
+ */
9ae3a8
+#include "qemu-common.h"
9ae3a8
+#include "block/block_int.h"
9ae3a8
+#include "qemu/module.h"
9ae3a8
+#include "block/vhdx.h"
9ae3a8
+
9ae3a8
+
9ae3a8
+typedef struct VHDXLogSequence {
9ae3a8
+    bool valid;
9ae3a8
+    uint32_t count;
9ae3a8
+    VHDXLogEntries log;
9ae3a8
+    VHDXLogEntryHeader hdr;
9ae3a8
+} VHDXLogSequence;
9ae3a8
+
9ae3a8
+typedef struct VHDXLogDescEntries {
9ae3a8
+    VHDXLogEntryHeader hdr;
9ae3a8
+    VHDXLogDescriptor desc[];
9ae3a8
+} VHDXLogDescEntries;
9ae3a8
+
9ae3a8
+static const MSGUID zero_guid = { 0 };
9ae3a8
+
9ae3a8
+/* The log located on the disk is circular buffer containing
9ae3a8
+ * sectors of 4096 bytes each.
9ae3a8
+ *
9ae3a8
+ * It is assumed for the read/write functions below that the
9ae3a8
+ * circular buffer scheme uses a 'one sector open' to indicate
9ae3a8
+ * the buffer is full.  Given the validation methods used for each
9ae3a8
+ * sector, this method should be compatible with other methods that
9ae3a8
+ * do not waste a sector.
9ae3a8
+ */
9ae3a8
+
9ae3a8
+
9ae3a8
+/* Allow peeking at the hdr entry at the beginning of the current
9ae3a8
+ * read index, without advancing the read index */
9ae3a8
+static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log,
9ae3a8
+                             VHDXLogEntryHeader *hdr)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint64_t offset;
9ae3a8
+    uint32_t read;
9ae3a8
+
9ae3a8
+    assert(hdr != NULL);
9ae3a8
+
9ae3a8
+    /* peek is only supported on sector boundaries */
9ae3a8
+    if (log->read % VHDX_LOG_SECTOR_SIZE) {
9ae3a8
+        ret = -EFAULT;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    read = log->read;
9ae3a8
+    /* we are guaranteed that a) log sectors are 4096 bytes,
9ae3a8
+     * and b) the log length is a multiple of 1MB. So, there
9ae3a8
+     * is always a round number of sectors in the buffer */
9ae3a8
+    if ((read + sizeof(VHDXLogEntryHeader)) > log->length) {
9ae3a8
+        read = 0;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (read == log->write) {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    offset = log->offset + read;
9ae3a8
+
9ae3a8
+    ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader));
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Index increment for log, based on sector boundaries */
9ae3a8
+static int vhdx_log_inc_idx(uint32_t idx, uint64_t length)
9ae3a8
+{
9ae3a8
+    idx += VHDX_LOG_SECTOR_SIZE;
9ae3a8
+    /* we are guaranteed that a) log sectors are 4096 bytes,
9ae3a8
+     * and b) the log length is a multiple of 1MB. So, there
9ae3a8
+     * is always a round number of sectors in the buffer */
9ae3a8
+    return idx >= length ? 0 : idx;
9ae3a8
+}
9ae3a8
+
9ae3a8
+
9ae3a8
+/* Reset the log to empty */
9ae3a8
+static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s)
9ae3a8
+{
9ae3a8
+    MSGUID guid = { 0 };
9ae3a8
+    s->log.read = s->log.write = 0;
9ae3a8
+    /* a log guid of 0 indicates an empty log to any parser of v0
9ae3a8
+     * VHDX logs */
9ae3a8
+    vhdx_update_headers(bs, s, false, &guid);
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Reads num_sectors from the log (all log sectors are 4096 bytes),
9ae3a8
+ * into buffer 'buffer'.  Upon return, *sectors_read will contain
9ae3a8
+ * the number of sectors successfully read.
9ae3a8
+ *
9ae3a8
+ * It is assumed that 'buffer' is already allocated, and of sufficient
9ae3a8
+ * size (i.e. >= 4096*num_sectors).
9ae3a8
+ *
9ae3a8
+ * If 'peek' is true, then the tail (read) pointer for the circular buffer is
9ae3a8
+ * not modified.
9ae3a8
+ *
9ae3a8
+ * 0 is returned on success, -errno otherwise.  */
9ae3a8
+static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log,
9ae3a8
+                                 uint32_t *sectors_read, void *buffer,
9ae3a8
+                                 uint32_t num_sectors, bool peek)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint64_t offset;
9ae3a8
+    uint32_t read;
9ae3a8
+
9ae3a8
+    read = log->read;
9ae3a8
+
9ae3a8
+    *sectors_read = 0;
9ae3a8
+    while (num_sectors) {
9ae3a8
+        if (read == log->write) {
9ae3a8
+            /* empty */
9ae3a8
+            break;
9ae3a8
+        }
9ae3a8
+        offset = log->offset + read;
9ae3a8
+
9ae3a8
+        ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+        read = vhdx_log_inc_idx(read, log->length);
9ae3a8
+
9ae3a8
+        *sectors_read = *sectors_read + 1;
9ae3a8
+        num_sectors--;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    if (!peek) {
9ae3a8
+        log->read = read;
9ae3a8
+    }
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Validates a log entry header */
9ae3a8
+static bool vhdx_log_hdr_is_valid(VHDXLogEntries *log, VHDXLogEntryHeader *hdr,
9ae3a8
+                                  BDRVVHDXState *s)
9ae3a8
+{
9ae3a8
+    int valid = false;
9ae3a8
+
9ae3a8
+    if (memcmp(&hdr->signature, "loge", 4)) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* if the individual entry length is larger than the whole log
9ae3a8
+     * buffer, that is obviously invalid */
9ae3a8
+    if (log->length < hdr->entry_length) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* length of entire entry must be in units of 4KB (log sector size) */
9ae3a8
+    if (hdr->entry_length % (VHDX_LOG_SECTOR_SIZE)) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* per spec, sequence # must be > 0 */
9ae3a8
+    if (hdr->sequence_number == 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* log entries are only valid if they match the file-wide log guid
9ae3a8
+     * found in the active header */
9ae3a8
+    if (!guid_eq(hdr->log_guid, s->headers[s->curr_header]->log_guid)) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (hdr->descriptor_count * sizeof(VHDXLogDescriptor) > hdr->entry_length) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    valid = true;
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    return valid;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/*
9ae3a8
+ * Given a log header, this will validate that the descriptors and the
9ae3a8
+ * corresponding data sectors (if applicable)
9ae3a8
+ *
9ae3a8
+ * Validation consists of:
9ae3a8
+ *      1. Making sure the sequence numbers matches the entry header
9ae3a8
+ *      2. Verifying a valid signature ('zero' or 'desc' for descriptors)
9ae3a8
+ *      3. File offset field is a multiple of 4KB
9ae3a8
+ *      4. If a data descriptor, the corresponding data sector
9ae3a8
+ *         has its signature ('data') and matching sequence number
9ae3a8
+ *
9ae3a8
+ * @desc: the data buffer containing the descriptor
9ae3a8
+ * @hdr:  the log entry header
9ae3a8
+ *
9ae3a8
+ * Returns true if valid
9ae3a8
+ */
9ae3a8
+static bool vhdx_log_desc_is_valid(VHDXLogDescriptor *desc,
9ae3a8
+                                   VHDXLogEntryHeader *hdr)
9ae3a8
+{
9ae3a8
+    bool ret = false;
9ae3a8
+
9ae3a8
+    if (desc->sequence_number != hdr->sequence_number) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+    if (desc->file_offset % VHDX_LOG_SECTOR_SIZE) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (!memcmp(&desc->signature, "zero", 4)) {
9ae3a8
+        if (desc->zero_length % VHDX_LOG_SECTOR_SIZE == 0) {
9ae3a8
+            /* valid */
9ae3a8
+            ret = true;
9ae3a8
+        }
9ae3a8
+    } else if (!memcmp(&desc->signature, "desc", 4)) {
9ae3a8
+            /* valid */
9ae3a8
+            ret = true;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+
9ae3a8
+/* Prior to sector data for a log entry, there is the header
9ae3a8
+ * and the descriptors referenced in the header:
9ae3a8
+ *
9ae3a8
+ * [] = 4KB sector
9ae3a8
+ *
9ae3a8
+ * [ hdr, desc ][   desc   ][ ... ][ data ][ ... ]
9ae3a8
+ *
9ae3a8
+ * The first sector in a log entry has a 64 byte header, and
9ae3a8
+ * up to 126 32-byte descriptors.  If more descriptors than
9ae3a8
+ * 126 are required, then subsequent sectors can have up to 128
9ae3a8
+ * descriptors.  Each sector is 4KB.  Data follows the descriptor
9ae3a8
+ * sectors.
9ae3a8
+ *
9ae3a8
+ * This will return the number of sectors needed to encompass
9ae3a8
+ * the passed number of descriptors in desc_cnt.
9ae3a8
+ *
9ae3a8
+ * This will never return 0, even if desc_cnt is 0.
9ae3a8
+ */
9ae3a8
+static int vhdx_compute_desc_sectors(uint32_t desc_cnt)
9ae3a8
+{
9ae3a8
+    uint32_t desc_sectors;
9ae3a8
+
9ae3a8
+    desc_cnt += 2; /* account for header in first sector */
9ae3a8
+    desc_sectors = desc_cnt / 128;
9ae3a8
+    if (desc_cnt % 128) {
9ae3a8
+        desc_sectors++;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    return desc_sectors;
9ae3a8
+}
9ae3a8
+
9ae3a8
+
9ae3a8
+/* Reads the log header, and subsequent descriptors (if any).  This
9ae3a8
+ * will allocate all the space for buffer, which must be NULL when
9ae3a8
+ * passed into this function. Each descriptor will also be validated,
9ae3a8
+ * and error returned if any are invalid. */
9ae3a8
+static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s,
9ae3a8
+                              VHDXLogEntries *log, VHDXLogDescEntries **buffer)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint32_t desc_sectors;
9ae3a8
+    uint32_t sectors_read;
9ae3a8
+    VHDXLogEntryHeader hdr;
9ae3a8
+    VHDXLogDescEntries *desc_entries = NULL;
9ae3a8
+    int i;
9ae3a8
+
9ae3a8
+    assert(*buffer == NULL);
9ae3a8
+
9ae3a8
+    ret = vhdx_log_peek_hdr(bs, log, &hdr);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+    vhdx_log_entry_hdr_le_import(&hdr);
9ae3a8
+    if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count);
9ae3a8
+    desc_entries = qemu_blockalign(bs, desc_sectors * VHDX_LOG_SECTOR_SIZE);
9ae3a8
+
9ae3a8
+    ret = vhdx_log_read_sectors(bs, log, &sectors_read, desc_entries,
9ae3a8
+                                desc_sectors, false);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto free_and_exit;
9ae3a8
+    }
9ae3a8
+    if (sectors_read != desc_sectors) {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto free_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* put in proper endianness, and validate each desc */
9ae3a8
+    for (i = 0; i < hdr.descriptor_count; i++) {
9ae3a8
+        vhdx_log_desc_le_import(&desc_entries->desc[i]);
9ae3a8
+        if (vhdx_log_desc_is_valid(&desc_entries->desc[i], &hdr) == false) {
9ae3a8
+            ret = -EINVAL;
9ae3a8
+            goto free_and_exit;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    *buffer = desc_entries;
9ae3a8
+    goto exit;
9ae3a8
+
9ae3a8
+free_and_exit:
9ae3a8
+    qemu_vfree(desc_entries);
9ae3a8
+exit:
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+
9ae3a8
+/* Flushes the descriptor described by desc to the VHDX image file.
9ae3a8
+ * If the descriptor is a data descriptor, than 'data' must be non-NULL,
9ae3a8
+ * and >= 4096 bytes (VHDX_LOG_SECTOR_SIZE), containing the data to be
9ae3a8
+ * written.
9ae3a8
+ *
9ae3a8
+ * Verification is performed to make sure the sequence numbers of a data
9ae3a8
+ * descriptor match the sequence number in the desc.
9ae3a8
+ *
9ae3a8
+ * For a zero descriptor, it may describe multiple sectors to fill with zeroes.
9ae3a8
+ * In this case, it should be noted that zeroes are written to disk, and the
9ae3a8
+ * image file is not extended as a sparse file.  */
9ae3a8
+static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc,
9ae3a8
+                               VHDXLogDataSector *data)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint64_t seq, file_offset;
9ae3a8
+    uint32_t offset = 0;
9ae3a8
+    void *buffer = NULL;
9ae3a8
+    uint64_t count = 1;
9ae3a8
+    int i;
9ae3a8
+
9ae3a8
+    buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
9ae3a8
+
9ae3a8
+    if (!memcmp(&desc->signature, "desc", 4)) {
9ae3a8
+        /* data sector */
9ae3a8
+        if (data == NULL) {
9ae3a8
+            ret = -EFAULT;
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        /* The sequence number of the data sector must match that
9ae3a8
+         * in the descriptor */
9ae3a8
+        seq = data->sequence_high;
9ae3a8
+        seq <<= 32;
9ae3a8
+        seq |= data->sequence_low & 0xffffffff;
9ae3a8
+
9ae3a8
+        if (seq != desc->sequence_number) {
9ae3a8
+            ret = -EINVAL;
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        /* Each data sector is in total 4096 bytes, however the first
9ae3a8
+         * 8 bytes, and last 4 bytes, are located in the descriptor */
9ae3a8
+        memcpy(buffer, &desc->leading_bytes, 8);
9ae3a8
+        offset += 8;
9ae3a8
+
9ae3a8
+        memcpy(buffer+offset, data->data, 4084);
9ae3a8
+        offset += 4084;
9ae3a8
+
9ae3a8
+        memcpy(buffer+offset, &desc->trailing_bytes, 4);
9ae3a8
+
9ae3a8
+    } else if (!memcmp(&desc->signature, "zero", 4)) {
9ae3a8
+        /* write 'count' sectors of sector */
9ae3a8
+        memset(buffer, 0, VHDX_LOG_SECTOR_SIZE);
9ae3a8
+        count = desc->zero_length / VHDX_LOG_SECTOR_SIZE;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    file_offset = desc->file_offset;
9ae3a8
+
9ae3a8
+    /* count is only > 1 if we are writing zeroes */
9ae3a8
+    for (i = 0; i < count; i++) {
9ae3a8
+        ret = bdrv_pwrite_sync(bs->file, file_offset, buffer,
9ae3a8
+                               VHDX_LOG_SECTOR_SIZE);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+        file_offset += VHDX_LOG_SECTOR_SIZE;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    qemu_vfree(buffer);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Flush the entire log (as described by 'logs') to the VHDX image
9ae3a8
+ * file, and then set the log to 'empty' status once complete.
9ae3a8
+ *
9ae3a8
+ * The log entries should be validate prior to flushing */
9ae3a8
+static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
9ae3a8
+                          VHDXLogSequence *logs)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    int i;
9ae3a8
+    uint32_t cnt, sectors_read;
9ae3a8
+    uint64_t new_file_size;
9ae3a8
+    void *data = NULL;
9ae3a8
+    VHDXLogDescEntries *desc_entries = NULL;
9ae3a8
+    VHDXLogEntryHeader hdr_tmp = { 0 };
9ae3a8
+
9ae3a8
+    cnt = logs->count;
9ae3a8
+
9ae3a8
+    data = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
9ae3a8
+
9ae3a8
+    ret = vhdx_user_visible_write(bs, s);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* each iteration represents one log sequence, which may span multiple
9ae3a8
+     * sectors */
9ae3a8
+    while (cnt--) {
9ae3a8
+        ret = vhdx_log_peek_hdr(bs, &logs->log, &hdr_tmp);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+        /* if the log shows a FlushedFileOffset larger than our current file
9ae3a8
+         * size, then that means the file has been truncated / corrupted, and
9ae3a8
+         * we must refused to open it / use it */
9ae3a8
+        if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file)) {
9ae3a8
+            ret = -EINVAL;
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        for (i = 0; i < desc_entries->hdr.descriptor_count; i++) {
9ae3a8
+            if (!memcmp(&desc_entries->desc[i].signature, "desc", 4)) {
9ae3a8
+                /* data sector, so read a sector to flush */
9ae3a8
+                ret = vhdx_log_read_sectors(bs, &logs->log, &sectors_read,
9ae3a8
+                                            data, 1, false);
9ae3a8
+                if (ret < 0) {
9ae3a8
+                    goto exit;
9ae3a8
+                }
9ae3a8
+                if (sectors_read != 1) {
9ae3a8
+                    ret = -EINVAL;
9ae3a8
+                    goto exit;
9ae3a8
+                }
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            ret = vhdx_log_flush_desc(bs, &desc_entries->desc[i], data);
9ae3a8
+            if (ret < 0) {
9ae3a8
+                goto exit;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+        if (bdrv_getlength(bs->file) < desc_entries->hdr.last_file_offset) {
9ae3a8
+            new_file_size = desc_entries->hdr.last_file_offset;
9ae3a8
+            if (new_file_size % (1024*1024)) {
9ae3a8
+                /* round up to nearest 1MB boundary */
9ae3a8
+                new_file_size = ((new_file_size >> 20) + 1) << 20;
9ae3a8
+                bdrv_truncate(bs->file, new_file_size);
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+        qemu_vfree(desc_entries);
9ae3a8
+        desc_entries = NULL;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    bdrv_flush(bs);
9ae3a8
+    /* once the log is fully flushed, indicate that we have an empty log
9ae3a8
+     * now.  This also sets the log guid to 0, to indicate an empty log */
9ae3a8
+    vhdx_log_reset(bs, s);
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    qemu_vfree(data);
9ae3a8
+    qemu_vfree(desc_entries);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s,
9ae3a8
+                                   VHDXLogEntries *log, uint64_t seq,
9ae3a8
+                                   bool *valid, VHDXLogEntryHeader *entry)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    VHDXLogEntryHeader hdr;
9ae3a8
+    void *buffer = NULL;
9ae3a8
+    uint32_t i, desc_sectors, total_sectors, crc;
9ae3a8
+    uint32_t sectors_read = 0;
9ae3a8
+    VHDXLogDescEntries *desc_buffer = NULL;
9ae3a8
+
9ae3a8
+    *valid = false;
9ae3a8
+
9ae3a8
+    ret = vhdx_log_peek_hdr(bs, log, &hdr);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto inc_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    vhdx_log_entry_hdr_le_import(&hdr);
9ae3a8
+
9ae3a8
+
9ae3a8
+    if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) {
9ae3a8
+        goto inc_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (seq > 0) {
9ae3a8
+        if (hdr.sequence_number != seq + 1) {
9ae3a8
+            goto inc_and_exit;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count);
9ae3a8
+
9ae3a8
+    /* Read desc sectors, and calculate log checksum */
9ae3a8
+
9ae3a8
+    total_sectors = hdr.entry_length / VHDX_LOG_SECTOR_SIZE;
9ae3a8
+
9ae3a8
+
9ae3a8
+    /* read_desc() will incrememnt the read idx */
9ae3a8
+    ret = vhdx_log_read_desc(bs, s, log, &desc_buffer);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto free_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    crc = vhdx_checksum_calc(0xffffffff, (void *)desc_buffer,
9ae3a8
+                            desc_sectors * VHDX_LOG_SECTOR_SIZE, 4);
9ae3a8
+    crc ^= 0xffffffff;
9ae3a8
+
9ae3a8
+    buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
9ae3a8
+    if (total_sectors > desc_sectors) {
9ae3a8
+        for (i = 0; i < total_sectors - desc_sectors; i++) {
9ae3a8
+            sectors_read = 0;
9ae3a8
+            ret = vhdx_log_read_sectors(bs, log, &sectors_read, buffer,
9ae3a8
+                                        1, false);
9ae3a8
+            if (ret < 0 || sectors_read != 1) {
9ae3a8
+                goto free_and_exit;
9ae3a8
+            }
9ae3a8
+            crc = vhdx_checksum_calc(crc, buffer, VHDX_LOG_SECTOR_SIZE, -1);
9ae3a8
+            crc ^= 0xffffffff;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+    crc ^= 0xffffffff;
9ae3a8
+    if (crc != desc_buffer->hdr.checksum) {
9ae3a8
+        goto free_and_exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    *valid = true;
9ae3a8
+    *entry = hdr;
9ae3a8
+    goto free_and_exit;
9ae3a8
+
9ae3a8
+inc_and_exit:
9ae3a8
+    log->read = vhdx_log_inc_idx(log->read, log->length);
9ae3a8
+
9ae3a8
+free_and_exit:
9ae3a8
+    qemu_vfree(buffer);
9ae3a8
+    qemu_vfree(desc_buffer);
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Search through the log circular buffer, and find the valid, active
9ae3a8
+ * log sequence, if any exists
9ae3a8
+ * */
9ae3a8
+static int vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s,
9ae3a8
+                           VHDXLogSequence *logs)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    uint32_t tail;
9ae3a8
+    bool seq_valid = false;
9ae3a8
+    VHDXLogSequence candidate = { 0 };
9ae3a8
+    VHDXLogEntryHeader hdr = { 0 };
9ae3a8
+    VHDXLogEntries curr_log;
9ae3a8
+
9ae3a8
+    memcpy(&curr_log, &s->log, sizeof(VHDXLogEntries));
9ae3a8
+    curr_log.write = curr_log.length;   /* assume log is full */
9ae3a8
+    curr_log.read = 0;
9ae3a8
+
9ae3a8
+
9ae3a8
+    /* now we will go through the whole log sector by sector, until
9ae3a8
+     * we find a valid, active log sequence, or reach the end of the
9ae3a8
+     * log buffer */
9ae3a8
+    for (;;) {
9ae3a8
+        uint64_t curr_seq = 0;
9ae3a8
+        VHDXLogSequence current = { 0 };
9ae3a8
+
9ae3a8
+        tail = curr_log.read;
9ae3a8
+
9ae3a8
+        ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq,
9ae3a8
+                                      &seq_valid, &hdr);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (seq_valid) {
9ae3a8
+            current.valid     = true;
9ae3a8
+            current.log       = curr_log;
9ae3a8
+            current.log.read  = tail;
9ae3a8
+            current.log.write = curr_log.read;
9ae3a8
+            current.count     = 1;
9ae3a8
+            current.hdr       = hdr;
9ae3a8
+
9ae3a8
+
9ae3a8
+            for (;;) {
9ae3a8
+                ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq,
9ae3a8
+                                              &seq_valid, &hdr);
9ae3a8
+                if (ret < 0) {
9ae3a8
+                    goto exit;
9ae3a8
+                }
9ae3a8
+                if (seq_valid == false) {
9ae3a8
+                    break;
9ae3a8
+                }
9ae3a8
+                current.log.write = curr_log.read;
9ae3a8
+                current.count++;
9ae3a8
+
9ae3a8
+                curr_seq = hdr.sequence_number;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (current.valid) {
9ae3a8
+            if (candidate.valid == false ||
9ae3a8
+                current.hdr.sequence_number > candidate.hdr.sequence_number) {
9ae3a8
+                candidate = current;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (curr_log.read < tail) {
9ae3a8
+            break;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    *logs = candidate;
9ae3a8
+
9ae3a8
+    if (candidate.valid) {
9ae3a8
+        /* this is the next sequence number, for writes */
9ae3a8
+        s->log.sequence = candidate.hdr.sequence_number + 1;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+/* Parse the replay log.  Per the VHDX spec, if the log is present
9ae3a8
+ * it must be replayed prior to opening the file, even read-only.
9ae3a8
+ *
9ae3a8
+ * If read-only, we must replay the log in RAM (or refuse to open
9ae3a8
+ * a dirty VHDX file read-only) */
9ae3a8
+int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed)
9ae3a8
+{
9ae3a8
+    int ret = 0;
9ae3a8
+    VHDXHeader *hdr;
9ae3a8
+    VHDXLogSequence logs = { 0 };
9ae3a8
+
9ae3a8
+    hdr = s->headers[s->curr_header];
9ae3a8
+
9ae3a8
+    *flushed = false;
9ae3a8
+
9ae3a8
+    /* s->log.hdr is freed in vhdx_close() */
9ae3a8
+    if (s->log.hdr == NULL) {
9ae3a8
+        s->log.hdr = qemu_blockalign(bs, sizeof(VHDXLogEntryHeader));
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    s->log.offset = hdr->log_offset;
9ae3a8
+    s->log.length = hdr->log_length;
9ae3a8
+
9ae3a8
+    if (s->log.offset < VHDX_LOG_MIN_SIZE ||
9ae3a8
+        s->log.offset % VHDX_LOG_MIN_SIZE) {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* per spec, only log version of 0 is supported */
9ae3a8
+    if (hdr->log_version != 0) {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* If either the log guid, or log length is zero,
9ae3a8
+     * then a replay log is not present */
9ae3a8
+    if (guid_eq(hdr->log_guid, zero_guid)) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (hdr->log_length == 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (hdr->log_length % VHDX_LOG_MIN_SIZE) {
9ae3a8
+        ret = -EINVAL;
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+    /* The log is present, we need to find if and where there is an active
9ae3a8
+     * sequence of valid entries present in the log.  */
9ae3a8
+
9ae3a8
+    ret = vhdx_log_search(bs, s, &logs);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto exit;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (logs.valid) {
9ae3a8
+        /* now flush the log */
9ae3a8
+        ret = vhdx_log_flush(bs, s, &logs);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto exit;
9ae3a8
+        }
9ae3a8
+        *flushed = true;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
+exit:
9ae3a8
+    return ret;
9ae3a8
+}
9ae3a8
+
9ae3a8
+
9ae3a8
diff --git a/block/vhdx.c b/block/vhdx.c
9ae3a8
index 7d697e4..b552dde 100644
9ae3a8
--- a/block/vhdx.c
9ae3a8
+++ b/block/vhdx.c
9ae3a8
@@ -735,58 +735,22 @@ exit:
9ae3a8
     return ret;
9ae3a8
 }
9ae3a8
 
9ae3a8
-/* Parse the replay log.  Per the VHDX spec, if the log is present
9ae3a8
- * it must be replayed prior to opening the file, even read-only.
9ae3a8
- *
9ae3a8
- * If read-only, we must replay the log in RAM (or refuse to open
9ae3a8
- * a dirty VHDX file read-only */
9ae3a8
-static int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s)
9ae3a8
-{
9ae3a8
-    int ret = 0;
9ae3a8
-    int i;
9ae3a8
-    VHDXHeader *hdr;
9ae3a8
-
9ae3a8
-    hdr = s->headers[s->curr_header];
9ae3a8
-
9ae3a8
-    /* either the log guid, or log length is zero,
9ae3a8
-     * then a replay log is present */
9ae3a8
-    for (i = 0; i < sizeof(hdr->log_guid.data4); i++) {
9ae3a8
-        ret |= hdr->log_guid.data4[i];
9ae3a8
-    }
9ae3a8
-    if (hdr->log_guid.data1 == 0 &&
9ae3a8
-        hdr->log_guid.data2 == 0 &&
9ae3a8
-        hdr->log_guid.data3 == 0 &&
9ae3a8
-        ret == 0) {
9ae3a8
-        goto exit;
9ae3a8
-    }
9ae3a8
-
9ae3a8
-    /* per spec, only log version of 0 is supported */
9ae3a8
-    if (hdr->log_version != 0) {
9ae3a8
-        ret = -EINVAL;
9ae3a8
-        goto exit;
9ae3a8
-    }
9ae3a8
-
9ae3a8
-    if (hdr->log_length == 0) {
9ae3a8
-        goto exit;
9ae3a8
-    }
9ae3a8
-
9ae3a8
-    /* We currently do not support images with logs to replay */
9ae3a8
-    ret = -ENOTSUP;
9ae3a8
-
9ae3a8
-exit:
9ae3a8
-    return ret;
9ae3a8
-}
9ae3a8
-
9ae3a8
 
9ae3a8
 static void vhdx_close(BlockDriverState *bs)
9ae3a8
 {
9ae3a8
     BDRVVHDXState *s = bs->opaque;
9ae3a8
     qemu_vfree(s->headers[0]);
9ae3a8
+    s->headers[0] = NULL;
9ae3a8
     qemu_vfree(s->headers[1]);
9ae3a8
+    s->headers[1] = NULL;
9ae3a8
     qemu_vfree(s->bat);
9ae3a8
+    s->bat = NULL;
9ae3a8
     qemu_vfree(s->parent_entries);
9ae3a8
+    s->parent_entries = NULL;
9ae3a8
     migrate_del_blocker(s->migration_blocker);
9ae3a8
     error_free(s->migration_blocker);
9ae3a8
+    qemu_vfree(s->log.hdr);
9ae3a8
+    s->log.hdr = NULL;
9ae3a8
 }
9ae3a8
 
9ae3a8
 static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
@@ -797,6 +761,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
     uint32_t i;
9ae3a8
     uint64_t signature;
9ae3a8
     uint32_t data_blocks_cnt, bitmap_blocks_cnt;
9ae3a8
+    bool log_flushed = false;
9ae3a8
 
9ae3a8
 
9ae3a8
     s->bat = NULL;
9ae3a8
@@ -820,24 +785,25 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
     vhdx_guid_generate(&s->session_guid);
9ae3a8
 
9ae3a8
     ret = vhdx_parse_header(bs, s);
9ae3a8
-    if (ret) {
9ae3a8
+    if (ret < 0) {
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
 
9ae3a8
-    ret = vhdx_parse_log(bs, s);
9ae3a8
-    if (ret) {
9ae3a8
+    ret = vhdx_parse_log(bs, s, &log_flushed);
9ae3a8
+    if (ret < 0) {
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
 
9ae3a8
     ret = vhdx_open_region_tables(bs, s);
9ae3a8
-    if (ret) {
9ae3a8
+    if (ret < 0) {
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
 
9ae3a8
     ret = vhdx_parse_metadata(bs, s);
9ae3a8
-    if (ret) {
9ae3a8
+    if (ret < 0) {
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
+
9ae3a8
     s->block_size = s->params.block_size;
9ae3a8
 
9ae3a8
     /* the VHDX spec dictates that virtual_disk_size is always a multiple of
9ae3a8
@@ -897,10 +863,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
 
9ae3a8
     return 0;
9ae3a8
 fail:
9ae3a8
-    qemu_vfree(s->headers[0]);
9ae3a8
-    qemu_vfree(s->headers[1]);
9ae3a8
-    qemu_vfree(s->bat);
9ae3a8
-    qemu_vfree(s->parent_entries);
9ae3a8
+    vhdx_close(bs);
9ae3a8
     return ret;
9ae3a8
 }
9ae3a8
 
9ae3a8
diff --git a/block/vhdx.h b/block/vhdx.h
9ae3a8
index 81785e5..b150ad1 100644
9ae3a8
--- a/block/vhdx.h
9ae3a8
+++ b/block/vhdx.h
9ae3a8
@@ -326,7 +326,11 @@ typedef struct VHDXMetadataEntries {
9ae3a8
 typedef struct VHDXLogEntries {
9ae3a8
     uint64_t offset;
9ae3a8
     uint64_t length;
9ae3a8
-    uint32_t head;
9ae3a8
+    uint32_t write;
9ae3a8
+    uint32_t read;
9ae3a8
+    VHDXLogEntryHeader *hdr;
9ae3a8
+    void *desc_buffer;
9ae3a8
+    uint64_t sequence;
9ae3a8
     uint32_t tail;
9ae3a8
 } VHDXLogEntries;
9ae3a8
 
9ae3a8
@@ -383,6 +387,7 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
9ae3a8
 
9ae3a8
 bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset);
9ae3a8
 
9ae3a8
+int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed);
9ae3a8
 
9ae3a8
 static inline void leguid_to_cpus(MSGUID *guid)
9ae3a8
 {
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8