All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options
@ 2018-05-02 21:32 Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 1/7] qdict: Add qdict_set_default_bool() Max Reitz
                   ` (8 more replies)
  0 siblings, 9 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

(Sorry, Markus, sorry, Kevin, if this series makes you angry.)

The subject says it all, I think.  The original issue I was assigned to
solve is this:

    $ ./qemu-img info --image-opts driver=null-co,size=42
    image: json:{"driver": "null-co", "size": "42"}
    [...]

As you can see, "size" gets a string value in the json:{} object
although it is an integer, according to the QAPI schema.  (Buglink:
https://bugzilla.redhat.com/show_bug.cgi?id=1534396)

My first approach to fix this was to try to pipe the full_open_options
dict generated in bdrv_refresh_filename() through a keyval input visitor
and then an output visitor (which would have depended on my filename
series).  This did not work out because bs->options (where all of the
reconstructed options come from) may either be correctly typed or not,
and keyval chokes on non-string values.  I could have probably converted
bs->options to fully string values before trying to pipe it through
keyval, but I decided I didn't like that very much.  (I suppose I can be
convinced otherwise, though.)

So I decided to venture into the territory of what we actually want at
some point: Correctly typed bs->options.  Or, rather, in the end we want
bs->options to be BlockdevOptions, but let's not be too hasty here.

So it turns out that with really just a bit of work we can separate the
interfaces that generate correctly typed options (e.g. blockdev-add)
from the ones that generate pure string-valued options (e.g. qemu-img
--image-opts) rather well.  Once we have done that, we can pipe all of
the pure string options through keyval and back to get correctly typed
options.

So far the theory.  Now, in practice we of course have pitfalls, and
those are not addressed by this series, which is the main reason this is
an RFC.

The first pitfall (I can see...) is that json:{} allows you to specify
mixed options -- some values are incorrectly strings, others are
non-strings.  keyval cannot cope with that, so the result after this
series is that those options end up in bs->options just like that.  I
suppose we could just forbid that mixed-typed case, though, and call it
a bug fix.

The second problem (and I think the big reason why we have not
approached this issue so far) is that there are options which you can
give as strings, but which are not covered by the schema.  In such a
case, the input visitor will reject the QDict and we will just use it
as-is, that is, full of strings.  Now that is not what we want in the
end, of course -- we want everything to be converted into something that
is covered by the schema.

My reasoning for sending this series anyway is that it doesn't make
things worse (bs->options is a mess already, you can never be certain
that it contains correctly typed values or just strings or both), and
that it at least gives a starting point from which we can continue on.
After this series, we have a clear separation between the interfaces
that use purely string values and the ones that provide correct typing
(well, and json:{}).

Oh, and it fixes the above BZ for the more common cases.


Max Reitz (7):
  qdict: Add qdict_set_default_bool()
  block: Let change-medium add detect-zeroes as bool
  block: Make use of qdict_set_default_bool()
  block: Add bdrv_open_string_opts()
  block: Add blk_new_open_string_opts()
  block: Use {blk_new,bdrv}_open_string_opts()
  iotests: Test internal option typing

 include/block/block.h          |  2 +
 include/qapi/qmp/qdict.h       |  1 +
 include/sysemu/block-backend.h |  2 +
 block.c                        | 96 ++++++++++++++++++++++++++++++++++++++----
 block/block-backend.c          | 30 ++++++++++++-
 block/vvfat.c                  |  4 +-
 blockdev.c                     | 33 ++++++++++-----
 qemu-img.c                     |  3 +-
 qemu-io.c                      |  2 +-
 qemu-nbd.c                     |  2 +-
 qobject/qdict.c                | 13 ++++++
 tests/test-replication.c       |  6 +--
 tests/qemu-iotests/089         | 12 ++++++
 tests/qemu-iotests/089.out     |  5 +++
 14 files changed, 183 insertions(+), 28 deletions(-)

-- 
2.14.3

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC 1/7] qdict: Add qdict_set_default_bool()
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
@ 2018-05-02 21:32 ` Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 2/7] block: Let change-medium add detect-zeroes as bool Max Reitz
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 include/qapi/qmp/qdict.h |  1 +
 qobject/qdict.c          | 13 +++++++++++++
 2 files changed, 14 insertions(+)

diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index 2cc3e906f7..a6fb89302d 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -69,6 +69,7 @@ const char *qdict_get_try_str(const QDict *qdict, const char *key);
 
 void qdict_copy_default(QDict *dst, QDict *src, const char *key);
 void qdict_set_default_str(QDict *dst, const char *key, const char *val);
+void qdict_set_default_bool(QDict *dst, const char *key, bool val);
 
 QDict *qdict_clone_shallow(const QDict *src);
 void qdict_flatten(QDict *qdict);
diff --git a/qobject/qdict.c b/qobject/qdict.c
index d1997a0d8a..5c25da36b3 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -498,6 +498,19 @@ void qdict_set_default_str(QDict *dst, const char *key, const char *val)
     qdict_put_str(dst, key, val);
 }
 
+/**
+ * qdict_set_default_bool(): If no entry mapped by 'key' exists in
+ * 'dst' yet, a new QBool initialized by 'val' is put there.
+ */
+void qdict_set_default_bool(QDict *dst, const char *key, bool val)
+{
+    if (qdict_haskey(dst, key)) {
+        return;
+    }
+
+    qdict_put_bool(dst, key, val);
+}
+
 static void qdict_flatten_qdict(QDict *qdict, QDict *target,
                                 const char *prefix);
 
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC 2/7] block: Let change-medium add detect-zeroes as bool
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 1/7] qdict: Add qdict_set_default_bool() Max Reitz
@ 2018-05-02 21:32 ` Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 3/7] block: Make use of qdict_set_default_bool() Max Reitz
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

There is no reason to use the wrong type here.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 blockdev.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/blockdev.c b/blockdev.c
index c31bf3d98d..76f811c415 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2654,7 +2654,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device,
 
     options = qdict_new();
     detect_zeroes = blk_get_detect_zeroes_from_root_state(blk);
-    qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off");
+    qdict_put_bool(options, "detect-zeroes", detect_zeroes);
 
     if (has_format) {
         qdict_put_str(options, "driver", format);
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC 3/7] block: Make use of qdict_set_default_bool()
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 1/7] qdict: Add qdict_set_default_bool() Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 2/7] block: Let change-medium add detect-zeroes as bool Max Reitz
@ 2018-05-02 21:32 ` Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 4/7] block: Add bdrv_open_string_opts() Max Reitz
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

When dealing with blockdev option QDicts that contain purely string
values, it is not really advisable to break that by adding non-string
values.  But it does make sense to use the correct type for QDicts that
may contain non-string values already, so do that.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block.c       | 12 ++++++------
 block/vvfat.c |  4 ++--
 blockdev.c    | 23 ++++++++++++++++-------
 3 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/block.c b/block.c
index a2caadf0a0..8a8d9c02a9 100644
--- a/block.c
+++ b/block.c
@@ -856,8 +856,8 @@ static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options,
     *child_flags = (parent_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY;
 
     /* For temporary files, unconditional cache=unsafe is fine */
-    qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off");
-    qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
+    qdict_set_default_bool(child_options, BDRV_OPT_CACHE_DIRECT, false);
+    qdict_set_default_bool(child_options, BDRV_OPT_CACHE_NO_FLUSH, true);
 
     /* Copy the read-only option from the parent */
     qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
@@ -1005,7 +1005,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
     qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
 
     /* backing files always opened read-only */
-    qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
+    qdict_set_default_bool(child_options, BDRV_OPT_READ_ONLY, true);
     flags &= ~BDRV_O_COPY_ON_READ;
 
     /* snapshot=on is handled on the top layer */
@@ -2440,9 +2440,9 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
         /* bdrv_open_inherit() defaults to the values in bdrv_flags (for
          * compatibility with other callers) rather than what we want as the
          * real defaults. Apply the defaults here instead. */
-        qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
-        qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
-        qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
+        qdict_set_default_bool(qdict, BDRV_OPT_CACHE_DIRECT, false);
+        qdict_set_default_bool(qdict, BDRV_OPT_CACHE_NO_FLUSH, false);
+        qdict_set_default_bool(qdict, BDRV_OPT_READ_ONLY, false);
     }
 
     bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
diff --git a/block/vvfat.c b/block/vvfat.c
index 1569783b0f..177b179ed2 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -3128,8 +3128,8 @@ static BlockDriver vvfat_write_target = {
 static void vvfat_qcow_options(int *child_flags, QDict *child_options,
                                int parent_flags, QDict *parent_options)
 {
-    qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
-    qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
+    qdict_set_default_bool(child_options, BDRV_OPT_READ_ONLY, false);
+    qdict_set_default_bool(child_options, BDRV_OPT_CACHE_NO_FLUSH, true);
 }
 
 static const BdrvChildRole child_vvfat_qcow = {
diff --git a/blockdev.c b/blockdev.c
index 76f811c415..9d4955f23e 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -645,17 +645,26 @@ err_no_opts:
     return NULL;
 }
 
-/* Takes the ownership of bs_opts */
-static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
+/* Takes the ownership of bs_opts.
+ * If @string_opts is true, @bs_opts contains purely string values.
+ * Otherwise, all values are correctly typed. */
+static BlockDriverState *bds_tree_init(QDict *bs_opts, bool string_opts,
+                                       Error **errp)
 {
     int bdrv_flags = 0;
 
     /* bdrv_open() defaults to the values in bdrv_flags (for compatibility
      * with other callers) rather than what we want as the real defaults.
      * Apply the defaults here instead. */
-    qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
-    qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
-    qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, "off");
+    if (string_opts) {
+        qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
+        qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
+        qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, "off");
+    } else {
+        qdict_set_default_bool(bs_opts, BDRV_OPT_CACHE_DIRECT, false);
+        qdict_set_default_bool(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, false);
+        qdict_set_default_bool(bs_opts, BDRV_OPT_READ_ONLY, false);
+    }
 
     if (runstate_check(RUN_STATE_INMIGRATE)) {
         bdrv_flags |= BDRV_O_INACTIVE;
@@ -4027,7 +4036,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr)
         goto out;
     }
 
-    BlockDriverState *bs = bds_tree_init(qdict, &local_err);
+    BlockDriverState *bs = bds_tree_init(qdict, true, &local_err);
     if (!bs) {
         error_report_err(local_err);
         goto out;
@@ -4063,7 +4072,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
         goto fail;
     }
 
-    bs = bds_tree_init(qdict, errp);
+    bs = bds_tree_init(qdict, false, errp);
     if (!bs) {
         goto fail;
     }
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC 4/7] block: Add bdrv_open_string_opts()
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
                   ` (2 preceding siblings ...)
  2018-05-02 21:32 ` [Qemu-devel] [RFC 3/7] block: Make use of qdict_set_default_bool() Max Reitz
@ 2018-05-02 21:32 ` Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 5/7] block: Add blk_new_open_string_opts() Max Reitz
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

This function is to be used by callers that cannot guarantee that all
values in @options are correctly typed.  In the future, we would like
this function to be gone, of course, but for now it at least lets us
begin a proper separation of legacy interfaces.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 include/block/block.h |  2 ++
 block.c               | 84 +++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/include/block/block.h b/include/block/block.h
index 3894edda9d..5335fa8c20 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -261,6 +261,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
                            const char *bdref_key, Error **errp);
 BlockDriverState *bdrv_open(const char *filename, const char *reference,
                             QDict *options, int flags, Error **errp);
+BlockDriverState *bdrv_open_string_opts(const char *filename, QDict *options,
+                                        int flags, Error **errp);
 BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
                                        int flags, Error **errp);
 BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
diff --git a/block.c b/block.c
index 8a8d9c02a9..1969fa303f 100644
--- a/block.c
+++ b/block.c
@@ -35,6 +35,7 @@
 #include "qapi/qmp/qjson.h"
 #include "qapi/qmp/qnull.h"
 #include "qapi/qmp/qstring.h"
+#include "qapi/qobject-input-visitor.h"
 #include "qapi/qobject-output-visitor.h"
 #include "qapi/qapi-visit-block-core.h"
 #include "sysemu/block-backend.h"
@@ -77,6 +78,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
                                            const BdrvChildRole *child_role,
                                            Error **errp);
 
+static QDict *bdrv_type_blockdev_opts(QDict *options);
+
 /* If non-zero, use only whitelisted block drivers */
 static int use_bdrv_whitelist;
 
@@ -1465,9 +1468,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
         return NULL;
     }
 
-    qdict_flatten(options);
-
-    return options;
+    return bdrv_type_blockdev_opts(options);
 }
 
 static void parse_json_protocol(QDict *options, const char **pfilename,
@@ -2801,6 +2802,83 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
                              NULL, errp);
 }
 
+/*
+ * Take a blockdev @options QDict and convert its values to the
+ * correct type.  This function takes ownership of @options.
+ *
+ * On both success and failure, @options is flattened.  On success,
+ * its refcount is decreased and a new well typed flattened QDict is
+ * returned.  On failure, @options is returned.
+ *
+ * TODO: Currently, this function cannot cope with partially typed
+ * dicts.  If everything is typed correctly, the keyval visitor will
+ * complain and we will return the original @options -- which is
+ * correct.  If all values are strings, the visitor will convert them
+ * to the correct type (if possible).  But if both are mixed, the
+ * visitor will fail and the partially typed @options is returned.  In
+ * practice, this should only be an issue with json:{} filenames,
+ * though.
+ *
+ * TODO: If @options does not conform to the schema, that should be a
+ * real error.
+ *
+ * TODO: Ideally, bdrv_open() should take BlockdevOptions, and the BDS
+ * should only contain BlockdevOptions.  But this is not possible
+ * until this function really rejects anything it does not recognize
+ * (without breaking existing interfaces).
+ */
+static QDict *bdrv_type_blockdev_opts(QDict *options)
+{
+    Visitor *v;
+    BlockdevOptions *blockdev_options = NULL;
+    QObject *typed_opts, *crumpled_opts = NULL;
+    Error *local_err = NULL;
+
+    if (!options) {
+        return NULL;
+    }
+
+    qdict_flatten(options);
+    crumpled_opts = qdict_crumple(options, &local_err);
+    if (local_err) {
+        goto done;
+    }
+
+    v = qobject_input_visitor_new_keyval(crumpled_opts);
+    visit_type_BlockdevOptions(v, NULL, &blockdev_options, &local_err);
+    visit_free(v);
+    if (local_err) {
+        goto done;
+    }
+
+    v = qobject_output_visitor_new(&typed_opts);
+    visit_type_BlockdevOptions(v, NULL, &blockdev_options, &local_err);
+    if (!local_err) {
+        visit_complete(v, &typed_opts);
+    }
+    visit_free(v);
+    if (local_err) {
+        goto done;
+    }
+
+    QDECREF(options);
+    options = qobject_to(QDict, typed_opts);
+    qdict_flatten(options);
+
+done:
+    error_free(local_err);
+    qapi_free_BlockdevOptions(blockdev_options);
+    qobject_decref(crumpled_opts);
+    return options;
+}
+
+BlockDriverState *bdrv_open_string_opts(const char *filename, QDict *options,
+                                        int flags, Error **errp)
+{
+    return bdrv_open_inherit(filename, NULL, bdrv_type_blockdev_opts(options),
+                             flags, NULL, NULL, errp);
+}
+
 /*
  * Adds a BlockDriverState to a simple queue for an atomic, transactional
  * reopen of multiple devices.
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC 5/7] block: Add blk_new_open_string_opts()
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
                   ` (3 preceding siblings ...)
  2018-05-02 21:32 ` [Qemu-devel] [RFC 4/7] block: Add bdrv_open_string_opts() Max Reitz
@ 2018-05-02 21:32 ` Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 6/7] block: Use {blk_new, bdrv}_open_string_opts() Max Reitz
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

This is an interface to bdrv_open_string_opts().

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 include/sysemu/block-backend.h |  2 ++
 block/block-backend.c          | 30 +++++++++++++++++++++++++++++-
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 92ab624fac..1ad002fa7e 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -79,6 +79,8 @@ typedef struct BlockBackendPublic {
 BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm);
 BlockBackend *blk_new_open(const char *filename, const char *reference,
                            QDict *options, int flags, Error **errp);
+BlockBackend *blk_new_open_string_opts(const char *filename, QDict *options,
+                                       int flags, Error **errp);
 int blk_get_refcnt(BlockBackend *blk);
 void blk_ref(BlockBackend *blk);
 void blk_unref(BlockBackend *blk);
diff --git a/block/block-backend.c b/block/block-backend.c
index 89f47b00ea..fd3024602a 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -105,6 +105,11 @@ static const AIOCBInfo block_backend_aiocb_info = {
 static void drive_info_del(DriveInfo *dinfo);
 static BlockBackend *bdrv_first_blk(BlockDriverState *bs);
 
+static BlockBackend *blk_do_new_open(const char *filename,
+                                     const char *reference, QDict *options,
+                                     int flags, bool string_opts,
+                                     Error **errp);
+
 /* All BlockBackends */
 static QTAILQ_HEAD(, BlockBackend) block_backends =
     QTAILQ_HEAD_INITIALIZER(block_backends);
@@ -348,6 +353,24 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
  */
 BlockBackend *blk_new_open(const char *filename, const char *reference,
                            QDict *options, int flags, Error **errp)
+{
+    return blk_do_new_open(filename, reference, options, flags, false, errp);
+}
+
+/*
+ * Like blk_new_open(), but accepts an @options QDict where some (or
+ * all) values are strings instead of their correct type.
+ */
+BlockBackend *blk_new_open_string_opts(const char *filename, QDict *options,
+                                       int flags, Error **errp)
+{
+    return blk_do_new_open(filename, NULL, options, flags, true, errp);
+}
+
+static BlockBackend *blk_do_new_open(const char *filename,
+                                     const char *reference, QDict *options,
+                                     int flags, bool string_opts,
+                                     Error **errp)
 {
     BlockBackend *blk;
     BlockDriverState *bs;
@@ -372,7 +395,12 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
     }
 
     blk = blk_new(perm, BLK_PERM_ALL);
-    bs = bdrv_open(filename, reference, options, flags, errp);
+    if (string_opts) {
+        assert(!reference);
+        bs = bdrv_open_string_opts(filename, options, flags, errp);
+    } else {
+        bs = bdrv_open(filename, reference, options, flags, errp);
+    }
     if (!bs) {
         blk_unref(blk);
         return NULL;
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC 6/7] block: Use {blk_new, bdrv}_open_string_opts()
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
                   ` (4 preceding siblings ...)
  2018-05-02 21:32 ` [Qemu-devel] [RFC 5/7] block: Add blk_new_open_string_opts() Max Reitz
@ 2018-05-02 21:32 ` Max Reitz
  2018-05-02 21:32 ` [Qemu-devel] [RFC 7/7] iotests: Test internal option typing Max Reitz
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

This patch makes every caller of blk_new_open() and bdrv_open() instead
call blk_new_open_string_opts() or bdrv_open_string_opts(),
respectively, when needed.  That is the case when the blockdev options
QDict may contain incorrectly typed string values.

In fact, all callers converted in this patch pass dicts that contain
string values only; and after this patch, all remaining callers of
blk_new_open() and bdrv_open() indeed guarantee that the dicts contain
only values that are correctly typed.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
Assuming that bdrv_open_inherit() always receives correctly typed
options and that parse_json_filename() always returns a correctly typed
dict, bs->options will always be correctly typed after this patch (as
far as I can see, that is).  All other callers of bdrv_open_inherit()
besides bdrv_open() and bdrv_open_string_opts() will inductively fulfill
these conditions, if they are met before them.

Now, of course both assumptions are wrong in the general case.
parse_json_filename() will only return a correctly typed dict if the
user used the correct types, or if all values were strings (which can be
converted with bdrv_type_blockdev_opts()).  Forbidding all other cases
could be seen as a bug fix, though.

But even more importantly, bdrv_open_string_opts() may receive a dict
that does not match the schema because there are string values that do
not match it.
---
 blockdev.c               | 8 ++++++--
 qemu-img.c               | 3 ++-
 qemu-io.c                | 2 +-
 qemu-nbd.c               | 2 +-
 tests/test-replication.c | 6 +++---
 5 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 9d4955f23e..3e1125cbfa 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -595,7 +595,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
             bdrv_flags |= BDRV_O_INACTIVE;
         }
 
-        blk = blk_new_open(file, NULL, bs_opts, bdrv_flags, errp);
+        blk = blk_new_open_string_opts(file, bs_opts, bdrv_flags, errp);
         if (!blk) {
             goto err_no_bs_opts;
         }
@@ -670,7 +670,11 @@ static BlockDriverState *bds_tree_init(QDict *bs_opts, bool string_opts,
         bdrv_flags |= BDRV_O_INACTIVE;
     }
 
-    return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp);
+    if (string_opts) {
+        return bdrv_open_string_opts(NULL, bs_opts, bdrv_flags, errp);
+    } else {
+        return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp);
+    }
 }
 
 void blockdev_close_all_bdrv_states(void)
diff --git a/qemu-img.c b/qemu-img.c
index 42b60917b0..a29d76797f 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -284,7 +284,7 @@ static BlockBackend *img_open_opts(const char *optstr,
         }
         qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on");
     }
-    blk = blk_new_open(NULL, NULL, options, flags, &local_err);
+    blk = blk_new_open_string_opts(NULL, options, flags, &local_err);
     if (!blk) {
         error_reportf_err(local_err, "Could not open '%s': ", optstr);
         return NULL;
@@ -294,6 +294,7 @@ static BlockBackend *img_open_opts(const char *optstr,
     return blk;
 }
 
+/* @options must be correctly typed */
 static BlockBackend *img_open_file(const char *filename,
                                    QDict *options,
                                    const char *fmt, int flags,
diff --git a/qemu-io.c b/qemu-io.c
index 0755a30447..544e47b7fc 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -102,7 +102,7 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share,
         }
         qdict_put_str(opts, BDRV_OPT_FORCE_SHARE, "on");
     }
-    qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err);
+    qemuio_blk = blk_new_open_string_opts(name, opts, flags, &local_err);
     if (!qemuio_blk) {
         error_reportf_err(local_err, "can't open%s%s: ",
                           name ? " device " : "", name ?: "");
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 0af0560ad1..e5e1877106 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -944,7 +944,7 @@ int main(int argc, char **argv)
         }
         options = qemu_opts_to_qdict(opts, NULL);
         qemu_opts_reset(&file_opts);
-        blk = blk_new_open(NULL, NULL, options, flags, &local_err);
+        blk = blk_new_open_string_opts(NULL, options, flags, &local_err);
     } else {
         if (fmt) {
             options = qdict_new();
diff --git a/tests/test-replication.c b/tests/test-replication.c
index 68c0d04f2a..5a2bae66fd 100644
--- a/tests/test-replication.c
+++ b/tests/test-replication.c
@@ -191,7 +191,7 @@ static BlockBackend *start_primary(void)
     qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
     qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
 
-    blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &local_err);
+    blk = blk_new_open_string_opts(NULL, qdict, BDRV_O_RDWR, &local_err);
     g_assert(blk);
     g_assert(!local_err);
 
@@ -323,7 +323,7 @@ static BlockBackend *start_secondary(void)
     qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
     qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
 
-    blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &local_err);
+    blk = blk_new_open_string_opts(NULL, qdict, BDRV_O_RDWR, &local_err);
     assert(blk);
     monitor_add_blk(blk, S_LOCAL_DISK_ID, &local_err);
     g_assert(!local_err);
@@ -350,7 +350,7 @@ static BlockBackend *start_secondary(void)
     qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
     qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
 
-    blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &local_err);
+    blk = blk_new_open_string_opts(NULL, qdict, BDRV_O_RDWR, &local_err);
     assert(blk);
     monitor_add_blk(blk, S_ID, &local_err);
     g_assert(!local_err);
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [Qemu-devel] [RFC 7/7] iotests: Test internal option typing
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
                   ` (5 preceding siblings ...)
  2018-05-02 21:32 ` [Qemu-devel] [RFC 6/7] block: Use {blk_new, bdrv}_open_string_opts() Max Reitz
@ 2018-05-02 21:32 ` Max Reitz
  2018-05-02 21:36 ` [Qemu-devel] [Qemu-block] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
  2018-05-03  8:16 ` [Qemu-devel] " Markus Armbruster
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:32 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Markus Armbruster

It would be nice if qemu used the correct types for blockdev options
internally, even if the user specified string values (either through
-drive or by being not so nice and using json:{} with string values).
This patch adds a test verifying that fact.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 tests/qemu-iotests/089     | 12 ++++++++++++
 tests/qemu-iotests/089.out |  5 +++++
 2 files changed, 17 insertions(+)

diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089
index aa1ba4a98e..640ecbfa2a 100755
--- a/tests/qemu-iotests/089
+++ b/tests/qemu-iotests/089
@@ -145,6 +145,18 @@ $QEMU_IO -c "open -o driver=qcow2 json:{\"file.filename\":\"$TEST_IMG\"}" \
 $QEMU_IO -c "open -o driver=qcow2 json:{\"driver\":\"raw\",\"file.filename\":\"$TEST_IMG\"}" \
          -c "info" 2>&1 | _filter_img_info
 
+echo
+echo "=== Testing option typing ==="
+echo
+
+# json:{} accepts both strings and correctly typed values (even mixed,
+# although we probably do not want to support that...), but when
+# creating a json:{} filename, it should be correctly typed.
+# Therefore, both of these should make the "size" value an integer.
+
+TEST_IMG="json:{'driver': 'null-co', 'size':  42 }" _img_info | grep '^image'
+TEST_IMG="json:{'driver': 'null-co', 'size': '42'}" _img_info | grep '^image'
+
 
 # success, all done
 echo "*** done"
diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out
index 89e3e4340a..d880fd76f7 100644
--- a/tests/qemu-iotests/089.out
+++ b/tests/qemu-iotests/089.out
@@ -49,4 +49,9 @@ vm state offset: 512 MiB
 format name: IMGFMT
 cluster size: 64 KiB
 vm state offset: 512 MiB
+
+=== Testing option typing ===
+
+image: json:{"driver": "null-co", "size": 42}
+image: json:{"driver": "null-co", "size": 42}
 *** done
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [Qemu-block] [RFC 0/7] block: Try to use correctly typed blockdev options
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
                   ` (6 preceding siblings ...)
  2018-05-02 21:32 ` [Qemu-devel] [RFC 7/7] iotests: Test internal option typing Max Reitz
@ 2018-05-02 21:36 ` Max Reitz
  2018-05-03  8:16 ` [Qemu-devel] " Markus Armbruster
  8 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-02 21:36 UTC (permalink / raw)
  To: qemu-block; +Cc: Kevin Wolf, Markus Armbruster, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 4605 bytes --]

On 2018-05-02 23:32, Max Reitz wrote:
> (Sorry, Markus, sorry, Kevin, if this series makes you angry.)
> 
> The subject says it all, I think.  The original issue I was assigned to
> solve is this:
> 
>     $ ./qemu-img info --image-opts driver=null-co,size=42
>     image: json:{"driver": "null-co", "size": "42"}
>     [...]
> 
> As you can see, "size" gets a string value in the json:{} object
> although it is an integer, according to the QAPI schema.  (Buglink:
> https://bugzilla.redhat.com/show_bug.cgi?id=1534396)
> 
> My first approach to fix this was to try to pipe the full_open_options
> dict generated in bdrv_refresh_filename() through a keyval input visitor
> and then an output visitor (which would have depended on my filename
> series).  This did not work out because bs->options (where all of the
> reconstructed options come from) may either be correctly typed or not,
> and keyval chokes on non-string values.  I could have probably converted
> bs->options to fully string values before trying to pipe it through
> keyval, but I decided I didn't like that very much.  (I suppose I can be
> convinced otherwise, though.)
> 
> So I decided to venture into the territory of what we actually want at
> some point: Correctly typed bs->options.  Or, rather, in the end we want
> bs->options to be BlockdevOptions, but let's not be too hasty here.
> 
> So it turns out that with really just a bit of work we can separate the
> interfaces that generate correctly typed options (e.g. blockdev-add)
> from the ones that generate pure string-valued options (e.g. qemu-img
> --image-opts) rather well.  Once we have done that, we can pipe all of
> the pure string options through keyval and back to get correctly typed
> options.
> 
> So far the theory.  Now, in practice we of course have pitfalls, and
> those are not addressed by this series, which is the main reason this is
> an RFC.
> 
> The first pitfall (I can see...) is that json:{} allows you to specify
> mixed options -- some values are incorrectly strings, others are
> non-strings.  keyval cannot cope with that, so the result after this
> series is that those options end up in bs->options just like that.  I
> suppose we could just forbid that mixed-typed case, though, and call it
> a bug fix.
> 
> The second problem (and I think the big reason why we have not
> approached this issue so far) is that there are options which you can
> give as strings, but which are not covered by the schema.  In such a
> case, the input visitor will reject the QDict and we will just use it
> as-is, that is, full of strings.  Now that is not what we want in the
> end, of course -- we want everything to be converted into something that
> is covered by the schema.
> 
> My reasoning for sending this series anyway is that it doesn't make
> things worse (bs->options is a mess already, you can never be certain
> that it contains correctly typed values or just strings or both), and
> that it at least gives a starting point from which we can continue on.
> After this series, we have a clear separation between the interfaces
> that use purely string values and the ones that provide correct typing
> (well, and json:{}).
> 
> Oh, and it fixes the above BZ for the more common cases.
> 
> 
> Max Reitz (7):
>   qdict: Add qdict_set_default_bool()
>   block: Let change-medium add detect-zeroes as bool
>   block: Make use of qdict_set_default_bool()
>   block: Add bdrv_open_string_opts()
>   block: Add blk_new_open_string_opts()
>   block: Use {blk_new,bdrv}_open_string_opts()
>   iotests: Test internal option typing
> 
>  include/block/block.h          |  2 +
>  include/qapi/qmp/qdict.h       |  1 +
>  include/sysemu/block-backend.h |  2 +
>  block.c                        | 96 ++++++++++++++++++++++++++++++++++++++----
>  block/block-backend.c          | 30 ++++++++++++-
>  block/vvfat.c                  |  4 +-
>  blockdev.c                     | 33 ++++++++++-----
>  qemu-img.c                     |  3 +-
>  qemu-io.c                      |  2 +-
>  qemu-nbd.c                     |  2 +-
>  qobject/qdict.c                | 13 ++++++
>  tests/test-replication.c       |  6 +--
>  tests/qemu-iotests/089         | 12 ++++++
>  tests/qemu-iotests/089.out     |  5 +++
>  14 files changed, 183 insertions(+), 28 deletions(-)

(Forgot to mention: This series depends on my "qemu-io/img: Fix
-U/force-share conflict testing" series.

Based-on: <20180502202051.15493-1-mreitz@redhat.com>

But who builds an RFC anyway... *me ducks*)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options
  2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
                   ` (7 preceding siblings ...)
  2018-05-02 21:36 ` [Qemu-devel] [Qemu-block] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
@ 2018-05-03  8:16 ` Markus Armbruster
  2018-05-04 15:53   ` Max Reitz
  8 siblings, 1 reply; 12+ messages in thread
From: Markus Armbruster @ 2018-05-03  8:16 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, Kevin Wolf, qemu-devel, Eric Blake

Max Reitz <mreitz@redhat.com> writes:

> (Sorry, Markus, sorry, Kevin, if this series makes you angry.)

Anger?  Nah.  Gallows humor :)

> The subject says it all, I think.  The original issue I was assigned to
> solve is this:
>
>     $ ./qemu-img info --image-opts driver=null-co,size=42
>     image: json:{"driver": "null-co", "size": "42"}
>     [...]
>
> As you can see, "size" gets a string value in the json:{} object
> although it is an integer, according to the QAPI schema.  (Buglink:
> https://bugzilla.redhat.com/show_bug.cgi?id=1534396)
>
> My first approach to fix this was to try to pipe the full_open_options
> dict generated in bdrv_refresh_filename() through a keyval input visitor
> and then an output visitor (which would have depended on my filename
> series).  This did not work out because bs->options (where all of the
> reconstructed options come from) may either be correctly typed or not,
> and keyval chokes on non-string values.  I could have probably converted
> bs->options to fully string values before trying to pipe it through
> keyval, but I decided I didn't like that very much.  (I suppose I can be
> convinced otherwise, though.)

See also thread

    [RFC][BROKEN] rbd: Allow configuration of authentication scheme"
    Message-Id: <20180405170619.20480-1-kwolf@redhat.com>
    https://lists.gnu.org/archive/html/qemu-devel/2018-04/msg00618.html

in particular the qdict_stringify_entries() proposal and its discussion.

> So I decided to venture into the territory of what we actually want at
> some point: Correctly typed bs->options.  Or, rather, in the end we want
> bs->options to be BlockdevOptions, but let's not be too hasty here.
>
> So it turns out that with really just a bit of work we can separate the
> interfaces that generate correctly typed options (e.g. blockdev-add)
> from the ones that generate pure string-valued options (e.g. qemu-img
> --image-opts) rather well.  Once we have done that, we can pipe all of
> the pure string options through keyval and back to get correctly typed
> options.
>
> So far the theory.  Now, in practice we of course have pitfalls, and
> those are not addressed by this series, which is the main reason this is
> an RFC.
>
> The first pitfall (I can see...) is that json:{} allows you to specify
> mixed options -- some values are incorrectly strings, others are
> non-strings.  keyval cannot cope with that, so the result after this
> series is that those options end up in bs->options just like that.  I
> suppose we could just forbid that mixed-typed case, though, and call it
> a bug fix.

Related: QMP device_add and netdev_add convert their arguments from
QDict to QemuOpts with qemu_opts_from_qdict(), and therefore accept
string in addition to number / bool.  If I remember correctly, Eric
Blake's patches to QAPIfy netdev_add got stuck because they didn't
replicate that behavior.

I think we should reject inappropriate strings.  If we think nobody's
relying on it, we can call it a bug and just fix it.  If we don't dare,
we need to go through the deprecation ritual.  We should've done that
long ago, but better now than never.

Same for json:{}.

> The second problem (and I think the big reason why we have not
> approached this issue so far) is that there are options which you can
> give as strings, but which are not covered by the schema.  In such a
> case, the input visitor will reject the QDict and we will just use it
> as-is, that is, full of strings.  Now that is not what we want in the
> end, of course -- we want everything to be converted into something that
> is covered by the schema.

Can you give an example?

> My reasoning for sending this series anyway is that it doesn't make
> things worse (bs->options is a mess already, you can never be certain
> that it contains correctly typed values or just strings or both), and
> that it at least gives a starting point from which we can continue on.
> After this series, we have a clear separation between the interfaces
> that use purely string values and the ones that provide correct typing
> (well, and json:{}).
>
> Oh, and it fixes the above BZ for the more common cases.

"for the more common cases", ha!

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options
  2018-05-03  8:16 ` [Qemu-devel] " Markus Armbruster
@ 2018-05-04 15:53   ` Max Reitz
  2018-05-04 16:11     ` Max Reitz
  0 siblings, 1 reply; 12+ messages in thread
From: Max Reitz @ 2018-05-04 15:53 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-block, Kevin Wolf, qemu-devel, Eric Blake

[-- Attachment #1: Type: text/plain, Size: 9646 bytes --]

On 2018-05-03 10:16, Markus Armbruster wrote:
> Max Reitz <mreitz@redhat.com> writes:
> 
>> (Sorry, Markus, sorry, Kevin, if this series makes you angry.)
> 
> Anger?  Nah.  Gallows humor :)

Good!  I like to make people happy wherever I can.

>> The subject says it all, I think.  The original issue I was assigned to
>> solve is this:
>>
>>     $ ./qemu-img info --image-opts driver=null-co,size=42
>>     image: json:{"driver": "null-co", "size": "42"}
>>     [...]
>>
>> As you can see, "size" gets a string value in the json:{} object
>> although it is an integer, according to the QAPI schema.  (Buglink:
>> https://bugzilla.redhat.com/show_bug.cgi?id=1534396)
>>
>> My first approach to fix this was to try to pipe the full_open_options
>> dict generated in bdrv_refresh_filename() through a keyval input visitor
>> and then an output visitor (which would have depended on my filename
>> series).  This did not work out because bs->options (where all of the
>> reconstructed options come from) may either be correctly typed or not,
>> and keyval chokes on non-string values.  I could have probably converted
>> bs->options to fully string values before trying to pipe it through
>> keyval, but I decided I didn't like that very much.  (I suppose I can be
>> convinced otherwise, though.)
> 
> See also thread
> 
>     [RFC][BROKEN] rbd: Allow configuration of authentication scheme"
>     Message-Id: <20180405170619.20480-1-kwolf@redhat.com>
>     https://lists.gnu.org/archive/html/qemu-devel/2018-04/msg00618.html
> 
> in particular the qdict_stringify_entries() proposal and its discussion.

Ah, right.

So if I did that I should note that the limitation below "works for the
more common cases" would still apply, of course.  The main difference is
that it wouldn't be so important anymore because at least it wouldn't
affect other parts of the block layer's option parsing, but only
generation of json:{} filenames.

>> So I decided to venture into the territory of what we actually want at
>> some point: Correctly typed bs->options.  Or, rather, in the end we want
>> bs->options to be BlockdevOptions, but let's not be too hasty here.
>>
>> So it turns out that with really just a bit of work we can separate the
>> interfaces that generate correctly typed options (e.g. blockdev-add)
>> from the ones that generate pure string-valued options (e.g. qemu-img
>> --image-opts) rather well.  Once we have done that, we can pipe all of
>> the pure string options through keyval and back to get correctly typed
>> options.
>>
>> So far the theory.  Now, in practice we of course have pitfalls, and
>> those are not addressed by this series, which is the main reason this is
>> an RFC.
>>
>> The first pitfall (I can see...) is that json:{} allows you to specify
>> mixed options -- some values are incorrectly strings, others are
>> non-strings.  keyval cannot cope with that, so the result after this
>> series is that those options end up in bs->options just like that.  I
>> suppose we could just forbid that mixed-typed case, though, and call it
>> a bug fix.
> 
> Related: QMP device_add and netdev_add convert their arguments from
> QDict to QemuOpts with qemu_opts_from_qdict(), and therefore accept
> string in addition to number / bool.  If I remember correctly, Eric
> Blake's patches to QAPIfy netdev_add got stuck because they didn't
> replicate that behavior.
> 
> I think we should reject inappropriate strings.  If we think nobody's
> relying on it, we can call it a bug and just fix it.  If we don't dare,
> we need to go through the deprecation ritual.  We should've done that
> long ago, but better now than never.

Sure we should reject them, but we can only do that once either
everything well-formed is converted automatically correctly, or once we
have deprecated the rest (which implies having a QAPI alternative).

> Same for json:{}.

Right.

>> The second problem (and I think the big reason why we have not
>> approached this issue so far) is that there are options which you can
>> give as strings, but which are not covered by the schema.  In such a
>> case, the input visitor will reject the QDict and we will just use it
>> as-is, that is, full of strings.  Now that is not what we want in the
>> end, of course -- we want everything to be converted into something that
>> is covered by the schema.
> 
> Can you give an example?

I looked into qcow2's overlap checking the other day but I was amazed
that someone actually managed to convert my mess into a proper QAPI
definition.

Well, rbd's password-secret comes to mind.  I think we should be good
apart from that...?

Oh no, I just stumbled over bdrv_parse_filename(). m(
I totally missed that.  This has the following effect:

$ ./qemu-img info --image-opts \
    driver=blkdebug,image.driver=null-co,align=512
image: json:{"image": {"driver": "null-co"}, "driver": "blkdebug",
"align": 512}

$ ./qemu-img info --image-opts \
    driver=blkdebug,x-image=null-co://,align=512
image: json:{"image": {"driver": "null-co"}, "driver": "blkdebug",
"align": "512"}

x-image (which is usually set by blkdebug_parse_filename()) is taken by
blkdebug_open() to open the "image" child.  But before that it breaks
conversion of the whole thing into BlockdevOptions, of course...

Of course that is cheating.  You're not allowed to use x-image like
that, actually, qemu just doesn't complain about it (yet).

So if you use it the "proper" way:

$ x86_64-softmmu/qemu-system-x86_64 -nodefaults \
    -drive if=none,driver=raw,file=blkdebug::null-co://,file.align=512 \
    -monitor stdio
QEMU 2.12.50 monitor - type 'help' for more information
(qemu) info block
none0 (#block238): json:{"driver": "raw", "file": {"image": {"driver":
"null-co"}, "driver": "blkdebug", "align": "512"}} (raw)

Well... Looks like the same issue, but it's different, actually.
blkdebug_parse_filename() only adds "x-image" after the conversion to
BlockdevOptions and back, so that can't be the thing that breaks the
conversion here.  No, the thing here is the "filename" option which is
not something that blkdebug supports according to the QAPI schema...

(And there are many protocol drivers like this, where the filename can
be very complex and is decomposed into proper QAPI options by
bdrv_parse_filename().  Those filenames of course are not part of the
QAPI schema, then.)

Of course the fun doesn't end here.  rbd for instance has a filename
that is decomposed into things that cannot be represented with QAPI at
all[1].  It has these great arbitrary "keyvalue-pairs" that just bypass
everything and go right into rados_conf_set().

The rest is rather tame, they just produce correct QAPI options
(although sometimes stringified) from their filename.


[1] Well, technically the same is true for blkdebug and blkverify.  The
options their filenames are decomposed into are again complex filenames
which need to go through bdrv_parse_filename() themselves.  But the
difference is that after they have opened their children, they can
discard those non-QAPI options because opening those child nodes
automatically produces the proper QAPI equivalent.

So all they need to do is keep the child filenames around somewhere
until their bdrv_open() implementation, and then they can discard it
because we now know the proper QAPI options for their children.

>> My reasoning for sending this series anyway is that it doesn't make
>> things worse (bs->options is a mess already, you can never be certain
>> that it contains correctly typed values or just strings or both), and
>> that it at least gives a starting point from which we can continue on.
>> After this series, we have a clear separation between the interfaces
>> that use purely string values and the ones that provide correct typing
>> (well, and json:{}).
>>
>> Oh, and it fixes the above BZ for the more common cases.
> 
> "for the more common cases", ha!

Yeah...  Well, if it had been only for the two cases I mentioned in the
cover letter (mixed-type json:{} and string options we don't have a QAPI
equivalent for yet), it would have been pretty much fine.  But now with
the filename chaos...  That's a bit different.


OK, collection time.

Option 1: Implement qdict_stringify() and do the whole magic in
bdrv_refresh_filename(), only for expressing json:{} filenames.  One
issue with this is that it would completely choke on rbd's
keyvalue-pairs.  (But really, who cares.)
OK, I've just seen that we could handle null just well...  Although
floats seem like a funny thing to do (albeit not impossible).

Option 2: Do the whole bdrv_parse_filename() thing before typing the
QDict.  Will choke on rbd's keyvalue-pairs as well, so not better in any
way.

Option 3: Keep doing what this series did.  Find out why "filename" is
an option in the QDict in the first place, because I would have assumed
that you need to specify it as the @filename parameter of bdrv_open()
instead of through the QDict.  Secondly, bdrv_parse_filename()
implementations would need to always put correctly typed values into the
QDict (easy fix).
The advantage of this is that it could cope with the keyvalue-pairs
option because it doesn't exist when the typing is done; and it would
just end up as a string in the json:{} object (which qemu could
interpret).  The question is whether this effort would be worth it.

I suppose I'll look into #3 for a bit if only out of curiosity, and if
that doesn't work, fall back to #1.

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options
  2018-05-04 15:53   ` Max Reitz
@ 2018-05-04 16:11     ` Max Reitz
  0 siblings, 0 replies; 12+ messages in thread
From: Max Reitz @ 2018-05-04 16:11 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-block, Kevin Wolf, qemu-devel, Eric Blake

[-- Attachment #1: Type: text/plain, Size: 1932 bytes --]

On 2018-05-04 17:53, Max Reitz wrote:

[...]

> So if you use it the "proper" way:
> 
> $ x86_64-softmmu/qemu-system-x86_64 -nodefaults \
>     -drive if=none,driver=raw,file=blkdebug::null-co://,file.align=512 \
>     -monitor stdio
> QEMU 2.12.50 monitor - type 'help' for more information
> (qemu) info block
> none0 (#block238): json:{"driver": "raw", "file": {"image": {"driver":
> "null-co"}, "driver": "blkdebug", "align": "512"}} (raw)
> 
> Well... Looks like the same issue, but it's different, actually.
> blkdebug_parse_filename() only adds "x-image" after the conversion to
> BlockdevOptions and back, so that can't be the thing that breaks the
> conversion here.  No, the thing here is the "filename" option which is
> not something that blkdebug supports according to the QAPI schema...

[...]

> Option 3: Keep doing what this series did.  Find out why "filename" is
> an option in the QDict in the first place, because I would have assumed
> that you need to specify it as the @filename parameter of bdrv_open()
> instead of through the QDict.

Solved the mystery.  The issue isn't "filename" in the QDict (it
actually isn't there), but the fact that -drive allows you to leave out
so many essential options.  In the above command line, for instance, I
left out file.driver and file.image.driver.  Both are derived by
bdrv_open(), but are not set at the point where I'm trying to do the typing.

(And this gets better with something like -drive file=foo.img where even
file.filename needs to be derived.  Or -drive file=nbd://localhost,
where it's not even file.filename but file.server.*, which comes from
nbd_parse_filename()...)

Sooo...  Guess I'll go to option #1.

(And in the long run, I suppose we need to pull out the whole
"derivation infrastructure" which includes probing and
bdrv_parse_filename() and do the conversion to BlockdevOptions afterwards.)

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2018-05-04 16:11 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-02 21:32 [Qemu-devel] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
2018-05-02 21:32 ` [Qemu-devel] [RFC 1/7] qdict: Add qdict_set_default_bool() Max Reitz
2018-05-02 21:32 ` [Qemu-devel] [RFC 2/7] block: Let change-medium add detect-zeroes as bool Max Reitz
2018-05-02 21:32 ` [Qemu-devel] [RFC 3/7] block: Make use of qdict_set_default_bool() Max Reitz
2018-05-02 21:32 ` [Qemu-devel] [RFC 4/7] block: Add bdrv_open_string_opts() Max Reitz
2018-05-02 21:32 ` [Qemu-devel] [RFC 5/7] block: Add blk_new_open_string_opts() Max Reitz
2018-05-02 21:32 ` [Qemu-devel] [RFC 6/7] block: Use {blk_new, bdrv}_open_string_opts() Max Reitz
2018-05-02 21:32 ` [Qemu-devel] [RFC 7/7] iotests: Test internal option typing Max Reitz
2018-05-02 21:36 ` [Qemu-devel] [Qemu-block] [RFC 0/7] block: Try to use correctly typed blockdev options Max Reitz
2018-05-03  8:16 ` [Qemu-devel] " Markus Armbruster
2018-05-04 15:53   ` Max Reitz
2018-05-04 16:11     ` Max Reitz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.