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:
Kevin Wolf 2018-03-13 15:37:40 +01:00
parent 182c883550
commit 1cfeaf386e

View File

@ -902,6 +902,62 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
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,
Error **errp)
{
@ -911,7 +967,6 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
uint8_t buf[1024];
VHDFooter *footer = (VHDFooter *) buf;
int i;
uint16_t cyls = 0;
uint8_t heads = 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);
/*
* 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);
}
/* Get geometry and check that it matches the image size*/
ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl,
&total_sectors, errp);
if (ret < 0) {
goto out;
}
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");
ret = -EFBIG;
goto out;
}
} else {
total_sectors = (int64_t)cyls * heads * secs_per_cyl;
total_size = total_sectors * BDRV_SECTOR_SIZE;
if (total_size != total_sectors * BDRV_SECTOR_SIZE) {
error_setg(errp, "The requested image size cannot be represented in "
"CHS geometry");
error_append_hint(errp, "Try size=%llu or force-size=on (the "
"latter makes the image incompatible with "
"Virtual PC)",
total_sectors * BDRV_SECTOR_SIZE);
ret = -EINVAL;
goto out;
}
/* 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 =
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) */
ret = vpc_co_create(create_options, errp);