diff --git a/block/parallels.c b/block/parallels.c index 8d738033c3..15f6cb32ad 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -33,6 +33,9 @@ #define HEADER_MAGIC2 "WithouFreSpacExt" #define HEADER_VERSION 2 +#define DEFAULT_CLUSTER_SIZE 1048576 /* 1 MiB */ + + // always little-endian typedef struct ParallelsHeader { char magic[16]; // "WithoutFreeSpace" @@ -317,12 +320,103 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, return ret; } +static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) +{ + int64_t total_size, cl_size; + uint8_t tmp[BDRV_SECTOR_SIZE]; + Error *local_err = NULL; + BlockDriverState *file; + uint32_t cat_entries, cat_sectors; + ParallelsHeader header; + int ret; + + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, + DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE); + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + + file = NULL; + ret = bdrv_open(&file, filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + return ret; + } + ret = bdrv_truncate(file, 0); + if (ret < 0) { + goto exit; + } + + cat_entries = DIV_ROUND_UP(total_size, cl_size); + cat_sectors = DIV_ROUND_UP(cat_entries * sizeof(uint32_t) + + sizeof(ParallelsHeader), cl_size); + cat_sectors = (cat_sectors * cl_size) >> BDRV_SECTOR_BITS; + + memset(&header, 0, sizeof(header)); + memcpy(header.magic, HEADER_MAGIC2, sizeof(header.magic)); + header.version = cpu_to_le32(HEADER_VERSION); + /* don't care much about geometry, it is not used on image level */ + header.heads = cpu_to_le32(16); + header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE / 16 / 32); + header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS); + header.catalog_entries = cpu_to_le32(cat_entries); + header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE)); + header.data_off = cpu_to_le32(cat_sectors); + + /* write all the data */ + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, &header, sizeof(header)); + + ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + ret = bdrv_write_zeroes(file, 1, cat_sectors - 1, 0); + if (ret < 0) { + goto exit; + } + ret = 0; + +done: + bdrv_unref(file); + return ret; + +exit: + error_setg_errno(errp, -ret, "Failed to create Parallels image"); + goto done; +} + static void parallels_close(BlockDriverState *bs) { BDRVParallelsState *s = bs->opaque; g_free(s->catalog_bitmap); } +static QemuOptsList parallels_create_opts = { + .name = "parallels-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size", + }, + { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Parallels image cluster size", + .def_value_str = stringify(DEFAULT_CLUSTER_SIZE), + }, + { /* end of list */ } + } +}; + static BlockDriver bdrv_parallels = { .format_name = "parallels", .instance_size = sizeof(BDRVParallelsState), @@ -333,6 +427,9 @@ static BlockDriver bdrv_parallels = { .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_readv = parallels_co_readv, .bdrv_co_writev = parallels_co_writev, + + .bdrv_create = parallels_create, + .create_opts = ¶llels_create_opts, }; static void bdrv_parallels_init(void)