#include "qemu/osdep.h"
#include "qapi/error.h"
#include "block/block_int.h"
+#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "qemu/module.h"
#include "qemu/option.h"
static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
{
if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
- return 100;
+ return 100;
return 0;
}
checksum = be32_to_cpu(footer->checksum);
footer->checksum = 0;
- if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum)
- fprintf(stderr, "block-vpc: The header checksum of '%s' is "
- "incorrect.\n", bs->filename);
+ if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) {
+ error_setg(errp, "Incorrect header checksum");
+ ret = -EINVAL;
+ goto fail;
+ }
/* Write 'checksum' back to footer, or else will leave it with zero. */
footer->checksum = cpu_to_be32(checksum);
}
qemu_co_mutex_init(&s->lock);
+ qemu_opts_del(opts);
return 0;
fail:
+ qemu_opts_del(opts);
qemu_vfree(s->pagetable);
#ifdef CACHE
g_free(s->pageentry_u8);
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)
{
uint8_t buf[1024];
VHDFooter *footer = (VHDFooter *) buf;
- int i;
uint16_t cyls = 0;
uint8_t heads = 0;
uint8_t secs_per_cyl = 0;
}
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 */
QemuOpts *opts, Error **errp)
{
BlockdevCreateOptions *create_options = NULL;
- QDict *qdict = NULL;
- QObject *qobj;
+ QDict *qdict;
Visitor *v;
BlockDriverState *bs = NULL;
Error *local_err = NULL;
qdict_put_str(qdict, "driver", "vpc");
qdict_put_str(qdict, "file", bs->node_name);
- qobj = qdict_crumple(qdict, errp);
- QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
- if (qdict == NULL) {
+ v = qobject_input_visitor_new_flat_confused(qdict, errp);
+ if (!v) {
ret = -EINVAL;
goto fail;
}
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v);
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);
fail:
- QDECREF(qdict);
+ qobject_unref(qdict);
bdrv_unref(bs);
qapi_free_BlockdevCreateOptions(create_options);
return ret;