提交 deb0f500 编写于 作者: A Anthony Liguori

Merge remote-tracking branch 'stefanha/block' into staging

# By Jeff Cody (26) and others
# Via Stefan Hajnoczi
* stefanha/block: (37 commits)
  block: Round up total_sectors
  block: vhdx qemu-iotest - log replay of data sector
  block: qemu-iotests for vhdx, add write test support
  block: vhdx - update _make_test_img() to filter out vhdx options
  block: vhdx - add .bdrv_create() support
  block: vhdx - fix comment typos in header, fix incorrect struct fields
  block: vhdx - break out code operations to functions
  block: vhdx - move more endian translations to vhdx-endian.c
  block: vhdx - remove BAT file offset bit shifting
  block: vhdx write support
  block: vhdx - add log write support
  block: vhdx - add region overlap detection for image files
  block: vhdx - log parsing, replay, and flush support
  block: vhdx code movement - move vhdx_close() above vhdx_open()
  block: vhdx - update log guid in header, and first write tracker
  block: vhdx - break endian translation functions out
  block: vhdx - log support struct and defines
  block: vhdx code movement - VHDXMetadataEntries and BDRVVHDXState to header.
  block: vhdx - add header update capability.
  block: vhdx - minor comments and typo correction.
  ...

Message-id: 1383905551-16411-1-git-send-email-stefanha@redhat.com
Signed-off-by: NAnthony Liguori <aliguori@amazon.com>
...@@ -640,7 +640,7 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) ...@@ -640,7 +640,7 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
if (length < 0) { if (length < 0) {
return length; return length;
} }
hint = length >> BDRV_SECTOR_BITS; hint = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE);
} }
bs->total_sectors = hint; bs->total_sectors = hint;
...@@ -1084,8 +1084,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, ...@@ -1084,8 +1084,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
snprintf(backing_filename, sizeof(backing_filename), snprintf(backing_filename, sizeof(backing_filename),
"%s", filename); "%s", filename);
} else if (!realpath(filename, backing_filename)) { } else if (!realpath(filename, backing_filename)) {
error_setg_errno(errp, errno, "Could not resolve path '%s'", filename);
ret = -errno; ret = -errno;
error_setg_errno(errp, errno, "Could not resolve path '%s'", filename);
goto fail; goto fail;
} }
......
...@@ -2,7 +2,7 @@ block-obj-y += raw_bsd.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o v ...@@ -2,7 +2,7 @@ block-obj-y += raw_bsd.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o v
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o 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.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-y += qed-check.o block-obj-y += qed-check.o
block-obj-y += vhdx.o block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
block-obj-y += parallels.o blkdebug.o blkverify.o block-obj-y += parallels.o blkdebug.o blkverify.o
block-obj-y += snapshot.o qapi.o block-obj-y += snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
......
...@@ -1842,7 +1842,8 @@ static BlockDriver bdrv_host_cdrom = { ...@@ -1842,7 +1842,8 @@ static BlockDriver bdrv_host_cdrom = {
#endif /* __linux__ */ #endif /* __linux__ */
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
static int cdrom_open(BlockDriverState *bs, QDict *options, int flags) static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
Error *local_err = NULL; Error *local_err = NULL;
......
/*
* Block driver for Hyper-V VHDX Images
*
* Copyright (c) 2013 Red Hat, Inc.,
*
* Authors:
* Jeff Cody <jcody@redhat.com>
*
* This is based on the "VHDX Format Specification v1.00", published 8/25/2012
* by Microsoft:
* https://www.microsoft.com/en-us/download/details.aspx?id=34750
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu-common.h"
#include "block/block_int.h"
#include "block/vhdx.h"
#include <uuid/uuid.h>
/*
* All the VHDX formats on disk are little endian - the following
* are helper import/export functions to correctly convert
* endianness from disk read to native cpu format, and back again.
*/
/* VHDX File Header */
void vhdx_header_le_import(VHDXHeader *h)
{
assert(h != NULL);
le32_to_cpus(&h->signature);
le32_to_cpus(&h->checksum);
le64_to_cpus(&h->sequence_number);
leguid_to_cpus(&h->file_write_guid);
leguid_to_cpus(&h->data_write_guid);
leguid_to_cpus(&h->log_guid);
le16_to_cpus(&h->log_version);
le16_to_cpus(&h->version);
le32_to_cpus(&h->log_length);
le64_to_cpus(&h->log_offset);
}
void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
{
assert(orig_h != NULL);
assert(new_h != NULL);
new_h->signature = cpu_to_le32(orig_h->signature);
new_h->checksum = cpu_to_le32(orig_h->checksum);
new_h->sequence_number = cpu_to_le64(orig_h->sequence_number);
new_h->file_write_guid = orig_h->file_write_guid;
new_h->data_write_guid = orig_h->data_write_guid;
new_h->log_guid = orig_h->log_guid;
cpu_to_leguids(&new_h->file_write_guid);
cpu_to_leguids(&new_h->data_write_guid);
cpu_to_leguids(&new_h->log_guid);
new_h->log_version = cpu_to_le16(orig_h->log_version);
new_h->version = cpu_to_le16(orig_h->version);
new_h->log_length = cpu_to_le32(orig_h->log_length);
new_h->log_offset = cpu_to_le64(orig_h->log_offset);
}
/* VHDX Log Headers */
void vhdx_log_desc_le_import(VHDXLogDescriptor *d)
{
assert(d != NULL);
le32_to_cpus(&d->signature);
le32_to_cpus(&d->trailing_bytes);
le64_to_cpus(&d->leading_bytes);
le64_to_cpus(&d->file_offset);
le64_to_cpus(&d->sequence_number);
}
void vhdx_log_desc_le_export(VHDXLogDescriptor *d)
{
assert(d != NULL);
cpu_to_le32s(&d->signature);
cpu_to_le32s(&d->trailing_bytes);
cpu_to_le64s(&d->leading_bytes);
cpu_to_le64s(&d->file_offset);
cpu_to_le64s(&d->sequence_number);
}
void vhdx_log_data_le_export(VHDXLogDataSector *d)
{
assert(d != NULL);
cpu_to_le32s(&d->data_signature);
cpu_to_le32s(&d->sequence_high);
cpu_to_le32s(&d->sequence_low);
}
void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr)
{
assert(hdr != NULL);
le32_to_cpus(&hdr->signature);
le32_to_cpus(&hdr->checksum);
le32_to_cpus(&hdr->entry_length);
le32_to_cpus(&hdr->tail);
le64_to_cpus(&hdr->sequence_number);
le32_to_cpus(&hdr->descriptor_count);
leguid_to_cpus(&hdr->log_guid);
le64_to_cpus(&hdr->flushed_file_offset);
le64_to_cpus(&hdr->last_file_offset);
}
void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr)
{
assert(hdr != NULL);
cpu_to_le32s(&hdr->signature);
cpu_to_le32s(&hdr->checksum);
cpu_to_le32s(&hdr->entry_length);
cpu_to_le32s(&hdr->tail);
cpu_to_le64s(&hdr->sequence_number);
cpu_to_le32s(&hdr->descriptor_count);
cpu_to_leguids(&hdr->log_guid);
cpu_to_le64s(&hdr->flushed_file_offset);
cpu_to_le64s(&hdr->last_file_offset);
}
/* Region table entries */
void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr)
{
assert(hdr != NULL);
le32_to_cpus(&hdr->signature);
le32_to_cpus(&hdr->checksum);
le32_to_cpus(&hdr->entry_count);
}
void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr)
{
assert(hdr != NULL);
cpu_to_le32s(&hdr->signature);
cpu_to_le32s(&hdr->checksum);
cpu_to_le32s(&hdr->entry_count);
}
void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
{
assert(e != NULL);
leguid_to_cpus(&e->guid);
le64_to_cpus(&e->file_offset);
le32_to_cpus(&e->length);
le32_to_cpus(&e->data_bits);
}
void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
{
assert(e != NULL);
cpu_to_leguids(&e->guid);
cpu_to_le64s(&e->file_offset);
cpu_to_le32s(&e->length);
cpu_to_le32s(&e->data_bits);
}
/* Metadata headers & table */
void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr)
{
assert(hdr != NULL);
le64_to_cpus(&hdr->signature);
le16_to_cpus(&hdr->entry_count);
}
void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr)
{
assert(hdr != NULL);
cpu_to_le64s(&hdr->signature);
cpu_to_le16s(&hdr->entry_count);
}
void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
{
assert(e != NULL);
leguid_to_cpus(&e->item_id);
le32_to_cpus(&e->offset);
le32_to_cpus(&e->length);
le32_to_cpus(&e->data_bits);
}
void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e)
{
assert(e != NULL);
cpu_to_leguids(&e->item_id);
cpu_to_le32s(&e->offset);
cpu_to_le32s(&e->length);
cpu_to_le32s(&e->data_bits);
}
此差异已折叠。
此差异已折叠。
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
* Authors: * Authors:
* Jeff Cody <jcody@redhat.com> * Jeff Cody <jcody@redhat.com>
* *
* This is based on the "VHDX Format Specification v0.95", published 4/12/2012 * This is based on the "VHDX Format Specification v1.00", published 8/25/2012
* by Microsoft: * by Microsoft:
* https://www.microsoft.com/en-us/download/details.aspx?id=29681 * https://www.microsoft.com/en-us/download/details.aspx?id=34750
* *
* This work is licensed under the terms of the GNU LGPL, version 2 or later. * This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory. * See the COPYING.LIB file in the top-level directory.
...@@ -18,6 +18,11 @@ ...@@ -18,6 +18,11 @@
#ifndef BLOCK_VHDX_H #ifndef BLOCK_VHDX_H
#define BLOCK_VHDX_H #define BLOCK_VHDX_H
#define KiB (1 * 1024)
#define MiB (KiB * 1024)
#define GiB (MiB * 1024)
#define TiB ((uint64_t) GiB * 1024)
/* Structures and fields present in the VHDX file */ /* Structures and fields present in the VHDX file */
/* The header section has the following blocks, /* The header section has the following blocks,
...@@ -30,14 +35,15 @@ ...@@ -30,14 +35,15 @@
* 0.........64KB...........128KB........192KB..........256KB................1MB * 0.........64KB...........128KB........192KB..........256KB................1MB
*/ */
#define VHDX_HEADER_BLOCK_SIZE (64*1024) #define VHDX_HEADER_BLOCK_SIZE (64 * 1024)
#define VHDX_FILE_ID_OFFSET 0 #define VHDX_FILE_ID_OFFSET 0
#define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE*1) #define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE * 1)
#define VHDX_HEADER2_OFFSET (VHDX_HEADER_BLOCK_SIZE*2) #define VHDX_HEADER2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 2)
#define VHDX_REGION_TABLE_OFFSET (VHDX_HEADER_BLOCK_SIZE*3) #define VHDX_REGION_TABLE_OFFSET (VHDX_HEADER_BLOCK_SIZE * 3)
#define VHDX_REGION_TABLE2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 4)
#define VHDX_HEADER_SECTION_END (1 * MiB)
/* /*
* A note on the use of MS-GUID fields. For more details on the GUID, * A note on the use of MS-GUID fields. For more details on the GUID,
* please see: https://en.wikipedia.org/wiki/Globally_unique_identifier. * please see: https://en.wikipedia.org/wiki/Globally_unique_identifier.
...@@ -55,10 +61,11 @@ ...@@ -55,10 +61,11 @@
/* These structures are ones that are defined in the VHDX specification /* These structures are ones that are defined in the VHDX specification
* document */ * document */
#define VHDX_FILE_SIGNATURE 0x656C696678646876 /* "vhdxfile" in ASCII */
typedef struct VHDXFileIdentifier { typedef struct VHDXFileIdentifier {
uint64_t signature; /* "vhdxfile" in ASCII */ uint64_t signature; /* "vhdxfile" in ASCII */
uint16_t creator[256]; /* optional; utf-16 string to identify uint16_t creator[256]; /* optional; utf-16 string to identify
the vhdx file creator. Diagnotistic the vhdx file creator. Diagnostic
only */ only */
} VHDXFileIdentifier; } VHDXFileIdentifier;
...@@ -67,7 +74,7 @@ typedef struct VHDXFileIdentifier { ...@@ -67,7 +74,7 @@ typedef struct VHDXFileIdentifier {
* Microsoft is not just 16 bytes though - it is a structure that is defined, * Microsoft is not just 16 bytes though - it is a structure that is defined,
* so we need to follow it here so that endianness does not trip us up */ * so we need to follow it here so that endianness does not trip us up */
typedef struct MSGUID { typedef struct QEMU_PACKED MSGUID {
uint32_t data1; uint32_t data1;
uint16_t data2; uint16_t data2;
uint16_t data3; uint16_t data3;
...@@ -77,7 +84,7 @@ typedef struct MSGUID { ...@@ -77,7 +84,7 @@ typedef struct MSGUID {
#define guid_eq(a, b) \ #define guid_eq(a, b) \
(memcmp(&(a), &(b), sizeof(MSGUID)) == 0) (memcmp(&(a), &(b), sizeof(MSGUID)) == 0)
#define VHDX_HEADER_SIZE (4*1024) /* although the vhdx_header struct in disk #define VHDX_HEADER_SIZE (4 * 1024) /* although the vhdx_header struct in disk
is only 582 bytes, for purposes of crc is only 582 bytes, for purposes of crc
the header is the first 4KB of the 64KB the header is the first 4KB of the 64KB
block */ block */
...@@ -85,6 +92,7 @@ typedef struct MSGUID { ...@@ -85,6 +92,7 @@ typedef struct MSGUID {
/* The full header is 4KB, although the actual header data is much smaller. /* The full header is 4KB, although the actual header data is much smaller.
* But for the checksum calculation, it is over the entire 4KB structure, * But for the checksum calculation, it is over the entire 4KB structure,
* not just the defined portion of it */ * not just the defined portion of it */
#define VHDX_HEADER_SIGNATURE 0x64616568
typedef struct QEMU_PACKED VHDXHeader { typedef struct QEMU_PACKED VHDXHeader {
uint32_t signature; /* "head" in ASCII */ uint32_t signature; /* "head" in ASCII */
uint32_t checksum; /* CRC-32C hash of the whole header */ uint32_t checksum; /* CRC-32C hash of the whole header */
...@@ -114,9 +122,9 @@ typedef struct QEMU_PACKED VHDXHeader { ...@@ -114,9 +122,9 @@ typedef struct QEMU_PACKED VHDXHeader {
there is no valid log. If non-zero, there is no valid log. If non-zero,
log entries with this guid are log entries with this guid are
valid. */ valid. */
uint16_t log_version; /* version of the log format. Mustn't be uint16_t log_version; /* version of the log format. Must be
zero, unless log_guid is also zero */ set to zero */
uint16_t version; /* version of th evhdx file. Currently, uint16_t version; /* version of the vhdx file. Currently,
only supported version is "1" */ only supported version is "1" */
uint32_t log_length; /* length of the log. Must be multiple uint32_t log_length; /* length of the log. Must be multiple
of 1MB */ of 1MB */
...@@ -125,6 +133,7 @@ typedef struct QEMU_PACKED VHDXHeader { ...@@ -125,6 +133,7 @@ typedef struct QEMU_PACKED VHDXHeader {
} VHDXHeader; } VHDXHeader;
/* Header for the region table block */ /* Header for the region table block */
#define VHDX_REGION_SIGNATURE 0x69676572 /* "regi" in ASCII */
typedef struct QEMU_PACKED VHDXRegionTableHeader { typedef struct QEMU_PACKED VHDXRegionTableHeader {
uint32_t signature; /* "regi" in ASCII */ uint32_t signature; /* "regi" in ASCII */
uint32_t checksum; /* CRC-32C hash of the 64KB table */ uint32_t checksum; /* CRC-32C hash of the 64KB table */
...@@ -151,7 +160,10 @@ typedef struct QEMU_PACKED VHDXRegionTableEntry { ...@@ -151,7 +160,10 @@ typedef struct QEMU_PACKED VHDXRegionTableEntry {
/* ---- LOG ENTRY STRUCTURES ---- */ /* ---- LOG ENTRY STRUCTURES ---- */
#define VHDX_LOG_MIN_SIZE (1024 * 1024)
#define VHDX_LOG_SECTOR_SIZE 4096
#define VHDX_LOG_HDR_SIZE 64 #define VHDX_LOG_HDR_SIZE 64
#define VHDX_LOG_SIGNATURE 0x65676f6c
typedef struct QEMU_PACKED VHDXLogEntryHeader { typedef struct QEMU_PACKED VHDXLogEntryHeader {
uint32_t signature; /* "loge" in ASCII */ uint32_t signature; /* "loge" in ASCII */
uint32_t checksum; /* CRC-32C hash of the 64KB table */ uint32_t checksum; /* CRC-32C hash of the 64KB table */
...@@ -174,7 +186,8 @@ typedef struct QEMU_PACKED VHDXLogEntryHeader { ...@@ -174,7 +186,8 @@ typedef struct QEMU_PACKED VHDXLogEntryHeader {
} VHDXLogEntryHeader; } VHDXLogEntryHeader;
#define VHDX_LOG_DESC_SIZE 32 #define VHDX_LOG_DESC_SIZE 32
#define VHDX_LOG_DESC_SIGNATURE 0x63736564
#define VHDX_LOG_ZERO_SIGNATURE 0x6f72657a
typedef struct QEMU_PACKED VHDXLogDescriptor { typedef struct QEMU_PACKED VHDXLogDescriptor {
uint32_t signature; /* "zero" or "desc" in ASCII */ uint32_t signature; /* "zero" or "desc" in ASCII */
union { union {
...@@ -194,6 +207,7 @@ typedef struct QEMU_PACKED VHDXLogDescriptor { ...@@ -194,6 +207,7 @@ typedef struct QEMU_PACKED VHDXLogDescriptor {
vhdx_log_entry_header */ vhdx_log_entry_header */
} VHDXLogDescriptor; } VHDXLogDescriptor;
#define VHDX_LOG_DATA_SIGNATURE 0x61746164
typedef struct QEMU_PACKED VHDXLogDataSector { typedef struct QEMU_PACKED VHDXLogDataSector {
uint32_t data_signature; /* "data" in ASCII */ uint32_t data_signature; /* "data" in ASCII */
uint32_t sequence_high; /* 4 MSB of 8 byte sequence_number */ uint32_t sequence_high; /* 4 MSB of 8 byte sequence_number */
...@@ -212,19 +226,19 @@ typedef struct QEMU_PACKED VHDXLogDataSector { ...@@ -212,19 +226,19 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
#define PAYLOAD_BLOCK_UNDEFINED 1 #define PAYLOAD_BLOCK_UNDEFINED 1
#define PAYLOAD_BLOCK_ZERO 2 #define PAYLOAD_BLOCK_ZERO 2
#define PAYLOAD_BLOCK_UNMAPPED 5 #define PAYLOAD_BLOCK_UNMAPPED 5
#define PAYLOAD_BLOCK_FULL_PRESENT 6 #define PAYLOAD_BLOCK_FULLY_PRESENT 6
#define PAYLOAD_BLOCK_PARTIALLY_PRESENT 7 #define PAYLOAD_BLOCK_PARTIALLY_PRESENT 7
#define SB_BLOCK_NOT_PRESENT 0 #define SB_BLOCK_NOT_PRESENT 0
#define SB_BLOCK_PRESENT 6 #define SB_BLOCK_PRESENT 6
/* per the spec */ /* per the spec */
#define VHDX_MAX_SECTORS_PER_BLOCK (1<<23) #define VHDX_MAX_SECTORS_PER_BLOCK (1 << 23)
/* upper 44 bits are the file offset in 1MB units lower 3 bits are the state /* upper 44 bits are the file offset in 1MB units lower 3 bits are the state
other bits are reserved */ other bits are reserved */
#define VHDX_BAT_STATE_BIT_MASK 0x07 #define VHDX_BAT_STATE_BIT_MASK 0x07
#define VHDX_BAT_FILE_OFF_BITS (64-44) #define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000 /* upper 44 bits */
typedef uint64_t VHDXBatEntry; typedef uint64_t VHDXBatEntry;
/* ---- METADATA REGION STRUCTURES ---- */ /* ---- METADATA REGION STRUCTURES ---- */
...@@ -233,6 +247,7 @@ typedef uint64_t VHDXBatEntry; ...@@ -233,6 +247,7 @@ typedef uint64_t VHDXBatEntry;
#define VHDX_METADATA_MAX_ENTRIES 2047 /* not including the header */ #define VHDX_METADATA_MAX_ENTRIES 2047 /* not including the header */
#define VHDX_METADATA_TABLE_MAX_SIZE \ #define VHDX_METADATA_TABLE_MAX_SIZE \
(VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1)) (VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1))
#define VHDX_METADATA_SIGNATURE 0x617461646174656D /* "metadata" in ASCII */
typedef struct QEMU_PACKED VHDXMetadataTableHeader { typedef struct QEMU_PACKED VHDXMetadataTableHeader {
uint64_t signature; /* "metadata" in ASCII */ uint64_t signature; /* "metadata" in ASCII */
uint16_t reserved; uint16_t reserved;
...@@ -252,8 +267,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry { ...@@ -252,8 +267,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
metadata region */ metadata region */
/* note: if length = 0, so is offset */ /* note: if length = 0, so is offset */
uint32_t length; /* length of metadata. <= 1MB. */ uint32_t length; /* length of metadata. <= 1MB. */
uint32_t data_bits; /* least-significant 3 bits are flags, the uint32_t data_bits; /* least-significant 3 bits are flags,
rest are reserved (see above) */ the rest are reserved (see above) */
uint32_t reserved2; uint32_t reserved2;
} VHDXMetadataTableEntry; } VHDXMetadataTableEntry;
...@@ -262,13 +277,16 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry { ...@@ -262,13 +277,16 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
If set indicates a fixed If set indicates a fixed
size VHDX file */ size VHDX file */
#define VHDX_PARAMS_HAS_PARENT 0x02 /* has parent / backing file */ #define VHDX_PARAMS_HAS_PARENT 0x02 /* has parent / backing file */
#define VHDX_BLOCK_SIZE_MIN (1 * MiB)
#define VHDX_BLOCK_SIZE_MAX (256 * MiB)
typedef struct QEMU_PACKED VHDXFileParameters { typedef struct QEMU_PACKED VHDXFileParameters {
uint32_t block_size; /* size of each payload block, always uint32_t block_size; /* size of each payload block, always
power of 2, <= 256MB and >= 1MB. */ power of 2, <= 256MB and >= 1MB. */
uint32_t data_bits; /* least-significant 2 bits are flags, the rest uint32_t data_bits; /* least-significant 2 bits are flags,
are reserved (see above) */ the rest are reserved (see above) */
} VHDXFileParameters; } VHDXFileParameters;
#define VHDX_MAX_IMAGE_SIZE ((uint64_t) 64 * TiB)
typedef struct QEMU_PACKED VHDXVirtualDiskSize { typedef struct QEMU_PACKED VHDXVirtualDiskSize {
uint64_t virtual_disk_size; /* Size of the virtual disk, in bytes. uint64_t virtual_disk_size; /* Size of the virtual disk, in bytes.
Must be multiple of the sector size, Must be multiple of the sector size,
...@@ -276,7 +294,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskSize { ...@@ -276,7 +294,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskSize {
} VHDXVirtualDiskSize; } VHDXVirtualDiskSize;
typedef struct QEMU_PACKED VHDXPage83Data { typedef struct QEMU_PACKED VHDXPage83Data {
MSGUID page_83_data[16]; /* unique id for scsi devices that MSGUID page_83_data; /* unique id for scsi devices that
support page 0x83 */ support page 0x83 */
} VHDXPage83Data; } VHDXPage83Data;
...@@ -291,7 +309,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskPhysicalSectorSize { ...@@ -291,7 +309,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskPhysicalSectorSize {
} VHDXVirtualDiskPhysicalSectorSize; } VHDXVirtualDiskPhysicalSectorSize;
typedef struct QEMU_PACKED VHDXParentLocatorHeader { typedef struct QEMU_PACKED VHDXParentLocatorHeader {
MSGUID locator_type[16]; /* type of the parent virtual disk. */ MSGUID locator_type; /* type of the parent virtual disk. */
uint16_t reserved; uint16_t reserved;
uint16_t key_value_count; /* number of key/value pairs for this uint16_t key_value_count; /* number of key/value pairs for this
locator */ locator */
...@@ -308,18 +326,122 @@ typedef struct QEMU_PACKED VHDXParentLocatorEntry { ...@@ -308,18 +326,122 @@ typedef struct QEMU_PACKED VHDXParentLocatorEntry {
/* ----- END VHDX SPECIFICATION STRUCTURES ---- */ /* ----- END VHDX SPECIFICATION STRUCTURES ---- */
typedef struct VHDXMetadataEntries {
VHDXMetadataTableEntry file_parameters_entry;
VHDXMetadataTableEntry virtual_disk_size_entry;
VHDXMetadataTableEntry page83_data_entry;
VHDXMetadataTableEntry logical_sector_size_entry;
VHDXMetadataTableEntry phys_sector_size_entry;
VHDXMetadataTableEntry parent_locator_entry;
uint16_t present;
} VHDXMetadataEntries;
typedef struct VHDXLogEntries {
uint64_t offset;
uint64_t length;
uint32_t write;
uint32_t read;
VHDXLogEntryHeader *hdr;
void *desc_buffer;
uint64_t sequence;
uint32_t tail;
} VHDXLogEntries;
typedef struct VHDXRegionEntry {
uint64_t start;
uint64_t end;
QLIST_ENTRY(VHDXRegionEntry) entries;
} VHDXRegionEntry;
typedef struct BDRVVHDXState {
CoMutex lock;
int curr_header;
VHDXHeader *headers[2];
VHDXRegionTableHeader rt;
VHDXRegionTableEntry bat_rt; /* region table for the BAT */
VHDXRegionTableEntry metadata_rt; /* region table for the metadata */
VHDXMetadataTableHeader metadata_hdr;
VHDXMetadataEntries metadata_entries;
VHDXFileParameters params;
uint32_t block_size;
uint32_t block_size_bits;
uint32_t sectors_per_block;
uint32_t sectors_per_block_bits;
uint64_t virtual_disk_size;
uint32_t logical_sector_size;
uint32_t physical_sector_size;
uint64_t chunk_ratio;
uint32_t chunk_ratio_bits;
uint32_t logical_sector_size_bits;
uint32_t bat_entries;
VHDXBatEntry *bat;
uint64_t bat_offset;
bool first_visible_write;
MSGUID session_guid;
VHDXLogEntries log;
VHDXParentLocatorHeader parent_header;
VHDXParentLocatorEntry *parent_entries;
Error *migration_blocker;
QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions;
} BDRVVHDXState;
void vhdx_guid_generate(MSGUID *guid);
int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw,
MSGUID *log_guid);
uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset);
uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
int crc_offset); int crc_offset);
bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset); bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset);
int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed);
int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
void *data, uint32_t length, uint64_t offset);
static void leguid_to_cpus(MSGUID *guid) static inline void leguid_to_cpus(MSGUID *guid)
{ {
le32_to_cpus(&guid->data1); le32_to_cpus(&guid->data1);
le16_to_cpus(&guid->data2); le16_to_cpus(&guid->data2);
le16_to_cpus(&guid->data3); le16_to_cpus(&guid->data3);
} }
static inline void cpu_to_leguids(MSGUID *guid)
{
cpu_to_le32s(&guid->data1);
cpu_to_le16s(&guid->data2);
cpu_to_le16s(&guid->data3);
}
void vhdx_header_le_import(VHDXHeader *h);
void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h);
void vhdx_log_desc_le_import(VHDXLogDescriptor *d);
void vhdx_log_desc_le_export(VHDXLogDescriptor *d);
void vhdx_log_data_le_export(VHDXLogDataSector *d);
void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr);
void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr);
void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr);
void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr);
void vhdx_region_entry_le_import(VHDXRegionTableEntry *e);
void vhdx_region_entry_le_export(VHDXRegionTableEntry *e);
void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr);
void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr);
void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e);
void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e);
int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s);
#endif #endif
...@@ -211,6 +211,15 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, ...@@ -211,6 +211,15 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
bs->total_sectors = (int64_t) bs->total_sectors = (int64_t)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
/* images created with disk2vhd report a far higher virtual size
* than expected with the cyls * heads * sectors_per_cyl formula.
* use the footer->size instead if the image was created with
* disk2vhd.
*/
if (!strncmp(footer->creator_app, "d2v", 4)) {
bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE;
}
/* Allow a maximum disk size of approximately 2 TB */ /* Allow a maximum disk size of approximately 2 TB */
if (bs->total_sectors >= 65535LL * 255 * 255) { if (bs->total_sectors >= 65535LL * 255 * 255) {
ret = -EFBIG; ret = -EFBIG;
......
...@@ -341,7 +341,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -341,7 +341,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
qemu_opts_absorb_qdict(opts, bs_opts, &error); qemu_opts_absorb_qdict(opts, bs_opts, &error);
if (error_is_set(&error)) { if (error_is_set(&error)) {
error_propagate(errp, error); error_propagate(errp, error);
return NULL; goto early_err;
} }
if (id) { if (id) {
...@@ -361,7 +361,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -361,7 +361,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if ((buf = qemu_opt_get(opts, "discard")) != NULL) { if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
error_setg(errp, "invalid discard option"); error_setg(errp, "invalid discard option");
return NULL; goto early_err;
} }
} }
...@@ -383,7 +383,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -383,7 +383,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
/* this is the default */ /* this is the default */
} else { } else {
error_setg(errp, "invalid aio option"); error_setg(errp, "invalid aio option");
return NULL; goto early_err;
} }
} }
#endif #endif
...@@ -393,13 +393,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -393,13 +393,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
error_printf("Supported formats:"); error_printf("Supported formats:");
bdrv_iterate_format(bdrv_format_print, NULL); bdrv_iterate_format(bdrv_format_print, NULL);
error_printf("\n"); error_printf("\n");
return NULL; goto early_err;
} }
drv = bdrv_find_format(buf); drv = bdrv_find_format(buf);
if (!drv) { if (!drv) {
error_setg(errp, "'%s' invalid format", buf); error_setg(errp, "'%s' invalid format", buf);
return NULL; goto early_err;
} }
} }
...@@ -435,20 +435,20 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -435,20 +435,20 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if (!check_throttle_config(&cfg, &error)) { if (!check_throttle_config(&cfg, &error)) {
error_propagate(errp, error); error_propagate(errp, error);
return NULL; goto early_err;
} }
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) { if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
error_setg(errp, "werror is not supported by this bus type"); error_setg(errp, "werror is not supported by this bus type");
return NULL; goto early_err;
} }
on_write_error = parse_block_error_action(buf, 0, &error); on_write_error = parse_block_error_action(buf, 0, &error);
if (error_is_set(&error)) { if (error_is_set(&error)) {
error_propagate(errp, error); error_propagate(errp, error);
return NULL; goto early_err;
} }
} }
...@@ -456,13 +456,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -456,13 +456,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
error_report("rerror is not supported by this bus type"); error_report("rerror is not supported by this bus type");
return NULL; goto early_err;
} }
on_read_error = parse_block_error_action(buf, 1, &error); on_read_error = parse_block_error_action(buf, 1, &error);
if (error_is_set(&error)) { if (error_is_set(&error)) {
error_propagate(errp, error); error_propagate(errp, error);
return NULL; goto early_err;
} }
} }
...@@ -491,6 +491,8 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -491,6 +491,8 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if (has_driver_specific_opts) { if (has_driver_specific_opts) {
file = NULL; file = NULL;
} else { } else {
QDECREF(bs_opts);
qemu_opts_del(opts);
return dinfo; return dinfo;
} }
} }
...@@ -529,12 +531,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts, ...@@ -529,12 +531,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
return dinfo; return dinfo;
err: err:
qemu_opts_del(opts);
QDECREF(bs_opts);
bdrv_unref(dinfo->bdrv); bdrv_unref(dinfo->bdrv);
g_free(dinfo->id); g_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next); QTAILQ_REMOVE(&drives, dinfo, next);
g_free(dinfo); g_free(dinfo);
early_err:
QDECREF(bs_opts);
qemu_opts_del(opts);
return NULL; return NULL;
} }
......
...@@ -260,6 +260,7 @@ gtk="" ...@@ -260,6 +260,7 @@ gtk=""
gtkabi="2.0" gtkabi="2.0"
tpm="no" tpm="no"
libssh2="" libssh2=""
vhdx=""
# parse CC options first # parse CC options first
for opt do for opt do
...@@ -985,6 +986,10 @@ for opt do ...@@ -985,6 +986,10 @@ for opt do
;; ;;
--enable-libssh2) libssh2="yes" --enable-libssh2) libssh2="yes"
;; ;;
--enable-vhdx) vhdx="yes"
;;
--disable-vhdx) vhdx="no"
;;
*) echo "ERROR: unknown option $opt"; show_help="yes" *) echo "ERROR: unknown option $opt"; show_help="yes"
;; ;;
esac esac
...@@ -1217,6 +1222,8 @@ echo " --gcov=GCOV use specified gcov [$gcov_tool]" ...@@ -1217,6 +1222,8 @@ echo " --gcov=GCOV use specified gcov [$gcov_tool]"
echo " --enable-tpm enable TPM support" echo " --enable-tpm enable TPM support"
echo " --disable-libssh2 disable ssh block device support" echo " --disable-libssh2 disable ssh block device support"
echo " --enable-libssh2 enable ssh block device support" echo " --enable-libssh2 enable ssh block device support"
echo " --disable-vhdx disables support for the Microsoft VHDX image format"
echo " --enable-vhdx enable support for the Microsoft VHDX image format"
echo "" echo ""
echo "NOTE: The object files are built at the place where configure is launched" echo "NOTE: The object files are built at the place where configure is launched"
exit 1 exit 1
...@@ -2017,6 +2024,18 @@ EOF ...@@ -2017,6 +2024,18 @@ EOF
fi fi
fi fi
if test "$vhdx" = "yes" ; then
if test "$uuid" = "no" ; then
error_exit "uuid required for VHDX support"
fi
elif test "$vhdx" != "no" ; then
if test "$uuid" = "yes" ; then
vhdx=yes
else
vhdx=no
fi
fi
########################################## ##########################################
# xfsctl() probe, used for raw-posix # xfsctl() probe, used for raw-posix
if test "$xfs" != "no" ; then if test "$xfs" != "no" ; then
...@@ -3760,6 +3779,7 @@ echo "TPM support $tpm" ...@@ -3760,6 +3779,7 @@ echo "TPM support $tpm"
echo "libssh2 support $libssh2" echo "libssh2 support $libssh2"
echo "TPM passthrough $tpm_passthrough" echo "TPM passthrough $tpm_passthrough"
echo "QOM debugging $qom_cast_debug" echo "QOM debugging $qom_cast_debug"
echo "vhdx $vhdx"
if test "$sdl_too_old" = "yes"; then if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support" echo "-> Your SDL version is too old - please upgrade to have SDL support"
...@@ -4152,6 +4172,10 @@ if test "$virtio_blk_data_plane" = "yes" ; then ...@@ -4152,6 +4172,10 @@ if test "$virtio_blk_data_plane" = "yes" ; then
echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak
fi fi
if test "$vhdx" = "yes" ; then
echo "CONFIG_VHDX=y" >> $config_host_mak
fi
# USB host support # USB host support
if test "$libusb" = "yes"; then if test "$libusb" = "yes"; then
echo "HOST_USB=libusb legacy" >> $config_host_mak echo "HOST_USB=libusb legacy" >> $config_host_mak
......
...@@ -227,7 +227,7 @@ ...@@ -227,7 +227,7 @@
## ##
# @ImageInfoSpecificVmdk: # @ImageInfoSpecificVmdk:
# #
# @create_type: The create type of VMDK image # @create-type: The create type of VMDK image
# #
# @cid: Content id of image # @cid: Content id of image
# #
......
...@@ -68,6 +68,8 @@ check-qtest-i386-y += tests/rtc-test$(EXESUF) ...@@ -68,6 +68,8 @@ check-qtest-i386-y += tests/rtc-test$(EXESUF)
check-qtest-i386-y += tests/i440fx-test$(EXESUF) check-qtest-i386-y += tests/i440fx-test$(EXESUF)
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
check-qtest-i386-y += tests/qom-test$(EXESUF) check-qtest-i386-y += tests/qom-test$(EXESUF)
check-qtest-i386-y += tests/blockdev-test$(EXESUF)
check-qtest-i386-y += tests/qdev-monitor-test$(EXESUF)
check-qtest-x86_64-y = $(check-qtest-i386-y) check-qtest-x86_64-y = $(check-qtest-i386-y)
gcov-files-i386-y += i386-softmmu/hw/mc146818rtc.c gcov-files-i386-y += i386-softmmu/hw/mc146818rtc.c
gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y))
...@@ -200,6 +202,8 @@ tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) ...@@ -200,6 +202,8 @@ tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
tests/qom-test$(EXESUF): tests/qom-test.o tests/qom-test$(EXESUF): tests/qom-test.o
tests/blockdev-test$(EXESUF): tests/blockdev-test.o $(libqos-pc-obj-y)
tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y)
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
# QTest rules # QTest rules
......
/*
* blockdev.c test cases
*
* Copyright (C) 2013 Red Hat Inc.
*
* Authors:
* Stefan Hajnoczi <stefanha@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include <glib.h>
#include <string.h>
#include "libqtest.h"
static void test_drive_add_empty(void)
{
QDict *response;
const char *response_return;
/* Start with an empty drive */
qtest_start("-drive if=none,id=drive0");
/* Delete the drive */
response = qmp("{\"execute\": \"human-monitor-command\","
" \"arguments\": {"
" \"command-line\": \"drive_del drive0\""
"}}");
g_assert(response);
response_return = qdict_get_try_str(response, "return");
g_assert(response_return);
g_assert(strcmp(response_return, "") == 0);
QDECREF(response);
/* Ensure re-adding the drive works - there should be no duplicate ID error
* because the old drive must be gone.
*/
response = qmp("{\"execute\": \"human-monitor-command\","
" \"arguments\": {"
" \"command-line\": \"drive_add 0 if=none,id=drive0\""
"}}");
g_assert(response);
response_return = qdict_get_try_str(response, "return");
g_assert(response_return);
g_assert(strcmp(response_return, "OK\r\n") == 0);
QDECREF(response);
qtest_end();
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("/qmp/drive_add_empty", test_drive_add_empty);
return g_test_run();
}
...@@ -41,12 +41,12 @@ static void test_a_boot_order(const char *machine, ...@@ -41,12 +41,12 @@ static void test_a_boot_order(const char *machine,
qtest_start(args); qtest_start(args);
actual = read_boot_order(); actual = read_boot_order();
g_assert_cmphex(actual, ==, expected_boot); g_assert_cmphex(actual, ==, expected_boot);
qmp("{ 'execute': 'system_reset' }"); qmp_discard_response("{ 'execute': 'system_reset' }");
/* /*
* system_reset only requests reset. We get a RESET event after * system_reset only requests reset. We get a RESET event after
* the actual reset completes. Need to wait for that. * the actual reset completes. Need to wait for that.
*/ */
qmp(""); /* HACK: wait for event */ qmp_discard_response(""); /* HACK: wait for event */
actual = read_boot_order(); actual = read_boot_order();
g_assert_cmphex(actual, ==, expected_reboot); g_assert_cmphex(actual, ==, expected_reboot);
qtest_quit(global_qtest); qtest_quit(global_qtest);
......
...@@ -290,10 +290,12 @@ static void test_media_insert(void) ...@@ -290,10 +290,12 @@ static void test_media_insert(void)
/* Insert media in drive. DSKCHK should not be reset until a step pulse /* Insert media in drive. DSKCHK should not be reset until a step pulse
* is sent. */ * is sent. */
qmp("{'execute':'change', 'arguments':{ 'device':'floppy0', " qmp_discard_response("{'execute':'change', 'arguments':{"
"'target': '%s' }}", test_image); " 'device':'floppy0', 'target': '%s' }}",
qmp(""); /* ignore event (FIXME open -> open transition?!) */ test_image);
qmp(""); /* ignore event */ qmp_discard_response(""); /* ignore event
(FIXME open -> open transition?!) */
qmp_discard_response(""); /* ignore event */
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
...@@ -322,8 +324,9 @@ static void test_media_change(void) ...@@ -322,8 +324,9 @@ static void test_media_change(void)
/* Eject the floppy and check that DSKCHG is set. Reading it out doesn't /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't
* reset the bit. */ * reset the bit. */
qmp("{'execute':'eject', 'arguments':{ 'device':'floppy0' }}"); qmp_discard_response("{'execute':'eject', 'arguments':{"
qmp(""); /* ignore event */ " 'device':'floppy0' }}");
qmp_discard_response(""); /* ignore event */
dir = inb(FLOPPY_BASE + reg_dir); dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG); assert_bit_set(dir, DSKCHG);
......
...@@ -460,8 +460,9 @@ static void test_flush(void) ...@@ -460,8 +460,9 @@ static void test_flush(void)
tmp_path); tmp_path);
/* Delay the completion of the flush request until we explicitly do it */ /* Delay the completion of the flush request until we explicitly do it */
qmp("{'execute':'human-monitor-command', 'arguments': { " qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
"'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }"); " 'command-line':"
" 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
/* FLUSH CACHE command on device 0*/ /* FLUSH CACHE command on device 0*/
outb(IDE_BASE + reg_device, 0); outb(IDE_BASE + reg_device, 0);
...@@ -473,8 +474,9 @@ static void test_flush(void) ...@@ -473,8 +474,9 @@ static void test_flush(void)
assert_bit_clear(data, DF | ERR | DRQ); assert_bit_clear(data, DF | ERR | DRQ);
/* Complete the command */ /* Complete the command */
qmp("{'execute':'human-monitor-command', 'arguments': { " qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
"'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }"); " 'command-line':"
" 'qemu-io ide0-hd0 \"resume A\"'} }");
/* Check registers */ /* Check registers */
data = inb(IDE_BASE + reg_device); data = inb(IDE_BASE + reg_device);
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include "qemu/compiler.h" #include "qemu/compiler.h"
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
#define MAX_IRQ 256 #define MAX_IRQ 256
...@@ -151,8 +153,8 @@ QTestState *qtest_init(const char *extra_args) ...@@ -151,8 +153,8 @@ QTestState *qtest_init(const char *extra_args)
} }
/* Read the QMP greeting and then do the handshake */ /* Read the QMP greeting and then do the handshake */
qtest_qmp(s, ""); qtest_qmp_discard_response(s, "");
qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"); qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }");
if (getenv("QTEST_STOP")) { if (getenv("QTEST_STOP")) {
kill(qtest_qemu_pid(s), SIGSTOP); kill(qtest_qemu_pid(s), SIGSTOP);
...@@ -291,16 +293,38 @@ redo: ...@@ -291,16 +293,38 @@ redo:
return words; return words;
} }
void qtest_qmpv(QTestState *s, const char *fmt, va_list ap) typedef struct {
JSONMessageParser parser;
QDict *response;
} QMPResponseParser;
static void qmp_response(JSONMessageParser *parser, QList *tokens)
{
QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser);
QObject *obj;
obj = json_parser_parse(tokens, NULL);
if (!obj) {
fprintf(stderr, "QMP JSON response parsing failed\n");
exit(1);
}
g_assert(qobject_type(obj) == QTYPE_QDICT);
g_assert(!qmp->response);
qmp->response = (QDict *)obj;
}
QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
{ {
bool has_reply = false; QMPResponseParser qmp;
int nesting = 0;
/* Send QMP request */ /* Send QMP request */
socket_sendf(s->qmp_fd, fmt, ap); socket_sendf(s->qmp_fd, fmt, ap);
/* Receive reply */ /* Receive reply */
while (!has_reply || nesting > 0) { qmp.response = NULL;
json_message_parser_init(&qmp.parser, qmp_response);
while (!qmp.response) {
ssize_t len; ssize_t len;
char c; char c;
...@@ -314,25 +338,39 @@ void qtest_qmpv(QTestState *s, const char *fmt, va_list ap) ...@@ -314,25 +338,39 @@ void qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
exit(1); exit(1);
} }
switch (c) { json_message_parser_feed(&qmp.parser, &c, 1);
case '{':
nesting++;
has_reply = true;
break;
case '}':
nesting--;
break;
}
} }
json_message_parser_destroy(&qmp.parser);
return qmp.response;
}
QDict *qtest_qmp(QTestState *s, const char *fmt, ...)
{
va_list ap;
QDict *response;
va_start(ap, fmt);
response = qtest_qmpv(s, fmt, ap);
va_end(ap);
return response;
}
void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap)
{
QDict *response = qtest_qmpv(s, fmt, ap);
QDECREF(response);
} }
void qtest_qmp(QTestState *s, const char *fmt, ...) void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...)
{ {
va_list ap; va_list ap;
QDict *response;
va_start(ap, fmt); va_start(ap, fmt);
qtest_qmpv(s, fmt, ap); response = qtest_qmpv(s, fmt, ap);
va_end(ap); va_end(ap);
QDECREF(response);
} }
const char *qtest_get_arch(void) const char *qtest_get_arch(void)
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdarg.h> #include <stdarg.h>
#include <sys/types.h> #include <sys/types.h>
#include "qapi/qmp/qdict.h"
typedef struct QTestState QTestState; typedef struct QTestState QTestState;
...@@ -43,14 +44,33 @@ QTestState *qtest_init(const char *extra_args); ...@@ -43,14 +44,33 @@ QTestState *qtest_init(const char *extra_args);
*/ */
void qtest_quit(QTestState *s); void qtest_quit(QTestState *s);
/**
* qtest_qmp_discard_response:
* @s: #QTestState instance to operate on.
* @fmt...: QMP message to send to qemu
*
* Sends a QMP message to QEMU and consumes the response.
*/
void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...);
/** /**
* qtest_qmp: * qtest_qmp:
* @s: #QTestState instance to operate on. * @s: #QTestState instance to operate on.
* @fmt...: QMP message to send to qemu * @fmt...: QMP message to send to qemu
* *
* Sends a QMP message to QEMU * Sends a QMP message to QEMU and returns the response.
*/
QDict *qtest_qmp(QTestState *s, const char *fmt, ...);
/**
* qtest_qmpv_discard_response:
* @s: #QTestState instance to operate on.
* @fmt: QMP message to send to QEMU
* @ap: QMP message arguments
*
* Sends a QMP message to QEMU and consumes the response.
*/ */
void qtest_qmp(QTestState *s, const char *fmt, ...); void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap);
/** /**
* qtest_qmpv: * qtest_qmpv:
...@@ -58,9 +78,9 @@ void qtest_qmp(QTestState *s, const char *fmt, ...); ...@@ -58,9 +78,9 @@ void qtest_qmp(QTestState *s, const char *fmt, ...);
* @fmt: QMP message to send to QEMU * @fmt: QMP message to send to QEMU
* @ap: QMP message arguments * @ap: QMP message arguments
* *
* Sends a QMP message to QEMU. * Sends a QMP message to QEMU and returns the response.
*/ */
void qtest_qmpv(QTestState *s, const char *fmt, va_list ap); QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap);
/** /**
* qtest_get_irq: * qtest_get_irq:
...@@ -334,14 +354,31 @@ static inline void qtest_end(void) ...@@ -334,14 +354,31 @@ static inline void qtest_end(void)
* qmp: * qmp:
* @fmt...: QMP message to send to qemu * @fmt...: QMP message to send to qemu
* *
* Sends a QMP message to QEMU * Sends a QMP message to QEMU and returns the response.
*/
static inline QDict *qmp(const char *fmt, ...)
{
va_list ap;
QDict *response;
va_start(ap, fmt);
response = qtest_qmpv(global_qtest, fmt, ap);
va_end(ap);
return response;
}
/**
* qmp_discard_response:
* @fmt...: QMP message to send to qemu
*
* Sends a QMP message to QEMU and consumes the response.
*/ */
static inline void qmp(const char *fmt, ...) static inline void qmp_discard_response(const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
qtest_qmpv(global_qtest, fmt, ap); qtest_qmpv_discard_response(global_qtest, fmt, ap);
va_end(ap); va_end(ap);
} }
......
/*
* qdev-monitor.c test cases
*
* Copyright (C) 2013 Red Hat Inc.
*
* Authors:
* Stefan Hajnoczi <stefanha@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include <string.h>
#include <glib.h>
#include "libqtest.h"
#include "qapi/qmp/qjson.h"
static void test_device_add(void)
{
QDict *response;
QDict *error;
qtest_start("-drive if=none,id=drive0");
/* Make device_add fail. If this leaks the virtio-blk-pci device then a
* reference to drive0 will also be held (via qdev properties).
*/
response = qmp("{\"execute\": \"device_add\","
" \"arguments\": {"
" \"driver\": \"virtio-blk-pci\","
" \"drive\": \"drive0\""
"}}");
g_assert(response);
error = qdict_get_qdict(response, "error");
g_assert(!strcmp(qdict_get_try_str(error, "class") ?: "",
"GenericError"));
g_assert(!strcmp(qdict_get_try_str(error, "desc") ?: "",
"Device initialization failed."));
QDECREF(response);
/* Delete the drive */
response = qmp("{\"execute\": \"human-monitor-command\","
" \"arguments\": {"
" \"command-line\": \"drive_del drive0\""
"}}");
g_assert(response);
g_assert(!strcmp(qdict_get_try_str(response, "return") ?: "(null)", ""));
QDECREF(response);
/* Try to re-add the drive. This fails with duplicate IDs if a leaked
* virtio-blk-pci exists that holds a reference to the old drive0.
*/
response = qmp("{\"execute\": \"human-monitor-command\","
" \"arguments\": {"
" \"command-line\": \"drive_add pci-addr=auto if=none,id=drive0\""
"}}");
g_assert(response);
g_assert(!strcmp(qdict_get_try_str(response, "return") ?: "",
"OK\r\n"));
QDECREF(response);
qtest_end();
}
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
/* Check architecture */
if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
g_test_message("Skipping test for non-x86\n");
return 0;
}
/* Run the tests */
g_test_init(&argc, &argv, NULL);
qtest_add_func("/qmp/device_add", test_device_add);
return g_test_run();
}
...@@ -66,7 +66,7 @@ echo "Creating test image with backing file" ...@@ -66,7 +66,7 @@ echo "Creating test image with backing file"
echo echo
TEST_IMG=$TEST_IMG_SAVE TEST_IMG=$TEST_IMG_SAVE
_make_test_img -b $TEST_IMG.base 6G _make_test_img -b "$TEST_IMG.base" 6G
echo "Filling test image" echo "Filling test image"
echo echo
......
...@@ -90,12 +90,12 @@ mv "$TEST_IMG" "$TEST_IMG.orig" ...@@ -90,12 +90,12 @@ mv "$TEST_IMG" "$TEST_IMG.orig"
# Test the conversion twice: One test with the old-style -B option and another # Test the conversion twice: One test with the old-style -B option and another
# one with -o backing_file # one with -o backing_file
for backing_option in "-B $TEST_IMG.base" "-o backing_file=$TEST_IMG.base"; do for backing_option in "-B " "-o backing_file="; do
echo echo
echo Testing conversion with $backing_option | _filter_testdir | _filter_imgfmt echo Testing conversion with $backing_option$TEST_IMG.base | _filter_testdir | _filter_imgfmt
echo echo
$QEMU_IMG convert -O $IMGFMT $backing_option "$TEST_IMG.orig" "$TEST_IMG" $QEMU_IMG convert -O $IMGFMT $backing_option"$TEST_IMG.base" "$TEST_IMG.orig" "$TEST_IMG"
echo "Checking if backing clusters are allocated when they shouldn't" echo "Checking if backing clusters are allocated when they shouldn't"
echo echo
......
...@@ -54,7 +54,7 @@ echo "== Checking that image is clean on shutdown ==" ...@@ -54,7 +54,7 @@ echo "== Checking that image is clean on shutdown =="
IMGOPTS="compat=1.1,lazy_refcounts=on" IMGOPTS="compat=1.1,lazy_refcounts=on"
_make_test_img $size _make_test_img $size
$QEMU_IO -c "write -P 0x5a 0 512" ""$TEST_IMG"" | _filter_qemu_io $QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io
# The dirty bit must not be set # The dirty bit must not be set
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
......
...@@ -64,9 +64,9 @@ function run_qemu() ...@@ -64,9 +64,9 @@ function run_qemu()
size=128M size=128M
_make_test_img $size _make_test_img $size
cp $TEST_IMG $TEST_IMG.orig cp "$TEST_IMG" "$TEST_IMG.orig"
mv $TEST_IMG $TEST_IMG.base mv "$TEST_IMG" "$TEST_IMG.base"
_make_test_img -b $TEST_IMG.base $size _make_test_img -b "$TEST_IMG.base" $size
echo echo
echo === Unknown option === echo === Unknown option ===
...@@ -81,7 +81,7 @@ echo ...@@ -81,7 +81,7 @@ echo
echo === Overriding backing file === echo === Overriding backing file ===
echo echo
echo "info block" | run_qemu -drive file=$TEST_IMG,driver=qcow2,backing.file.filename=$TEST_IMG.orig -nodefaults echo "info block" | run_qemu -drive file="$TEST_IMG",driver=qcow2,backing.file.filename="$TEST_IMG.orig" -nodefaults
echo echo
echo === Enable and disable lazy refcounting on the command line, plus some invalid values === echo === Enable and disable lazy refcounting on the command line, plus some invalid values ===
......
...@@ -163,7 +163,7 @@ echo "=== Testing zero expansion on backed image ===" ...@@ -163,7 +163,7 @@ echo "=== Testing zero expansion on backed image ==="
echo echo
IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M
$QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img _check_test_img
...@@ -174,7 +174,7 @@ echo "=== Testing zero expansion on backed inactive clusters ===" ...@@ -174,7 +174,7 @@ echo "=== Testing zero expansion on backed inactive clusters ==="
echo echo
IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M
$QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io
...@@ -190,7 +190,7 @@ echo "=== Testing zero expansion on backed image with shared L2 table ===" ...@@ -190,7 +190,7 @@ echo "=== Testing zero expansion on backed image with shared L2 table ==="
echo echo
IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
......
...@@ -56,6 +56,17 @@ echo ...@@ -56,6 +56,17 @@ echo
echo "=== Verify pattern 0x00, 66M - 1024M ===" echo "=== Verify pattern 0x00, 66M - 1024M ==="
$QEMU_IO -r -c "read -pP 0x00 66M 958M" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -r -c "read -pP 0x00 66M 958M" "$TEST_IMG" | _filter_qemu_io
echo
echo "=== Verify pattern write, 0xc3 99M-157M ==="
$QEMU_IO -c "write -pP 0xc3 99M 58M" "$TEST_IMG" | _filter_qemu_io
# first verify we didn't write where we should not have
$QEMU_IO -c "read -pP 0xa5 0 33M" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -pP 0x96 33M 33M" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -pP 0x00 66M 33M" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -pP 0x00 157MM 867MM" "$TEST_IMG" | _filter_qemu_io
# now verify what we should have actually written
$QEMU_IO -c "read -pP 0xc3 99M 58M" "$TEST_IMG" | _filter_qemu_io
# success, all done # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full
......
...@@ -11,4 +11,18 @@ read 34603008/34603008 bytes at offset 34603008 ...@@ -11,4 +11,18 @@ read 34603008/34603008 bytes at offset 34603008
=== Verify pattern 0x00, 66M - 1024M === === Verify pattern 0x00, 66M - 1024M ===
read 1004535808/1004535808 bytes at offset 69206016 read 1004535808/1004535808 bytes at offset 69206016
958 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 958 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Verify pattern write, 0xc3 99M-157M ===
wrote 60817408/60817408 bytes at offset 103809024
58 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 34603008/34603008 bytes at offset 0
33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 34603008/34603008 bytes at offset 34603008
33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 34603008/34603008 bytes at offset 69206016
33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 909115392/909115392 bytes at offset 164626432
867 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 60817408/60817408 bytes at offset 103809024
58 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done *** done
...@@ -45,7 +45,7 @@ function do_run_qemu() ...@@ -45,7 +45,7 @@ function do_run_qemu()
function run_qemu() function run_qemu()
{ {
do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g'
} }
size=128M size=128M
......
...@@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 ...@@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0
QMP_VERSION QMP_VERSION
{"return": {}} {"return": {}}
{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} {"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
...@@ -24,7 +24,7 @@ QMP_VERSION ...@@ -24,7 +24,7 @@ QMP_VERSION
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
QMP_VERSION QMP_VERSION
{"return": {}} {"return": {}}
{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} {"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
...@@ -44,7 +44,7 @@ Testing: ...@@ -44,7 +44,7 @@ Testing:
QMP_VERSION QMP_VERSION
{"return": {}} {"return": {}}
{"return": "OK\r\n"} {"return": "OK\r\n"}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
...@@ -64,14 +64,14 @@ Testing: ...@@ -64,14 +64,14 @@ Testing:
QMP_VERSION QMP_VERSION
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"}
{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
......
#!/bin/bash
#
# Test VHDX log replay from an image with a journal that needs to be
# replayed
#
# Copyright (C) 2013 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=jcody@redhat.com
seq=`basename $0`
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt vhdx
_supported_proto generic
_supported_os Linux
# With the log replayed, the pattern 0xa5 extends to 0xc025000
# If the log was not replayed, it would only extend to 0xc000000
#
# This image is a 10G dynamic image, with 4M block size, and 1 unplayed
# data sector in the log
#
# This image was created with qemu-img, however it was verified using
# Hyper-V to properly replay the logs and give the same post-replay
# image as qemu.
_use_sample_img iotest-dirtylog-10G-4M.vhdx.bz2
echo
echo "=== Verify open image read-only fails, due to dirty log ==="
$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | grep -o "Permission denied"
echo "=== Verify open image replays log ==="
$QEMU_IO -c "read -pP 0xa5 0 18M" "$TEST_IMG" | _filter_qemu_io
# success, all done
echo "*** done"
rm -f $seq.full
status=0
QA output created by 070
=== Verify open image read-only fails, due to dirty log ===
Permission denied
=== Verify open image replays log ===
read 18874368/18874368 bytes at offset 0
18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
...@@ -200,7 +200,6 @@ testlist options ...@@ -200,7 +200,6 @@ testlist options
-vhdx) -vhdx)
IMGFMT=vhdx IMGFMT=vhdx
xpand=false xpand=false
IMGFMT_GENERIC=false
;; ;;
-rbd) -rbd)
......
...@@ -28,7 +28,7 @@ function do_is_allocated() { ...@@ -28,7 +28,7 @@ function do_is_allocated() {
} }
function is_allocated() { function is_allocated() {
do_is_allocated "$@" | $QEMU_IO $TEST_IMG | _filter_qemu_io do_is_allocated "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
} }
function do_io() { function do_io() {
...@@ -46,18 +46,18 @@ function do_io() { ...@@ -46,18 +46,18 @@ function do_io() {
} }
function io_pattern() { function io_pattern() {
do_io "$@" | $QEMU_IO $TEST_IMG | _filter_qemu_io do_io "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
} }
function io() { function io() {
local start=$2 local start=$2
local pattern=$(( (start >> 9) % 256 )) local pattern=$(( (start >> 9) % 256 ))
do_io "$@" $pattern | $QEMU_IO $TEST_IMG | _filter_qemu_io do_io "$@" $pattern | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
} }
function io_zero() { function io_zero() {
do_io "$@" 0 | $QEMU_IO $TEST_IMG | _filter_qemu_io do_io "$@" 0 | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
} }
function io_test() { function io_test() {
...@@ -117,8 +117,8 @@ function io_test2() { ...@@ -117,8 +117,8 @@ function io_test2() {
echo === Clusters to be compressed [3] echo === Clusters to be compressed [3]
io_pattern writev $((offset + 8 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 io_pattern writev $((offset + 8 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165
mv $TEST_IMG $TEST_IMG.orig mv "$TEST_IMG" "$TEST_IMG.orig"
$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c $TEST_IMG.orig $TEST_IMG $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c "$TEST_IMG.orig" "$TEST_IMG"
# Write the used clusters # Write the used clusters
echo === Used clusters [1] echo === Used clusters [1]
......
...@@ -111,6 +111,8 @@ _make_test_img() ...@@ -111,6 +111,8 @@ _make_test_img()
local image_size=$* local image_size=$*
local optstr="" local optstr=""
local img_name="" local img_name=""
local use_backing=0
local backing_file=""
if [ -n "$TEST_IMG_FILE" ]; then if [ -n "$TEST_IMG_FILE" ]; then
img_name=$TEST_IMG_FILE img_name=$TEST_IMG_FILE
...@@ -123,7 +125,8 @@ _make_test_img() ...@@ -123,7 +125,8 @@ _make_test_img()
fi fi
if [ "$1" = "-b" ]; then if [ "$1" = "-b" ]; then
extra_img_options="$1 $2" use_backing=1
backing_file=$2
image_size=$3 image_size=$3
fi fi
if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then
...@@ -135,7 +138,13 @@ _make_test_img() ...@@ -135,7 +138,13 @@ _make_test_img()
fi fi
# XXX(hch): have global image options? # XXX(hch): have global image options?
$QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size 2>&1 | \ (
if [ $use_backing = 1 ]; then
$QEMU_IMG create -f $IMGFMT $extra_img_options -b "$backing_file" "$img_name" $image_size 2>&1
else
$QEMU_IMG create -f $IMGFMT $extra_img_options "$img_name" $image_size 2>&1
fi
) | \
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \ -e "s#$IMGFMT#IMGFMT#g" \
...@@ -148,7 +157,10 @@ _make_test_img() ...@@ -148,7 +157,10 @@ _make_test_img()
-e "s# zeroed_grain=\\(on\\|off\\)##g" \ -e "s# zeroed_grain=\\(on\\|off\\)##g" \
-e "s# subformat='[^']*'##g" \ -e "s# subformat='[^']*'##g" \
-e "s# adapter_type='[^']*'##g" \ -e "s# adapter_type='[^']*'##g" \
-e "s# lazy_refcounts=\\(on\\|off\\)##g" -e "s# lazy_refcounts=\\(on\\|off\\)##g" \
-e "s# block_size=[0-9]\\+##g" \
-e "s# block_state_zero=\\(on\\|off\\)##g" \
-e "s# log_size=[0-9]\\+##g"
# Start an NBD server on the image file, which is what we'll be talking to # Start an NBD server on the image file, which is what we'll be talking to
if [ $IMGPROTO = "nbd" ]; then if [ $IMGPROTO = "nbd" ]; then
......
...@@ -75,3 +75,4 @@ ...@@ -75,3 +75,4 @@
067 rw auto 067 rw auto
068 rw auto 068 rw auto
069 rw auto 069 rw auto
070 rw auto
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册