mirror of https://github.com/xqemu/xqemu.git
vpc: Require aligned size in .bdrv_co_create
Perform the rounding to match a CHS geometry only in the legacy code path in .bdrv_co_create_opts. QMP now requires that the user already passes a CHS aligned image size, unless force-size=true is given. CHS alignment is required to make the image compatible with Virtual PC, but not for use with newer Microsoft hypervisors. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
182c883550
commit
1cfeaf386e
113
block/vpc.c
113
block/vpc.c
|
@ -902,6 +902,62 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
|
||||||
|
uint16_t *out_cyls,
|
||||||
|
uint8_t *out_heads,
|
||||||
|
uint8_t *out_secs_per_cyl,
|
||||||
|
int64_t *out_total_sectors,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int64_t total_size = vpc_opts->size;
|
||||||
|
uint16_t cyls = 0;
|
||||||
|
uint8_t heads = 0;
|
||||||
|
uint8_t secs_per_cyl = 0;
|
||||||
|
int64_t total_sectors;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate matching total_size and geometry. Increase the number of
|
||||||
|
* sectors requested until we get enough (or fail). This ensures that
|
||||||
|
* qemu-img convert doesn't truncate images, but rather rounds up.
|
||||||
|
*
|
||||||
|
* If the image size can't be represented by a spec conformant CHS geometry,
|
||||||
|
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
||||||
|
* the image size from the VHD footer to calculate total_sectors.
|
||||||
|
*/
|
||||||
|
if (vpc_opts->force_size) {
|
||||||
|
/* This will force the use of total_size for sector count, below */
|
||||||
|
cyls = VHD_CHS_MAX_C;
|
||||||
|
heads = VHD_CHS_MAX_H;
|
||||||
|
secs_per_cyl = VHD_CHS_MAX_S;
|
||||||
|
} else {
|
||||||
|
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
||||||
|
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
||||||
|
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
|
||||||
|
total_sectors = total_size / BDRV_SECTOR_SIZE;
|
||||||
|
/* Allow a maximum disk size of 2040 GiB */
|
||||||
|
if (total_sectors > VHD_MAX_SECTORS) {
|
||||||
|
error_setg(errp, "Disk size is too large, max size is 2040 GiB");
|
||||||
|
return -EFBIG;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_total_sectors = total_sectors;
|
||||||
|
if (out_cyls) {
|
||||||
|
*out_cyls = cyls;
|
||||||
|
*out_heads = heads;
|
||||||
|
*out_secs_per_cyl = secs_per_cyl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -911,7 +967,6 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||||
|
|
||||||
uint8_t buf[1024];
|
uint8_t buf[1024];
|
||||||
VHDFooter *footer = (VHDFooter *) buf;
|
VHDFooter *footer = (VHDFooter *) buf;
|
||||||
int i;
|
|
||||||
uint16_t cyls = 0;
|
uint16_t cyls = 0;
|
||||||
uint8_t heads = 0;
|
uint8_t heads = 0;
|
||||||
uint8_t secs_per_cyl = 0;
|
uint8_t secs_per_cyl = 0;
|
||||||
|
@ -953,38 +1008,22 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
|
||||||
}
|
}
|
||||||
blk_set_allow_write_beyond_eof(blk, true);
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
|
||||||
/*
|
/* Get geometry and check that it matches the image size*/
|
||||||
* Calculate matching total_size and geometry. Increase the number of
|
ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl,
|
||||||
* sectors requested until we get enough (or fail). This ensures that
|
&total_sectors, errp);
|
||||||
* qemu-img convert doesn't truncate images, but rather rounds up.
|
if (ret < 0) {
|
||||||
*
|
goto out;
|
||||||
* If the image size can't be represented by a spec conformant CHS geometry,
|
|
||||||
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
|
|
||||||
* the image size from the VHD footer to calculate total_sectors.
|
|
||||||
*/
|
|
||||||
if (vpc_opts->force_size) {
|
|
||||||
/* This will force the use of total_size for sector count, below */
|
|
||||||
cyls = VHD_CHS_MAX_C;
|
|
||||||
heads = VHD_CHS_MAX_H;
|
|
||||||
secs_per_cyl = VHD_CHS_MAX_S;
|
|
||||||
} else {
|
|
||||||
total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
|
|
||||||
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
|
|
||||||
calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
|
if (total_size != total_sectors * BDRV_SECTOR_SIZE) {
|
||||||
total_sectors = total_size / BDRV_SECTOR_SIZE;
|
error_setg(errp, "The requested image size cannot be represented in "
|
||||||
/* Allow a maximum disk size of 2040 GiB */
|
"CHS geometry");
|
||||||
if (total_sectors > VHD_MAX_SECTORS) {
|
error_append_hint(errp, "Try size=%llu or force-size=on (the "
|
||||||
error_setg(errp, "Disk size is too large, max size is 2040 GiB");
|
"latter makes the image incompatible with "
|
||||||
ret = -EFBIG;
|
"Virtual PC)",
|
||||||
goto out;
|
total_sectors * BDRV_SECTOR_SIZE);
|
||||||
}
|
ret = -EINVAL;
|
||||||
} else {
|
goto out;
|
||||||
total_sectors = (int64_t)cyls * heads * secs_per_cyl;
|
|
||||||
total_size = total_sectors * BDRV_SECTOR_SIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare the Hard Disk Footer */
|
/* Prepare the Hard Disk Footer */
|
||||||
|
@ -1102,6 +1141,18 @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
|
||||||
create_options->u.vpc.size =
|
create_options->u.vpc.size =
|
||||||
ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
|
ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
|
if (!create_options->u.vpc.force_size) {
|
||||||
|
int64_t total_sectors;
|
||||||
|
ret = calculate_rounded_image_size(&create_options->u.vpc, NULL, NULL,
|
||||||
|
NULL, &total_sectors, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_options->u.vpc.size = total_sectors * BDRV_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Create the vpc image (format layer) */
|
/* Create the vpc image (format layer) */
|
||||||
ret = vpc_co_create(create_options, errp);
|
ret = vpc_co_create(create_options, errp);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue