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: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
Anthony Liguori 2013-11-13 11:47:44 -08:00
commit deb0f50065
35 changed files with 2935 additions and 300 deletions

View File

@ -640,7 +640,7 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
if (length < 0) {
return length;
}
hint = length >> BDRV_SECTOR_BITS;
hint = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE);
}
bs->total_sectors = hint;
@ -1084,8 +1084,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
snprintf(backing_filename, sizeof(backing_filename),
"%s", filename);
} else if (!realpath(filename, backing_filename)) {
error_setg_errno(errp, errno, "Could not resolve path '%s'", filename);
ret = -errno;
error_setg_errno(errp, errno, "Could not resolve path '%s'", filename);
goto fail;
}

View File

@ -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 += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-y += qed-check.o
block-obj-y += vhdx.o
block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
block-obj-y += parallels.o blkdebug.o blkverify.o
block-obj-y += snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o

View File

@ -1842,7 +1842,8 @@ static BlockDriver bdrv_host_cdrom = {
#endif /* __linux__ */
#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;
Error *local_err = NULL;

216
block/vhdx-endian.c Normal file
View File

@ -0,0 +1,216 @@
/*
* 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);
}

1010
block/vhdx-log.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,9 +6,9 @@
* Authors:
* Jeff Cody <jcody@redhat.com>
*
* This is based on the "VHDX Format Specification v0.95", published 4/12/2012
* This is based on the "VHDX Format Specification v1.00", published 8/25/2012
* by Microsoft:
* https://www.microsoft.com/en-us/download/details.aspx?id=29681
* https://www.microsoft.com/en-us/download/details.aspx?id=34750
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
@ -18,6 +18,11 @@
#ifndef BLOCK_VHDX_H
#define BLOCK_VHDX_H
#define KiB (1 * 1024)
#define MiB (KiB * 1024)
#define GiB (MiB * 1024)
#define TiB ((uint64_t) GiB * 1024)
/* Structures and fields present in the VHDX file */
/* The header section has the following blocks,
@ -30,14 +35,15 @@
* 0.........64KB...........128KB........192KB..........256KB................1MB
*/
#define VHDX_HEADER_BLOCK_SIZE (64*1024)
#define VHDX_HEADER_BLOCK_SIZE (64 * 1024)
#define VHDX_FILE_ID_OFFSET 0
#define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE*1)
#define VHDX_HEADER2_OFFSET (VHDX_HEADER_BLOCK_SIZE*2)
#define VHDX_REGION_TABLE_OFFSET (VHDX_HEADER_BLOCK_SIZE*3)
#define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE * 1)
#define VHDX_HEADER2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 2)
#define VHDX_REGION_TABLE_OFFSET (VHDX_HEADER_BLOCK_SIZE * 3)
#define VHDX_REGION_TABLE2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 4)
#define VHDX_HEADER_SECTION_END (1 * MiB)
/*
* A note on the use of MS-GUID fields. For more details on the GUID,
* please see: https://en.wikipedia.org/wiki/Globally_unique_identifier.
@ -55,10 +61,11 @@
/* These structures are ones that are defined in the VHDX specification
* document */
#define VHDX_FILE_SIGNATURE 0x656C696678646876 /* "vhdxfile" in ASCII */
typedef struct VHDXFileIdentifier {
uint64_t signature; /* "vhdxfile" in ASCII */
uint16_t creator[256]; /* optional; utf-16 string to identify
the vhdx file creator. Diagnotistic
the vhdx file creator. Diagnostic
only */
} VHDXFileIdentifier;
@ -67,7 +74,7 @@ typedef struct VHDXFileIdentifier {
* Microsoft is not just 16 bytes though - it is a structure that is defined,
* so we need to follow it here so that endianness does not trip us up */
typedef struct MSGUID {
typedef struct QEMU_PACKED MSGUID {
uint32_t data1;
uint16_t data2;
uint16_t data3;
@ -77,14 +84,15 @@ typedef struct MSGUID {
#define guid_eq(a, b) \
(memcmp(&(a), &(b), sizeof(MSGUID)) == 0)
#define VHDX_HEADER_SIZE (4*1024) /* although the vhdx_header struct in disk
is only 582 bytes, for purposes of crc
the header is the first 4KB of the 64KB
block */
#define VHDX_HEADER_SIZE (4 * 1024) /* although the vhdx_header struct in disk
is only 582 bytes, for purposes of crc
the header is the first 4KB of the 64KB
block */
/* The full header is 4KB, although the actual header data is much smaller.
* But for the checksum calculation, it is over the entire 4KB structure,
* not just the defined portion of it */
#define VHDX_HEADER_SIGNATURE 0x64616568
typedef struct QEMU_PACKED VHDXHeader {
uint32_t signature; /* "head" in ASCII */
uint32_t checksum; /* CRC-32C hash of the whole header */
@ -92,7 +100,7 @@ typedef struct QEMU_PACKED VHDXHeader {
VHDX file has 2 of these headers,
and only the header with the highest
sequence number is valid */
MSGUID file_write_guid; /* 128 bit unique identifier. Must be
MSGUID file_write_guid; /* 128 bit unique identifier. Must be
updated to new, unique value before
the first modification is made to
file */
@ -114,9 +122,9 @@ typedef struct QEMU_PACKED VHDXHeader {
there is no valid log. If non-zero,
log entries with this guid are
valid. */
uint16_t log_version; /* version of the log format. Mustn't be
zero, unless log_guid is also zero */
uint16_t version; /* version of th evhdx file. Currently,
uint16_t log_version; /* version of the log format. Must be
set to zero */
uint16_t version; /* version of the vhdx file. Currently,
only supported version is "1" */
uint32_t log_length; /* length of the log. Must be multiple
of 1MB */
@ -125,6 +133,7 @@ typedef struct QEMU_PACKED VHDXHeader {
} VHDXHeader;
/* Header for the region table block */
#define VHDX_REGION_SIGNATURE 0x69676572 /* "regi" in ASCII */
typedef struct QEMU_PACKED VHDXRegionTableHeader {
uint32_t signature; /* "regi" in ASCII */
uint32_t checksum; /* CRC-32C hash of the 64KB table */
@ -151,7 +160,10 @@ typedef struct QEMU_PACKED VHDXRegionTableEntry {
/* ---- LOG ENTRY STRUCTURES ---- */
#define VHDX_LOG_MIN_SIZE (1024 * 1024)
#define VHDX_LOG_SECTOR_SIZE 4096
#define VHDX_LOG_HDR_SIZE 64
#define VHDX_LOG_SIGNATURE 0x65676f6c
typedef struct QEMU_PACKED VHDXLogEntryHeader {
uint32_t signature; /* "loge" in ASCII */
uint32_t checksum; /* CRC-32C hash of the 64KB table */
@ -174,7 +186,8 @@ typedef struct QEMU_PACKED VHDXLogEntryHeader {
} VHDXLogEntryHeader;
#define VHDX_LOG_DESC_SIZE 32
#define VHDX_LOG_DESC_SIGNATURE 0x63736564
#define VHDX_LOG_ZERO_SIGNATURE 0x6f72657a
typedef struct QEMU_PACKED VHDXLogDescriptor {
uint32_t signature; /* "zero" or "desc" in ASCII */
union {
@ -194,6 +207,7 @@ typedef struct QEMU_PACKED VHDXLogDescriptor {
vhdx_log_entry_header */
} VHDXLogDescriptor;
#define VHDX_LOG_DATA_SIGNATURE 0x61746164
typedef struct QEMU_PACKED VHDXLogDataSector {
uint32_t data_signature; /* "data" in ASCII */
uint32_t sequence_high; /* 4 MSB of 8 byte sequence_number */
@ -212,19 +226,19 @@ typedef struct QEMU_PACKED VHDXLogDataSector {
#define PAYLOAD_BLOCK_UNDEFINED 1
#define PAYLOAD_BLOCK_ZERO 2
#define PAYLOAD_BLOCK_UNMAPPED 5
#define PAYLOAD_BLOCK_FULL_PRESENT 6
#define PAYLOAD_BLOCK_FULLY_PRESENT 6
#define PAYLOAD_BLOCK_PARTIALLY_PRESENT 7
#define SB_BLOCK_NOT_PRESENT 0
#define SB_BLOCK_PRESENT 6
/* per the spec */
#define VHDX_MAX_SECTORS_PER_BLOCK (1<<23)
#define VHDX_MAX_SECTORS_PER_BLOCK (1 << 23)
/* upper 44 bits are the file offset in 1MB units lower 3 bits are the state
other bits are reserved */
#define VHDX_BAT_STATE_BIT_MASK 0x07
#define VHDX_BAT_FILE_OFF_BITS (64-44)
#define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000 /* upper 44 bits */
typedef uint64_t VHDXBatEntry;
/* ---- METADATA REGION STRUCTURES ---- */
@ -233,6 +247,7 @@ typedef uint64_t VHDXBatEntry;
#define VHDX_METADATA_MAX_ENTRIES 2047 /* not including the header */
#define VHDX_METADATA_TABLE_MAX_SIZE \
(VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1))
#define VHDX_METADATA_SIGNATURE 0x617461646174656D /* "metadata" in ASCII */
typedef struct QEMU_PACKED VHDXMetadataTableHeader {
uint64_t signature; /* "metadata" in ASCII */
uint16_t reserved;
@ -252,8 +267,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
metadata region */
/* note: if length = 0, so is offset */
uint32_t length; /* length of metadata. <= 1MB. */
uint32_t data_bits; /* least-significant 3 bits are flags, the
rest are reserved (see above) */
uint32_t data_bits; /* least-significant 3 bits are flags,
the rest are reserved (see above) */
uint32_t reserved2;
} VHDXMetadataTableEntry;
@ -262,13 +277,16 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry {
If set indicates a fixed
size VHDX file */
#define VHDX_PARAMS_HAS_PARENT 0x02 /* has parent / backing file */
#define VHDX_BLOCK_SIZE_MIN (1 * MiB)
#define VHDX_BLOCK_SIZE_MAX (256 * MiB)
typedef struct QEMU_PACKED VHDXFileParameters {
uint32_t block_size; /* size of each payload block, always
power of 2, <= 256MB and >= 1MB. */
uint32_t data_bits; /* least-significant 2 bits are flags, the rest
are reserved (see above) */
uint32_t data_bits; /* least-significant 2 bits are flags,
the rest are reserved (see above) */
} VHDXFileParameters;
#define VHDX_MAX_IMAGE_SIZE ((uint64_t) 64 * TiB)
typedef struct QEMU_PACKED VHDXVirtualDiskSize {
uint64_t virtual_disk_size; /* Size of the virtual disk, in bytes.
Must be multiple of the sector size,
@ -276,7 +294,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskSize {
} VHDXVirtualDiskSize;
typedef struct QEMU_PACKED VHDXPage83Data {
MSGUID page_83_data[16]; /* unique id for scsi devices that
MSGUID page_83_data; /* unique id for scsi devices that
support page 0x83 */
} VHDXPage83Data;
@ -291,7 +309,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskPhysicalSectorSize {
} VHDXVirtualDiskPhysicalSectorSize;
typedef struct QEMU_PACKED VHDXParentLocatorHeader {
MSGUID locator_type[16]; /* type of the parent virtual disk. */
MSGUID locator_type; /* type of the parent virtual disk. */
uint16_t reserved;
uint16_t key_value_count; /* number of key/value pairs for this
locator */
@ -308,18 +326,122 @@ typedef struct QEMU_PACKED VHDXParentLocatorEntry {
/* ----- END VHDX SPECIFICATION STRUCTURES ---- */
typedef struct VHDXMetadataEntries {
VHDXMetadataTableEntry file_parameters_entry;
VHDXMetadataTableEntry virtual_disk_size_entry;
VHDXMetadataTableEntry page83_data_entry;
VHDXMetadataTableEntry logical_sector_size_entry;
VHDXMetadataTableEntry phys_sector_size_entry;
VHDXMetadataTableEntry parent_locator_entry;
uint16_t present;
} VHDXMetadataEntries;
typedef struct 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,
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);
static void leguid_to_cpus(MSGUID *guid)
int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
void *data, uint32_t length, uint64_t offset);
static inline void leguid_to_cpus(MSGUID *guid)
{
le32_to_cpus(&guid->data1);
le16_to_cpus(&guid->data2);
le16_to_cpus(&guid->data3);
}
static inline void cpu_to_leguids(MSGUID *guid)
{
cpu_to_le32s(&guid->data1);
cpu_to_le16s(&guid->data2);
cpu_to_le16s(&guid->data3);
}
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

View File

@ -211,6 +211,15 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
bs->total_sectors = (int64_t)
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 */
if (bs->total_sectors >= 65535LL * 255 * 255) {
ret = -EFBIG;

View File

@ -341,7 +341,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
qemu_opts_absorb_qdict(opts, bs_opts, &error);
if (error_is_set(&error)) {
error_propagate(errp, error);
return NULL;
goto early_err;
}
if (id) {
@ -361,7 +361,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
error_setg(errp, "invalid discard option");
return NULL;
goto early_err;
}
}
@ -383,7 +383,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
/* this is the default */
} else {
error_setg(errp, "invalid aio option");
return NULL;
goto early_err;
}
}
#endif
@ -393,13 +393,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
error_printf("Supported formats:");
bdrv_iterate_format(bdrv_format_print, NULL);
error_printf("\n");
return NULL;
goto early_err;
}
drv = bdrv_find_format(buf);
if (!drv) {
error_setg(errp, "'%s' invalid format", buf);
return NULL;
goto early_err;
}
}
@ -435,20 +435,20 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if (!check_throttle_config(&cfg, &error)) {
error_propagate(errp, error);
return NULL;
goto early_err;
}
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
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);
if (error_is_set(&error)) {
error_propagate(errp, error);
return NULL;
goto early_err;
}
}
@ -456,13 +456,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
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);
if (error_is_set(&error)) {
error_propagate(errp, error);
return NULL;
goto early_err;
}
}
@ -491,6 +491,8 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
if (has_driver_specific_opts) {
file = NULL;
} else {
QDECREF(bs_opts);
qemu_opts_del(opts);
return dinfo;
}
}
@ -529,12 +531,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
return dinfo;
err:
qemu_opts_del(opts);
QDECREF(bs_opts);
bdrv_unref(dinfo->bdrv);
g_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next);
g_free(dinfo);
early_err:
QDECREF(bs_opts);
qemu_opts_del(opts);
return NULL;
}

24
configure vendored
View File

@ -260,6 +260,7 @@ gtk=""
gtkabi="2.0"
tpm="no"
libssh2=""
vhdx=""
# parse CC options first
for opt do
@ -985,6 +986,10 @@ for opt do
;;
--enable-libssh2) libssh2="yes"
;;
--enable-vhdx) vhdx="yes"
;;
--disable-vhdx) vhdx="no"
;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@ -1217,6 +1222,8 @@ echo " --gcov=GCOV use specified gcov [$gcov_tool]"
echo " --enable-tpm enable TPM support"
echo " --disable-libssh2 disable ssh block device support"
echo " --enable-libssh2 enable ssh block device support"
echo " --disable-vhdx disables support for the Microsoft VHDX image format"
echo " --enable-vhdx enable support for the Microsoft VHDX image format"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@ -2017,6 +2024,18 @@ EOF
fi
fi
if test "$vhdx" = "yes" ; then
if test "$uuid" = "no" ; then
error_exit "uuid required for VHDX support"
fi
elif test "$vhdx" != "no" ; then
if test "$uuid" = "yes" ; then
vhdx=yes
else
vhdx=no
fi
fi
##########################################
# xfsctl() probe, used for raw-posix
if test "$xfs" != "no" ; then
@ -3760,6 +3779,7 @@ echo "TPM support $tpm"
echo "libssh2 support $libssh2"
echo "TPM passthrough $tpm_passthrough"
echo "QOM debugging $qom_cast_debug"
echo "vhdx $vhdx"
if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@ -4152,6 +4172,10 @@ if test "$virtio_blk_data_plane" = "yes" ; then
echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak
fi
if test "$vhdx" = "yes" ; then
echo "CONFIG_VHDX=y" >> $config_host_mak
fi
# USB host support
if test "$libusb" = "yes"; then
echo "HOST_USB=libusb legacy" >> $config_host_mak

View File

@ -227,7 +227,7 @@
##
# @ImageInfoSpecificVmdk:
#
# @create_type: The create type of VMDK image
# @create-type: The create type of VMDK image
#
# @cid: Content id of image
#

View File

@ -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/fw_cfg-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)
gcov-files-i386-y += i386-softmmu/hw/mc146818rtc.c
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)
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/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
# QTest rules

59
tests/blockdev-test.c Normal file
View File

@ -0,0 +1,59 @@
/*
* 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();
}

View File

@ -41,12 +41,12 @@ static void test_a_boot_order(const char *machine,
qtest_start(args);
actual = read_boot_order();
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
* 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();
g_assert_cmphex(actual, ==, expected_reboot);
qtest_quit(global_qtest);

View File

@ -290,10 +290,12 @@ static void test_media_insert(void)
/* Insert media in drive. DSKCHK should not be reset until a step pulse
* is sent. */
qmp("{'execute':'change', 'arguments':{ 'device':'floppy0', "
"'target': '%s' }}", test_image);
qmp(""); /* ignore event (FIXME open -> open transition?!) */
qmp(""); /* ignore event */
qmp_discard_response("{'execute':'change', 'arguments':{"
" 'device':'floppy0', 'target': '%s' }}",
test_image);
qmp_discard_response(""); /* ignore event
(FIXME open -> open transition?!) */
qmp_discard_response(""); /* ignore event */
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);
@ -322,8 +324,9 @@ static void test_media_change(void)
/* Eject the floppy and check that DSKCHG is set. Reading it out doesn't
* reset the bit. */
qmp("{'execute':'eject', 'arguments':{ 'device':'floppy0' }}");
qmp(""); /* ignore event */
qmp_discard_response("{'execute':'eject', 'arguments':{"
" 'device':'floppy0' }}");
qmp_discard_response(""); /* ignore event */
dir = inb(FLOPPY_BASE + reg_dir);
assert_bit_set(dir, DSKCHG);

View File

@ -460,8 +460,9 @@ static void test_flush(void)
tmp_path);
/* Delay the completion of the flush request until we explicitly do it */
qmp("{'execute':'human-monitor-command', 'arguments': { "
"'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
" 'command-line':"
" 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
/* FLUSH CACHE command on device 0*/
outb(IDE_BASE + reg_device, 0);
@ -473,8 +474,9 @@ static void test_flush(void)
assert_bit_clear(data, DF | ERR | DRQ);
/* Complete the command */
qmp("{'execute':'human-monitor-command', 'arguments': { "
"'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }");
qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
" 'command-line':"
" 'qemu-io ide0-hd0 \"resume A\"'} }");
/* Check registers */
data = inb(IDE_BASE + reg_device);

View File

@ -30,6 +30,8 @@
#include "qemu/compiler.h"
#include "qemu/osdep.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
#define MAX_IRQ 256
@ -151,8 +153,8 @@ QTestState *qtest_init(const char *extra_args)
}
/* Read the QMP greeting and then do the handshake */
qtest_qmp(s, "");
qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }");
qtest_qmp_discard_response(s, "");
qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }");
if (getenv("QTEST_STOP")) {
kill(qtest_qemu_pid(s), SIGSTOP);
@ -291,16 +293,38 @@ redo:
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)
{
bool has_reply = false;
int nesting = 0;
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)
{
QMPResponseParser qmp;
/* Send QMP request */
socket_sendf(s->qmp_fd, fmt, ap);
/* Receive reply */
while (!has_reply || nesting > 0) {
qmp.response = NULL;
json_message_parser_init(&qmp.parser, qmp_response);
while (!qmp.response) {
ssize_t len;
char c;
@ -314,25 +338,39 @@ void qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
exit(1);
}
switch (c) {
case '{':
nesting++;
has_reply = true;
break;
case '}':
nesting--;
break;
}
json_message_parser_feed(&qmp.parser, &c, 1);
}
json_message_parser_destroy(&qmp.parser);
return qmp.response;
}
void qtest_qmp(QTestState *s, const char *fmt, ...)
QDict *qtest_qmp(QTestState *s, const char *fmt, ...)
{
va_list ap;
QDict *response;
va_start(ap, fmt);
qtest_qmpv(s, fmt, ap);
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_discard_response(QTestState *s, const char *fmt, ...)
{
va_list ap;
QDict *response;
va_start(ap, fmt);
response = qtest_qmpv(s, fmt, ap);
va_end(ap);
QDECREF(response);
}
const char *qtest_get_arch(void)

View File

@ -22,6 +22,7 @@
#include <stdbool.h>
#include <stdarg.h>
#include <sys/types.h>
#include "qapi/qmp/qdict.h"
typedef struct QTestState QTestState;
@ -43,14 +44,33 @@ QTestState *qtest_init(const char *extra_args);
*/
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:
* @s: #QTestState instance to operate on.
* @fmt...: QMP message to send to qemu
*
* Sends a QMP message to QEMU
* Sends a QMP message to QEMU and returns the response.
*/
void qtest_qmp(QTestState *s, const char *fmt, ...);
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_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap);
/**
* qtest_qmpv:
@ -58,9 +78,9 @@ void qtest_qmp(QTestState *s, const char *fmt, ...);
* @fmt: QMP message to send to QEMU
* @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:
@ -334,14 +354,31 @@ static inline void qtest_end(void)
* qmp:
* @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 void qmp(const char *fmt, ...)
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_discard_response(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
qtest_qmpv(global_qtest, fmt, ap);
qtest_qmpv_discard_response(global_qtest, fmt, ap);
va_end(ap);
}

81
tests/qdev-monitor-test.c Normal file
View File

@ -0,0 +1,81 @@
/*
* 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();
}

View File

@ -66,7 +66,7 @@ echo "Creating test image with backing file"
echo
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

View File

@ -90,12 +90,12 @@ mv "$TEST_IMG" "$TEST_IMG.orig"
# Test the conversion twice: One test with the old-style -B option and another
# 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 Testing conversion with $backing_option | _filter_testdir | _filter_imgfmt
echo Testing conversion with $backing_option$TEST_IMG.base | _filter_testdir | _filter_imgfmt
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

View File

@ -54,7 +54,7 @@ echo "== Checking that image is clean on shutdown =="
IMGOPTS="compat=1.1,lazy_refcounts=on"
_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
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features

View File

@ -64,9 +64,9 @@ function run_qemu()
size=128M
_make_test_img $size
cp $TEST_IMG $TEST_IMG.orig
mv $TEST_IMG $TEST_IMG.base
_make_test_img -b $TEST_IMG.base $size
cp "$TEST_IMG" "$TEST_IMG.orig"
mv "$TEST_IMG" "$TEST_IMG.base"
_make_test_img -b "$TEST_IMG.base" $size
echo
echo === Unknown option ===
@ -81,7 +81,7 @@ echo
echo === Overriding backing file ===
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 === Enable and disable lazy refcounting on the command line, plus some invalid values ===

View File

@ -163,7 +163,7 @@ echo "=== Testing zero expansion on backed image ==="
echo
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
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_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
@ -174,7 +174,7 @@ echo "=== Testing zero expansion on backed inactive clusters ==="
echo
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
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_IMG snapshot -c foo "$TEST_IMG"
$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 ==="
echo
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
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_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"

View File

@ -56,6 +56,17 @@ echo
echo "=== Verify pattern 0x00, 66M - 1024M ==="
$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
echo "*** done"
rm -f $seq.full

View File

@ -11,4 +11,18 @@ read 34603008/34603008 bytes at offset 34603008
=== Verify pattern 0x00, 66M - 1024M ===
read 1004535808/1004535808 bytes at offset 69206016
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

View File

@ -45,7 +45,7 @@ function do_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

View File

@ -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
QMP_VERSION
{"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": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}}
@ -24,7 +24,7 @@ QMP_VERSION
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk
QMP_VERSION
{"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": {}}
@ -44,7 +44,7 @@ Testing:
QMP_VERSION
{"return": {}}
{"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": {}}
@ -64,14 +64,14 @@ Testing:
QMP_VERSION
{"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": {}}
{"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": "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": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}

67
tests/qemu-iotests/070 Executable file
View File

@ -0,0 +1,67 @@
#!/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

View File

@ -0,0 +1,8 @@
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

View File

@ -200,7 +200,6 @@ testlist options
-vhdx)
IMGFMT=vhdx
xpand=false
IMGFMT_GENERIC=false
;;
-rbd)

View File

@ -28,7 +28,7 @@ function do_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() {
@ -46,18 +46,18 @@ function do_io() {
}
function io_pattern() {
do_io "$@" | $QEMU_IO $TEST_IMG | _filter_qemu_io
do_io "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
}
function io() {
local start=$2
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() {
do_io "$@" 0 | $QEMU_IO $TEST_IMG | _filter_qemu_io
do_io "$@" 0 | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
}
function io_test() {
@ -117,8 +117,8 @@ function io_test2() {
echo === Clusters to be compressed [3]
io_pattern writev $((offset + 8 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165
mv $TEST_IMG $TEST_IMG.orig
$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c $TEST_IMG.orig $TEST_IMG
mv "$TEST_IMG" "$TEST_IMG.orig"
$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c "$TEST_IMG.orig" "$TEST_IMG"
# Write the used clusters
echo === Used clusters [1]

View File

@ -111,6 +111,8 @@ _make_test_img()
local image_size=$*
local optstr=""
local img_name=""
local use_backing=0
local backing_file=""
if [ -n "$TEST_IMG_FILE" ]; then
img_name=$TEST_IMG_FILE
@ -123,7 +125,8 @@ _make_test_img()
fi
if [ "$1" = "-b" ]; then
extra_img_options="$1 $2"
use_backing=1
backing_file=$2
image_size=$3
fi
if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then
@ -135,7 +138,13 @@ _make_test_img()
fi
# 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" \
-e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \
@ -148,7 +157,10 @@ _make_test_img()
-e "s# zeroed_grain=\\(on\\|off\\)##g" \
-e "s# subformat='[^']*'##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
if [ $IMGPROTO = "nbd" ]; then

View File

@ -75,3 +75,4 @@
067 rw auto
068 rw auto
069 rw auto
070 rw auto