diff options
author | Kevin Wolf <kwolf@redhat.com> | 2018-01-11 16:18:08 +0100 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2018-03-09 15:17:47 +0100 |
commit | b76b4f604521e59f857d6177bc55f6f2e41fd392 (patch) | |
tree | 27205e373895cab07c2b0d3e3dacef7a68fe9dbb /block/qcow2.c | |
parent | bcebf102ccc3c6db327f341adc379fdf0673ca6b (diff) | |
download | qemu-b76b4f604521e59f857d6177bc55f6f2e41fd392.zip qemu-b76b4f604521e59f857d6177bc55f6f2e41fd392.tar.gz qemu-b76b4f604521e59f857d6177bc55f6f2e41fd392.tar.bz2 |
qcow2: Use visitor for options in qcow2_create()
Instead of manually creating the BlockdevCreateOptions object, use a
visitor to parse the given options into the QAPI object.
This involves translation from the old command line syntax to the syntax
mandated by the QAPI schema. Option names are still checked against
qcow2_create_opts, so only the old option names are allowed on the
command line, even if they are translated in qcow2_create().
In contrast, new option values are optionally recognised besides the old
values: 'compat' accepts 'v2'/'v3' as an alias for '0.10'/'1.1', and
'encrypt.format' accepts 'qcow' as an alias for 'aes' now.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Diffstat (limited to 'block/qcow2.c')
-rw-r--r-- | block/qcow2.c | 218 |
1 files changed, 78 insertions, 140 deletions
diff --git a/block/qcow2.c b/block/qcow2.c index 933c612..37b0e36 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -37,7 +37,8 @@ #include "qemu/option_int.h" #include "qemu/cutils.h" #include "qemu/bswap.h" -#include "qapi/opts-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-block-core.h" #include "block/crypto.h" /* @@ -2449,37 +2450,6 @@ static int qcow2_crypt_method_from_format(const char *encryptfmt) } } -static QCryptoBlockCreateOptions * -qcow2_parse_encryption(const char *encryptfmt, QemuOpts *opts, Error **errp) -{ - QCryptoBlockCreateOptions *cryptoopts = NULL; - QDict *options, *encryptopts; - int fmt; - - options = qemu_opts_to_qdict(opts, NULL); - qdict_extract_subqdict(options, &encryptopts, "encrypt."); - QDECREF(options); - - fmt = qcow2_crypt_method_from_format(encryptfmt); - - switch (fmt) { - case QCOW_CRYPT_LUKS: - cryptoopts = block_crypto_create_opts_init( - Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp); - break; - case QCOW_CRYPT_AES: - cryptoopts = block_crypto_create_opts_init( - Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); - break; - default: - error_setg(errp, "Unknown encryption format '%s'", encryptfmt); - break; - } - - QDECREF(encryptopts); - return cryptoopts; -} - static int qcow2_set_up_encryption(BlockDriverState *bs, QCryptoBlockCreateOptions *cryptoopts, Error **errp) @@ -2874,7 +2844,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } if (version < 3 && qcow2_opts->lazy_refcounts) { error_setg(errp, "Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)"); + "level 1.1 and above (use version=v3 or greater)"); ret = -EINVAL; goto out; } @@ -2892,7 +2862,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } if (version < 3 && qcow2_opts->refcount_bits != 16) { error_setg(errp, "Different refcount widths than 16 bits require " - "compatibility level 1.1 or above (use compat=1.1 or " + "compatibility level 1.1 or above (use version=v3 or " "greater)"); ret = -EINVAL; goto out; @@ -3080,144 +3050,112 @@ out: static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts, Error **errp) { - BlockdevCreateOptions create_options; - char *backing_file = NULL; - char *backing_fmt = NULL; - BlockdevDriver backing_drv; - char *buf = NULL; - uint64_t size = 0; - int flags = 0; - size_t cluster_size = DEFAULT_CLUSTER_SIZE; - PreallocMode prealloc; - int version; - uint64_t refcount_bits; - char *encryptfmt = NULL; - QCryptoBlockCreateOptions *cryptoopts = NULL; + BlockdevCreateOptions *create_options = NULL; + QDict *qdict = NULL; + QObject *qobj; + Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; + const char *val; int ret; - /* Read out options */ - size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); - backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); - backing_drv = qapi_enum_parse(&BlockdevDriver_lookup, backing_fmt, - 0, &local_err); - if (local_err) { - error_propagate(errp, local_err); + /* Only the keyval visitor supports the dotted syntax needed for + * encryption, so go through a QDict before getting a QAPI type. Ignore + * options meant for the protocol layer so that the visitor doesn't + * complain. */ + qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts, + true); + + /* Handle encryption options */ + val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT); + if (val && !strcmp(val, "on")) { + qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow"); + } else if (val && !strcmp(val, "off")) { + qdict_del(qdict, BLOCK_OPT_ENCRYPT); + } + + val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT); + if (val && !strcmp(val, "aes")) { + qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow"); + } + + /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into + * version=v2/v3 below. */ + val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL); + if (val && !strcmp(val, "0.10")) { + qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2"); + } else if (val && !strcmp(val, "1.1")) { + qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3"); + } + + /* Change legacy command line options into QMP ones */ + static const QDictRenames opt_renames[] = { + { BLOCK_OPT_BACKING_FILE, "backing-file" }, + { BLOCK_OPT_BACKING_FMT, "backing-fmt" }, + { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" }, + { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" }, + { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" }, + { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT }, + { BLOCK_OPT_COMPAT_LEVEL, "version" }, + { NULL, NULL }, + }; + + if (!qdict_rename_keys(qdict, opt_renames, errp)) { ret = -EINVAL; goto finish; } - encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); - if (encryptfmt) { - if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) { - error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and " - BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive"); - ret = -EINVAL; - goto finish; - } - } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) { - encryptfmt = g_strdup("aes"); - } - if (encryptfmt) { - cryptoopts = qcow2_parse_encryption(encryptfmt, opts, errp); - if (cryptoopts == NULL) { - ret = -EINVAL; - goto finish; - } - } - - cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; + /* Create and open the file (protocol layer) */ + ret = bdrv_create_file(filename, opts, errp); + if (ret < 0) { goto finish; } - buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); - prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, - PREALLOC_MODE_OFF, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; + + bs = bdrv_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + if (bs == NULL) { + ret = -EIO; goto finish; } - version = qcow2_opt_get_version_del(opts, &local_err); - if (local_err) { - error_propagate(errp, local_err); + /* Set 'driver' and 'node' options */ + qdict_put_str(qdict, "driver", "qcow2"); + qdict_put_str(qdict, "file", bs->node_name); + + /* Now get the QAPI type BlockdevCreateOptions */ + qobj = qdict_crumple(qdict, errp); + QDECREF(qdict); + qdict = qobject_to_qdict(qobj); + if (qdict == NULL) { ret = -EINVAL; goto finish; } - if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) { - flags |= BLOCK_FLAG_LAZY_REFCOUNTS; - } + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); - refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err); if (local_err) { error_propagate(errp, local_err); ret = -EINVAL; goto finish; } - - /* Create and open the file (protocol layer) */ - ret = bdrv_create_file(filename, opts, errp); - if (ret < 0) { - goto finish; - } - - bs = bdrv_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); - if (bs == NULL) { - ret = -EIO; - goto finish; - } + /* Silently round up size */ + create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size, + BDRV_SECTOR_SIZE); /* Create the qcow2 image (format layer) */ - create_options = (BlockdevCreateOptions) { - .driver = BLOCKDEV_DRIVER_QCOW2, - .u.qcow2 = { - .file = &(BlockdevRef) { - .type = QTYPE_QSTRING, - .u.reference = bs->node_name, - }, - .size = size, - .has_version = true, - .version = version == 2 - ? BLOCKDEV_QCOW2_VERSION_V2 - : BLOCKDEV_QCOW2_VERSION_V3, - .has_backing_file = (backing_file != NULL), - .backing_file = backing_file, - .has_backing_fmt = (backing_fmt != NULL), - .backing_fmt = backing_drv, - .has_encrypt = (encryptfmt != NULL), - .encrypt = cryptoopts, - .has_cluster_size = true, - .cluster_size = cluster_size, - .has_preallocation = true, - .preallocation = prealloc, - .has_lazy_refcounts = true, - .lazy_refcounts = (flags & BLOCK_FLAG_LAZY_REFCOUNTS), - .has_refcount_bits = true, - .refcount_bits = refcount_bits, - }, - }; - ret = qcow2_co_create(&create_options, errp); + ret = qcow2_co_create(create_options, errp); if (ret < 0) { goto finish; } + ret = 0; finish: + QDECREF(qdict); bdrv_unref(bs); - - qapi_free_QCryptoBlockCreateOptions(cryptoopts); - g_free(backing_file); - g_free(backing_fmt); - g_free(encryptfmt); - g_free(buf); + qapi_free_BlockdevCreateOptions(create_options); return ret; } |