@ -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 ) {