thebeanogamer / rpms / qemu-kvm

Forked from rpms/qemu-kvm 5 months ago
Clone

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

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