mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-30 13:31:52 -06:00
vmdk: Implement .bdrv_co_create callback
This makes VMDK support blockdev-create. The implementation reuses the image creation code in vmdk_co_create_opts which now acceptes a callback pointer to "retrieve" BlockBackend pointers from the caller. This way we separate the logic between file/extent acquisition and initialization. The QAPI command parameters are mostly the same as the old create_opts except the dropped legacy @compat6 switch, which is redundant with @hwversion. Signed-off-by: Fam Zheng <famz@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
5be28490ca
commit
3015372dd0
3 changed files with 401 additions and 135 deletions
450
block/vmdk.c
450
block/vmdk.c
|
@ -1932,33 +1932,56 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
|
||||||
return VMDK_OK;
|
return VMDK_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts,
|
/*
|
||||||
Error **errp)
|
* idx == 0: get or create the descriptor file (also the image file if in a
|
||||||
|
* non-split format.
|
||||||
|
* idx >= 1: get the n-th extent if in a split subformat
|
||||||
|
*/
|
||||||
|
typedef BlockBackend *(*vmdk_create_extent_fn)(int64_t size,
|
||||||
|
int idx,
|
||||||
|
bool flat,
|
||||||
|
bool split,
|
||||||
|
bool compress,
|
||||||
|
bool zeroed_grain,
|
||||||
|
void *opaque,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
|
static void vmdk_desc_add_extent(GString *desc,
|
||||||
|
const char *extent_line_fmt,
|
||||||
|
int64_t size, const char *filename)
|
||||||
{
|
{
|
||||||
int idx = 0;
|
char *basename = g_path_get_basename(filename);
|
||||||
BlockBackend *new_blk = NULL;
|
|
||||||
|
g_string_append_printf(desc, extent_line_fmt,
|
||||||
|
DIV_ROUND_UP(size, BDRV_SECTOR_SIZE), basename);
|
||||||
|
g_free(basename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn vmdk_co_do_create(int64_t size,
|
||||||
|
BlockdevVmdkSubformat subformat,
|
||||||
|
BlockdevVmdkAdapterType adapter_type,
|
||||||
|
const char *backing_file,
|
||||||
|
const char *hw_version,
|
||||||
|
bool compat6,
|
||||||
|
bool zeroed_grain,
|
||||||
|
vmdk_create_extent_fn extent_fn,
|
||||||
|
void *opaque,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int extent_idx;
|
||||||
|
BlockBackend *blk = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
char *desc = NULL;
|
char *desc = NULL;
|
||||||
int64_t total_size = 0, filesize;
|
|
||||||
char *adapter_type = NULL;
|
|
||||||
char *backing_file = NULL;
|
|
||||||
char *hw_version = NULL;
|
|
||||||
char *fmt = NULL;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
bool flat, split, compress;
|
bool flat, split, compress;
|
||||||
GString *ext_desc_lines;
|
GString *ext_desc_lines;
|
||||||
char *path = g_malloc0(PATH_MAX);
|
|
||||||
char *prefix = g_malloc0(PATH_MAX);
|
|
||||||
char *postfix = g_malloc0(PATH_MAX);
|
|
||||||
char *desc_line = g_malloc0(BUF_SIZE);
|
|
||||||
char *ext_filename = g_malloc0(PATH_MAX);
|
|
||||||
char *desc_filename = g_malloc0(PATH_MAX);
|
|
||||||
const int64_t split_size = 0x80000000; /* VMDK has constant split size */
|
const int64_t split_size = 0x80000000; /* VMDK has constant split size */
|
||||||
const char *desc_extent_line;
|
int64_t extent_size;
|
||||||
|
int64_t created_size = 0;
|
||||||
|
const char *extent_line_fmt;
|
||||||
char *parent_desc_line = g_malloc0(BUF_SIZE);
|
char *parent_desc_line = g_malloc0(BUF_SIZE);
|
||||||
uint32_t parent_cid = 0xffffffff;
|
uint32_t parent_cid = 0xffffffff;
|
||||||
uint32_t number_heads = 16;
|
uint32_t number_heads = 16;
|
||||||
bool zeroed_grain = false;
|
|
||||||
uint32_t desc_offset = 0, desc_len;
|
uint32_t desc_offset = 0, desc_len;
|
||||||
const char desc_template[] =
|
const char desc_template[] =
|
||||||
"# Disk DescriptorFile\n"
|
"# Disk DescriptorFile\n"
|
||||||
|
@ -1982,71 +2005,35 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
|
||||||
|
|
||||||
ext_desc_lines = g_string_new(NULL);
|
ext_desc_lines = g_string_new(NULL);
|
||||||
|
|
||||||
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
/* Read out options */
|
/* Read out options */
|
||||||
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
if (compat6) {
|
||||||
BDRV_SECTOR_SIZE);
|
if (hw_version) {
|
||||||
adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE);
|
|
||||||
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
|
||||||
hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION);
|
|
||||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false)) {
|
|
||||||
if (strcmp(hw_version, "undefined")) {
|
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"compat6 cannot be enabled with hwversion set");
|
"compat6 cannot be enabled with hwversion set");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
g_free(hw_version);
|
hw_version = "6";
|
||||||
hw_version = g_strdup("6");
|
|
||||||
}
|
}
|
||||||
if (strcmp(hw_version, "undefined") == 0) {
|
if (!hw_version) {
|
||||||
g_free(hw_version);
|
hw_version = "4";
|
||||||
hw_version = g_strdup("4");
|
|
||||||
}
|
|
||||||
fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
|
||||||
if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false)) {
|
|
||||||
zeroed_grain = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!adapter_type) {
|
if (adapter_type != BLOCKDEV_VMDK_ADAPTER_TYPE_IDE) {
|
||||||
adapter_type = g_strdup("ide");
|
|
||||||
} else if (strcmp(adapter_type, "ide") &&
|
|
||||||
strcmp(adapter_type, "buslogic") &&
|
|
||||||
strcmp(adapter_type, "lsilogic") &&
|
|
||||||
strcmp(adapter_type, "legacyESX")) {
|
|
||||||
error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (strcmp(adapter_type, "ide") != 0) {
|
|
||||||
/* that's the number of heads with which vmware operates when
|
/* that's the number of heads with which vmware operates when
|
||||||
creating, exporting, etc. vmdk files with a non-ide adapter type */
|
creating, exporting, etc. vmdk files with a non-ide adapter type */
|
||||||
number_heads = 255;
|
number_heads = 255;
|
||||||
}
|
}
|
||||||
if (!fmt) {
|
split = (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT) ||
|
||||||
/* Default format to monolithicSparse */
|
(subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE);
|
||||||
fmt = g_strdup("monolithicSparse");
|
flat = (subformat == BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT) ||
|
||||||
} else if (strcmp(fmt, "monolithicFlat") &&
|
(subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT);
|
||||||
strcmp(fmt, "monolithicSparse") &&
|
compress = subformat == BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED;
|
||||||
strcmp(fmt, "twoGbMaxExtentSparse") &&
|
|
||||||
strcmp(fmt, "twoGbMaxExtentFlat") &&
|
|
||||||
strcmp(fmt, "streamOptimized")) {
|
|
||||||
error_setg(errp, "Unknown subformat: '%s'", fmt);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
|
|
||||||
strcmp(fmt, "twoGbMaxExtentSparse"));
|
|
||||||
flat = !(strcmp(fmt, "monolithicFlat") &&
|
|
||||||
strcmp(fmt, "twoGbMaxExtentFlat"));
|
|
||||||
compress = !strcmp(fmt, "streamOptimized");
|
|
||||||
if (flat) {
|
if (flat) {
|
||||||
desc_extent_line = "RW %" PRId64 " FLAT \"%s\" 0\n";
|
extent_line_fmt = "RW %" PRId64 " FLAT \"%s\" 0\n";
|
||||||
} else {
|
} else {
|
||||||
desc_extent_line = "RW %" PRId64 " SPARSE \"%s\"\n";
|
extent_line_fmt = "RW %" PRId64 " SPARSE \"%s\"\n";
|
||||||
}
|
}
|
||||||
if (flat && backing_file) {
|
if (flat && backing_file) {
|
||||||
error_setg(errp, "Flat image can't have backing file");
|
error_setg(errp, "Flat image can't have backing file");
|
||||||
|
@ -2058,10 +2045,34 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
|
||||||
ret = -ENOTSUP;
|
ret = -ENOTSUP;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create extents */
|
||||||
|
if (split) {
|
||||||
|
extent_size = split_size;
|
||||||
|
} else {
|
||||||
|
extent_size = size;
|
||||||
|
}
|
||||||
|
if (!split && !flat) {
|
||||||
|
created_size = extent_size;
|
||||||
|
} else {
|
||||||
|
created_size = 0;
|
||||||
|
}
|
||||||
|
/* Get the descriptor file BDS */
|
||||||
|
blk = extent_fn(created_size, 0, flat, split, compress, zeroed_grain,
|
||||||
|
opaque, errp);
|
||||||
|
if (!blk) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!split && !flat) {
|
||||||
|
vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, created_size,
|
||||||
|
blk_bs(blk)->filename);
|
||||||
|
}
|
||||||
|
|
||||||
if (backing_file) {
|
if (backing_file) {
|
||||||
BlockBackend *blk;
|
BlockBackend *backing;
|
||||||
char *full_backing = g_new0(char, PATH_MAX);
|
char *full_backing = g_new0(char, PATH_MAX);
|
||||||
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
|
bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename, backing_file,
|
||||||
full_backing, PATH_MAX,
|
full_backing, PATH_MAX,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
@ -2071,93 +2082,64 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
blk = blk_new_open(full_backing, NULL, NULL,
|
backing = blk_new_open(full_backing, NULL, NULL,
|
||||||
BDRV_O_NO_BACKING, errp);
|
BDRV_O_NO_BACKING, errp);
|
||||||
g_free(full_backing);
|
g_free(full_backing);
|
||||||
if (blk == NULL) {
|
if (backing == NULL) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) {
|
if (strcmp(blk_bs(backing)->drv->format_name, "vmdk")) {
|
||||||
blk_unref(blk);
|
error_setg(errp, "Invalid backing file format: %s. Must be vmdk",
|
||||||
|
blk_bs(backing)->drv->format_name);
|
||||||
|
blk_unref(backing);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = vmdk_read_cid(blk_bs(blk), 0, &parent_cid);
|
ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid);
|
||||||
blk_unref(blk);
|
blk_unref(backing);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
error_setg(errp, "Failed to read parent CID");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
snprintf(parent_desc_line, BUF_SIZE,
|
snprintf(parent_desc_line, BUF_SIZE,
|
||||||
"parentFileNameHint=\"%s\"", backing_file);
|
"parentFileNameHint=\"%s\"", backing_file);
|
||||||
}
|
}
|
||||||
|
extent_idx = 1;
|
||||||
/* Create extents */
|
while (created_size < size) {
|
||||||
filesize = total_size;
|
BlockBackend *extent_blk;
|
||||||
while (filesize > 0) {
|
int64_t cur_size = MIN(size - created_size, extent_size);
|
||||||
int64_t size = filesize;
|
extent_blk = extent_fn(cur_size, extent_idx, flat, split, compress,
|
||||||
|
zeroed_grain, opaque, errp);
|
||||||
if (split && size > split_size) {
|
if (!extent_blk) {
|
||||||
size = split_size;
|
|
||||||
}
|
|
||||||
if (split) {
|
|
||||||
snprintf(desc_filename, PATH_MAX, "%s-%c%03d%s",
|
|
||||||
prefix, flat ? 'f' : 's', ++idx, postfix);
|
|
||||||
} else if (flat) {
|
|
||||||
snprintf(desc_filename, PATH_MAX, "%s-flat%s", prefix, postfix);
|
|
||||||
} else {
|
|
||||||
snprintf(desc_filename, PATH_MAX, "%s%s", prefix, postfix);
|
|
||||||
}
|
|
||||||
snprintf(ext_filename, PATH_MAX, "%s%s", path, desc_filename);
|
|
||||||
|
|
||||||
if (vmdk_create_extent(ext_filename, size,
|
|
||||||
flat, compress, zeroed_grain, NULL, opts, errp)) {
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
filesize -= size;
|
vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, cur_size,
|
||||||
|
blk_bs(extent_blk)->filename);
|
||||||
/* Format description line */
|
created_size += cur_size;
|
||||||
snprintf(desc_line, BUF_SIZE,
|
extent_idx++;
|
||||||
desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename);
|
blk_unref(extent_blk);
|
||||||
g_string_append(ext_desc_lines, desc_line);
|
|
||||||
}
|
}
|
||||||
/* generate descriptor file */
|
/* generate descriptor file */
|
||||||
desc = g_strdup_printf(desc_template,
|
desc = g_strdup_printf(desc_template,
|
||||||
g_random_int(),
|
g_random_int(),
|
||||||
parent_cid,
|
parent_cid,
|
||||||
fmt,
|
BlockdevVmdkSubformat_str(subformat),
|
||||||
parent_desc_line,
|
parent_desc_line,
|
||||||
ext_desc_lines->str,
|
ext_desc_lines->str,
|
||||||
hw_version,
|
hw_version,
|
||||||
total_size /
|
size /
|
||||||
(int64_t)(63 * number_heads * BDRV_SECTOR_SIZE),
|
(int64_t)(63 * number_heads * BDRV_SECTOR_SIZE),
|
||||||
number_heads,
|
number_heads,
|
||||||
adapter_type);
|
BlockdevVmdkAdapterType_str(adapter_type));
|
||||||
desc_len = strlen(desc);
|
desc_len = strlen(desc);
|
||||||
/* the descriptor offset = 0x200 */
|
/* the descriptor offset = 0x200 */
|
||||||
if (!split && !flat) {
|
if (!split && !flat) {
|
||||||
desc_offset = 0x200;
|
desc_offset = 0x200;
|
||||||
} else {
|
|
||||||
ret = bdrv_create_file(filename, opts, &local_err);
|
|
||||||
if (ret < 0) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new_blk = blk_new_open(filename, NULL, NULL,
|
ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0);
|
||||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
|
||||||
&local_err);
|
|
||||||
if (new_blk == NULL) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
ret = -EIO;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
blk_set_allow_write_beyond_eof(new_blk, true);
|
|
||||||
|
|
||||||
ret = blk_pwrite(new_blk, desc_offset, desc, desc_len, 0);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not write description");
|
error_setg_errno(errp, -ret, "Could not write description");
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -2165,12 +2147,146 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
|
||||||
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
|
||||||
* for description file */
|
* for description file */
|
||||||
if (desc_offset == 0) {
|
if (desc_offset == 0) {
|
||||||
ret = blk_truncate(new_blk, desc_len, PREALLOC_MODE_OFF, errp);
|
ret = blk_truncate(blk, desc_len, PREALLOC_MODE_OFF, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ret = 0;
|
||||||
exit:
|
exit:
|
||||||
if (new_blk) {
|
if (blk) {
|
||||||
blk_unref(new_blk);
|
blk_unref(blk);
|
||||||
}
|
}
|
||||||
|
g_free(desc);
|
||||||
|
g_free(parent_desc_line);
|
||||||
|
g_string_free(ext_desc_lines, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *path;
|
||||||
|
char *prefix;
|
||||||
|
char *postfix;
|
||||||
|
QemuOpts *opts;
|
||||||
|
} VMDKCreateOptsData;
|
||||||
|
|
||||||
|
static BlockBackend *vmdk_co_create_opts_cb(int64_t size, int idx,
|
||||||
|
bool flat, bool split, bool compress,
|
||||||
|
bool zeroed_grain, void *opaque,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BlockBackend *blk = NULL;
|
||||||
|
BlockDriverState *bs = NULL;
|
||||||
|
VMDKCreateOptsData *data = opaque;
|
||||||
|
char *ext_filename = NULL;
|
||||||
|
char *rel_filename = NULL;
|
||||||
|
|
||||||
|
if (idx == 0) {
|
||||||
|
rel_filename = g_strdup_printf("%s%s", data->prefix, data->postfix);
|
||||||
|
} else if (split) {
|
||||||
|
rel_filename = g_strdup_printf("%s-%c%03d%s",
|
||||||
|
data->prefix,
|
||||||
|
flat ? 'f' : 's', idx, data->postfix);
|
||||||
|
} else {
|
||||||
|
assert(idx == 1);
|
||||||
|
rel_filename = g_strdup_printf("%s-flat%s", data->prefix, data->postfix);
|
||||||
|
}
|
||||||
|
|
||||||
|
ext_filename = g_strdup_printf("%s%s", data->path, rel_filename);
|
||||||
|
g_free(rel_filename);
|
||||||
|
|
||||||
|
if (vmdk_create_extent(ext_filename, size,
|
||||||
|
flat, compress, zeroed_grain, &blk, data->opts,
|
||||||
|
errp)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
bdrv_unref(bs);
|
||||||
|
exit:
|
||||||
|
g_free(ext_filename);
|
||||||
|
return blk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
char *desc = NULL;
|
||||||
|
int64_t total_size = 0;
|
||||||
|
char *adapter_type = NULL;
|
||||||
|
BlockdevVmdkAdapterType adapter_type_enum;
|
||||||
|
char *backing_file = NULL;
|
||||||
|
char *hw_version = NULL;
|
||||||
|
char *fmt = NULL;
|
||||||
|
BlockdevVmdkSubformat subformat;
|
||||||
|
int ret = 0;
|
||||||
|
char *path = g_malloc0(PATH_MAX);
|
||||||
|
char *prefix = g_malloc0(PATH_MAX);
|
||||||
|
char *postfix = g_malloc0(PATH_MAX);
|
||||||
|
char *desc_line = g_malloc0(BUF_SIZE);
|
||||||
|
char *ext_filename = g_malloc0(PATH_MAX);
|
||||||
|
char *desc_filename = g_malloc0(PATH_MAX);
|
||||||
|
char *parent_desc_line = g_malloc0(BUF_SIZE);
|
||||||
|
bool zeroed_grain;
|
||||||
|
bool compat6;
|
||||||
|
VMDKCreateOptsData data;
|
||||||
|
|
||||||
|
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
/* Read out options */
|
||||||
|
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
|
||||||
|
BDRV_SECTOR_SIZE);
|
||||||
|
adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE);
|
||||||
|
backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
|
||||||
|
hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION);
|
||||||
|
compat6 = qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false);
|
||||||
|
if (strcmp(hw_version, "undefined") == 0) {
|
||||||
|
g_free(hw_version);
|
||||||
|
hw_version = g_strdup("4");
|
||||||
|
}
|
||||||
|
fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
|
||||||
|
zeroed_grain = qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false);
|
||||||
|
|
||||||
|
if (adapter_type) {
|
||||||
|
adapter_type_enum = qapi_enum_parse(&BlockdevVmdkAdapterType_lookup,
|
||||||
|
adapter_type,
|
||||||
|
BLOCKDEV_VMDK_ADAPTER_TYPE_IDE,
|
||||||
|
&local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adapter_type_enum = BLOCKDEV_VMDK_ADAPTER_TYPE_IDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fmt) {
|
||||||
|
/* Default format to monolithicSparse */
|
||||||
|
subformat = BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE;
|
||||||
|
} else {
|
||||||
|
subformat = qapi_enum_parse(&BlockdevVmdkSubformat_lookup,
|
||||||
|
fmt,
|
||||||
|
BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE,
|
||||||
|
&local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = (VMDKCreateOptsData){
|
||||||
|
.prefix = prefix,
|
||||||
|
.postfix = postfix,
|
||||||
|
.path = path,
|
||||||
|
.opts = opts,
|
||||||
|
};
|
||||||
|
ret = vmdk_co_do_create(total_size, subformat, adapter_type_enum,
|
||||||
|
backing_file, hw_version, compat6, zeroed_grain,
|
||||||
|
vmdk_co_create_opts_cb, &data, errp);
|
||||||
|
|
||||||
|
exit:
|
||||||
g_free(adapter_type);
|
g_free(adapter_type);
|
||||||
g_free(backing_file);
|
g_free(backing_file);
|
||||||
g_free(hw_version);
|
g_free(hw_version);
|
||||||
|
@ -2183,7 +2299,84 @@ exit:
|
||||||
g_free(ext_filename);
|
g_free(ext_filename);
|
||||||
g_free(desc_filename);
|
g_free(desc_filename);
|
||||||
g_free(parent_desc_line);
|
g_free(parent_desc_line);
|
||||||
g_string_free(ext_desc_lines, true);
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockBackend *vmdk_co_create_cb(int64_t size, int idx,
|
||||||
|
bool flat, bool split, bool compress,
|
||||||
|
bool zeroed_grain, void *opaque,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
BlockDriverState *bs;
|
||||||
|
BlockBackend *blk;
|
||||||
|
BlockdevCreateOptionsVmdk *opts = opaque;
|
||||||
|
|
||||||
|
if (idx == 0) {
|
||||||
|
bs = bdrv_open_blockdev_ref(opts->file, errp);
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
BlockdevRefList *list = opts->extents;
|
||||||
|
for (i = 1; i < idx; i++) {
|
||||||
|
if (!list || !list->next) {
|
||||||
|
error_setg(errp, "Extent [%d] not specified", i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
list = list->next;
|
||||||
|
}
|
||||||
|
if (!list) {
|
||||||
|
error_setg(errp, "Extent [%d] not specified", idx - 1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bs = bdrv_open_blockdev_ref(list->value, errp);
|
||||||
|
}
|
||||||
|
if (!bs) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
|
||||||
|
BLK_PERM_ALL);
|
||||||
|
if (blk_insert_bs(blk, bs, errp)) {
|
||||||
|
bdrv_unref(bs);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
blk_set_allow_write_beyond_eof(blk, true);
|
||||||
|
bdrv_unref(bs);
|
||||||
|
|
||||||
|
ret = vmdk_init_extent(blk, size, flat, compress, zeroed_grain, errp);
|
||||||
|
if (ret) {
|
||||||
|
blk_unref(blk);
|
||||||
|
blk = NULL;
|
||||||
|
}
|
||||||
|
return blk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn vmdk_co_create(BlockdevCreateOptions *create_options,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
BlockdevCreateOptionsVmdk *opts;
|
||||||
|
|
||||||
|
opts = &create_options->u.vmdk;
|
||||||
|
|
||||||
|
/* Validate options */
|
||||||
|
if (!QEMU_IS_ALIGNED(opts->size, BDRV_SECTOR_SIZE)) {
|
||||||
|
error_setg(errp, "Image size must be a multiple of 512 bytes");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = vmdk_co_do_create(opts->size,
|
||||||
|
opts->subformat,
|
||||||
|
opts->adapter_type,
|
||||||
|
opts->backing_file,
|
||||||
|
opts->hwversion,
|
||||||
|
false,
|
||||||
|
opts->zeroed_grain,
|
||||||
|
vmdk_co_create_cb,
|
||||||
|
opts, errp);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2451,6 +2644,7 @@ static BlockDriver bdrv_vmdk = {
|
||||||
.bdrv_co_pwrite_zeroes = vmdk_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = vmdk_co_pwrite_zeroes,
|
||||||
.bdrv_close = vmdk_close,
|
.bdrv_close = vmdk_close,
|
||||||
.bdrv_co_create_opts = vmdk_co_create_opts,
|
.bdrv_co_create_opts = vmdk_co_create_opts,
|
||||||
|
.bdrv_co_create = vmdk_co_create,
|
||||||
.bdrv_co_flush_to_disk = vmdk_co_flush,
|
.bdrv_co_flush_to_disk = vmdk_co_flush,
|
||||||
.bdrv_co_block_status = vmdk_co_block_status,
|
.bdrv_co_block_status = vmdk_co_block_status,
|
||||||
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
||||||
|
|
|
@ -4137,6 +4137,75 @@
|
||||||
'size': 'size',
|
'size': 'size',
|
||||||
'*cluster-size' : 'size' } }
|
'*cluster-size' : 'size' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevVmdkSubformat:
|
||||||
|
#
|
||||||
|
# Subformat options for VMDK images
|
||||||
|
#
|
||||||
|
# @monolithicSparse: Single file image with sparse cluster allocation
|
||||||
|
#
|
||||||
|
# @monolithicFlat: Single flat data image and a descriptor file
|
||||||
|
#
|
||||||
|
# @twoGbMaxExtentSparse: Data is split into 2GB (per virtual LBA) sparse extent
|
||||||
|
# files, in addition to a descriptor file
|
||||||
|
#
|
||||||
|
# @twoGbMaxExtentFlat: Data is split into 2GB (per virtual LBA) flat extent
|
||||||
|
# files, in addition to a descriptor file
|
||||||
|
#
|
||||||
|
# @streamOptimized: Single file image sparse cluster allocation, optimized
|
||||||
|
# for streaming over network.
|
||||||
|
#
|
||||||
|
# Since: 4.0
|
||||||
|
##
|
||||||
|
{ 'enum': 'BlockdevVmdkSubformat',
|
||||||
|
'data': [ 'monolithicSparse', 'monolithicFlat', 'twoGbMaxExtentSparse',
|
||||||
|
'twoGbMaxExtentFlat', 'streamOptimized'] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevVmdkAdapterType:
|
||||||
|
#
|
||||||
|
# Adapter type info for VMDK images
|
||||||
|
#
|
||||||
|
# Since: 4.0
|
||||||
|
##
|
||||||
|
{ 'enum': 'BlockdevVmdkAdapterType',
|
||||||
|
'data': [ 'ide', 'buslogic', 'lsilogic', 'legacyESX'] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevCreateOptionsVmdk:
|
||||||
|
#
|
||||||
|
# Driver specific image creation options for VMDK.
|
||||||
|
#
|
||||||
|
# @file Where to store the new image file. This refers to the image
|
||||||
|
# file for monolithcSparse and streamOptimized format, or the
|
||||||
|
# descriptor file for other formats.
|
||||||
|
# @size Size of the virtual disk in bytes
|
||||||
|
# @extents Where to store the data extents. Required for monolithcFlat,
|
||||||
|
# twoGbMaxExtentSparse and twoGbMaxExtentFlat formats. For
|
||||||
|
# monolithicFlat, only one entry is required; for
|
||||||
|
# twoGbMaxExtent* formats, the number of entries required is
|
||||||
|
# calculated as extent_number = virtual_size / 2GB.
|
||||||
|
# @subformat The subformat of the VMDK image. Default: "monolithicSparse".
|
||||||
|
# @backing-file The path of backing file. Default: no backing file is used.
|
||||||
|
# @adapter-type The adapter type used to fill in the descriptor. Default: ide.
|
||||||
|
# @hwversion Hardware version. The meaningful options are "4" or "6".
|
||||||
|
# Default: "4".
|
||||||
|
# @zeroed-grain Whether to enable zeroed-grain feature for sparse subformats.
|
||||||
|
# Default: false.
|
||||||
|
#
|
||||||
|
# Since: 4.0
|
||||||
|
##
|
||||||
|
{ 'struct': 'BlockdevCreateOptionsVmdk',
|
||||||
|
'data': { 'file': 'BlockdevRef',
|
||||||
|
'size': 'size',
|
||||||
|
'*extents': ['BlockdevRef'],
|
||||||
|
'*subformat': 'BlockdevVmdkSubformat',
|
||||||
|
'*backing-file': 'str',
|
||||||
|
'*adapter-type': 'BlockdevVmdkAdapterType',
|
||||||
|
'*hwversion': 'str',
|
||||||
|
'*zeroed-grain': 'bool' } }
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# @SheepdogRedundancyType:
|
# @SheepdogRedundancyType:
|
||||||
#
|
#
|
||||||
|
@ -4331,6 +4400,7 @@
|
||||||
'ssh': 'BlockdevCreateOptionsSsh',
|
'ssh': 'BlockdevCreateOptionsSsh',
|
||||||
'vdi': 'BlockdevCreateOptionsVdi',
|
'vdi': 'BlockdevCreateOptionsVdi',
|
||||||
'vhdx': 'BlockdevCreateOptionsVhdx',
|
'vhdx': 'BlockdevCreateOptionsVhdx',
|
||||||
|
'vmdk': 'BlockdevCreateOptionsVmdk',
|
||||||
'vpc': 'BlockdevCreateOptionsVpc'
|
'vpc': 'BlockdevCreateOptionsVpc'
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,15 @@
|
||||||
'query-tpm-types',
|
'query-tpm-types',
|
||||||
'ringbuf-read' ],
|
'ringbuf-read' ],
|
||||||
'name-case-whitelist': [
|
'name-case-whitelist': [
|
||||||
'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
|
'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
|
||||||
'CpuInfoMIPS', # PC, visible through query-cpu
|
'CpuInfoMIPS', # PC, visible through query-cpu
|
||||||
'CpuInfoTricore', # PC, visible through query-cpu
|
'CpuInfoTricore', # PC, visible through query-cpu
|
||||||
'QapiErrorClass', # all members, visible through errors
|
'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings
|
||||||
'UuidInfo', # UUID, visible through query-uuid
|
'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings
|
||||||
'X86CPURegister32', # all members, visible indirectly through qom-get
|
'QapiErrorClass', # all members, visible through errors
|
||||||
'q_obj_CpuInfo-base' # CPU, visible through query-cpu
|
'UuidInfo', # UUID, visible through query-uuid
|
||||||
|
'X86CPURegister32', # all members, visible indirectly through qom-get
|
||||||
|
'q_obj_CpuInfo-base' # CPU, visible through query-cpu
|
||||||
] } }
|
] } }
|
||||||
|
|
||||||
# Documentation generated with qapi-gen.py is in source order, with
|
# Documentation generated with qapi-gen.py is in source order, with
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue