aboutsummaryrefslogtreecommitdiff
path: root/block/vhdx.c
diff options
context:
space:
mode:
authorJeff Cody <jcody@redhat.com>2014-08-06 15:54:58 -0400
committerKevin Wolf <kwolf@redhat.com>2014-08-15 15:07:14 +0200
commit4f75b52a07e0a0e71c1c81d0cb0ddf9c52c8e1bc (patch)
tree4725d765df96358203dcaed03f633e465685294a /block/vhdx.c
parent349592e0b9112171500e940dd921bb96cfc496d2 (diff)
block: VHDX endian fixes
This patch contains several changes for endian conversion fixes for VHDX, particularly for big-endian machines (multibyte values in VHDX are all on disk in LE format). Tests were done with existing qemu-iotests on an IBM POWER7 (8406-71Y). This includes sample images created by Hyper-V, both with dirty logs and without. In addition, VHDX image files created (and written to) on a BE machine were tested on a LE machine, and vice-versa. Reported-by: Markus Armburster <armbru@redhat.com> Reported-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Jeff Cody <jcody@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'block/vhdx.c')
-rw-r--r--block/vhdx.c89
1 files changed, 53 insertions, 36 deletions
diff --git a/block/vhdx.c b/block/vhdx.c
index fedcf9f9ca..febce21f82 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -135,10 +135,8 @@ typedef struct VHDXSectorInfo {
* buf: buffer pointer
* size: size of buffer (must be > crc_offset+4)
*
- * Note: The resulting checksum is in the CPU endianness, not necessarily
- * in the file format endianness (LE). Any header export to disk should
- * make sure that vhdx_header_le_export() is used to convert to the
- * correct endianness
+ * Note: The buffer should have all multi-byte data in little-endian format,
+ * and the resulting checksum is in little endian format.
*/
uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset)
{
@@ -149,6 +147,7 @@ uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset)
memset(buf + crc_offset, 0, sizeof(crc));
crc = crc32c(0xffffffff, buf, size);
+ cpu_to_le32s(&crc);
memcpy(buf + crc_offset, &crc, sizeof(crc));
return crc;
@@ -300,7 +299,7 @@ static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr,
{
uint8_t *buffer = NULL;
int ret;
- VHDXHeader header_le;
+ VHDXHeader *header_le;
assert(bs_file != NULL);
assert(hdr != NULL);
@@ -321,11 +320,12 @@ static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr,
}
/* overwrite the actual VHDXHeader portion */
- memcpy(buffer, hdr, sizeof(VHDXHeader));
- hdr->checksum = vhdx_update_checksum(buffer, VHDX_HEADER_SIZE,
- offsetof(VHDXHeader, checksum));
- vhdx_header_le_export(hdr, &header_le);
- ret = bdrv_pwrite_sync(bs_file, offset, &header_le, sizeof(VHDXHeader));
+ header_le = (VHDXHeader *)buffer;
+ memcpy(header_le, hdr, sizeof(VHDXHeader));
+ vhdx_header_le_export(hdr, header_le);
+ vhdx_update_checksum(buffer, VHDX_HEADER_SIZE,
+ offsetof(VHDXHeader, checksum));
+ ret = bdrv_pwrite_sync(bs_file, offset, header_le, sizeof(VHDXHeader));
exit:
qemu_vfree(buffer);
@@ -432,13 +432,14 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
}
/* copy over just the relevant portion that we need */
memcpy(header1, buffer, sizeof(VHDXHeader));
- vhdx_header_le_import(header1);
- if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4) &&
- !memcmp(&header1->signature, "head", 4) &&
- header1->version == 1) {
- h1_seq = header1->sequence_number;
- h1_valid = true;
+ if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4)) {
+ vhdx_header_le_import(header1);
+ if (header1->signature == VHDX_HEADER_SIGNATURE &&
+ header1->version == 1) {
+ h1_seq = header1->sequence_number;
+ h1_valid = true;
+ }
}
ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, VHDX_HEADER_SIZE);
@@ -447,13 +448,14 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
}
/* copy over just the relevant portion that we need */
memcpy(header2, buffer, sizeof(VHDXHeader));
- vhdx_header_le_import(header2);
- if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4) &&
- !memcmp(&header2->signature, "head", 4) &&
- header2->version == 1) {
- h2_seq = header2->sequence_number;
- h2_valid = true;
+ if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4)) {
+ vhdx_header_le_import(header2);
+ if (header2->signature == VHDX_HEADER_SIGNATURE &&
+ header2->version == 1) {
+ h2_seq = header2->sequence_number;
+ h2_valid = true;
+ }
}
/* If there is only 1 valid header (or no valid headers), we
@@ -519,15 +521,21 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
goto fail;
}
memcpy(&s->rt, buffer, sizeof(s->rt));
- vhdx_region_header_le_import(&s->rt);
offset += sizeof(s->rt);
- if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4) ||
- memcmp(&s->rt.signature, "regi", 4)) {
+ if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ vhdx_region_header_le_import(&s->rt);
+
+ if (s->rt.signature != VHDX_REGION_SIGNATURE) {
ret = -EINVAL;
goto fail;
}
+
/* Per spec, maximum region table entry count is 2047 */
if (s->rt.entry_count > 2047) {
ret = -EINVAL;
@@ -630,7 +638,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
vhdx_metadata_header_le_import(&s->metadata_hdr);
- if (memcmp(&s->metadata_hdr.signature, "metadata", 8)) {
+ if (s->metadata_hdr.signature != VHDX_METADATA_SIGNATURE) {
ret = -EINVAL;
goto exit;
}
@@ -951,7 +959,6 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
/* s->bat is freed in vhdx_close() */
s->bat = qemu_blockalign(bs, s->bat_rt.length);
-
ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length);
if (ret < 0) {
goto fail;
@@ -1540,7 +1547,8 @@ exit:
*/
static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
uint64_t image_size, VHDXImageType type,
- bool use_zero_blocks, VHDXRegionTableEntry *rt_bat)
+ bool use_zero_blocks, uint64_t file_offset,
+ uint32_t length)
{
int ret = 0;
uint64_t data_file_offset;
@@ -1555,7 +1563,7 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
/* this gives a data start after BAT/bitmap entries, and well
* past any metadata entries (with a 4 MB buffer for future
* expansion */
- data_file_offset = rt_bat->file_offset + rt_bat->length + 5 * MiB;
+ data_file_offset = file_offset + length + 5 * MiB;
total_sectors = image_size >> s->logical_sector_size_bits;
if (type == VHDX_TYPE_DYNAMIC) {
@@ -1579,7 +1587,7 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
use_zero_blocks ||
bdrv_has_zero_init(bs) == 0) {
/* for a fixed file, the default BAT entry is not zero */
- s->bat = g_malloc0(rt_bat->length);
+ s->bat = g_malloc0(length);
block_state = type == VHDX_TYPE_FIXED ? PAYLOAD_BLOCK_FULLY_PRESENT :
PAYLOAD_BLOCK_NOT_PRESENT;
block_state = use_zero_blocks ? PAYLOAD_BLOCK_ZERO : block_state;
@@ -1594,7 +1602,7 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s,
cpu_to_le64s(&s->bat[sinfo.bat_idx]);
sector_num += s->sectors_per_block;
}
- ret = bdrv_pwrite(bs, rt_bat->file_offset, s->bat, rt_bat->length);
+ ret = bdrv_pwrite(bs, file_offset, s->bat, length);
if (ret < 0) {
goto exit;
}
@@ -1626,6 +1634,8 @@ static int vhdx_create_new_region_table(BlockDriverState *bs,
int ret = 0;
uint32_t offset = 0;
void *buffer = NULL;
+ uint64_t bat_file_offset;
+ uint32_t bat_length;
BDRVVHDXState *s = NULL;
VHDXRegionTableHeader *region_table;
VHDXRegionTableEntry *rt_bat;
@@ -1674,19 +1684,26 @@ static int vhdx_create_new_region_table(BlockDriverState *bs,
rt_metadata->length = 1 * MiB; /* min size, and more than enough */
*metadata_offset = rt_metadata->file_offset;
+ bat_file_offset = rt_bat->file_offset;
+ bat_length = rt_bat->length;
+
+ vhdx_region_header_le_export(region_table);
+ vhdx_region_entry_le_export(rt_bat);
+ vhdx_region_entry_le_export(rt_metadata);
+
vhdx_update_checksum(buffer, VHDX_HEADER_BLOCK_SIZE,
offsetof(VHDXRegionTableHeader, checksum));
/* The region table gives us the data we need to create the BAT,
* so do that now */
- ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks, rt_bat);
+ ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks,
+ bat_file_offset, bat_length);
+ if (ret < 0) {
+ goto exit;
+ }
/* Now write out the region headers to disk */
- vhdx_region_header_le_export(region_table);
- vhdx_region_entry_le_export(rt_bat);
- vhdx_region_entry_le_export(rt_metadata);
-
ret = bdrv_pwrite(bs, VHDX_REGION_TABLE_OFFSET, buffer,
VHDX_HEADER_BLOCK_SIZE);
if (ret < 0) {