Blame SOURCES/kvm-block-Fix-blockdev-for-certain-non-string-scalars.patch

357786
From 26de0d4fe93a094dcf7aef4c2b139c46c1117a99 Mon Sep 17 00:00:00 2001
357786
From: Markus Armbruster <armbru@redhat.com>
357786
Date: Mon, 18 Jun 2018 08:43:17 +0200
357786
Subject: [PATCH 10/54] block: Fix -blockdev for certain non-string scalars
357786
357786
RH-Author: Markus Armbruster <armbru@redhat.com>
357786
Message-id: <20180618084330.30009-11-armbru@redhat.com>
357786
Patchwork-id: 80743
357786
O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 10/23] block: Fix -blockdev for certain non-string scalars
357786
Bugzilla: 1557995
357786
RH-Acked-by: Max Reitz <mreitz@redhat.com>
357786
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
357786
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
357786
357786
Configuration flows through the block subsystem in a rather peculiar
357786
way.  Configuration made with -drive enters it as QemuOpts.
357786
Configuration made with -blockdev / blockdev-add enters it as QAPI
357786
type BlockdevOptions.  The block subsystem uses QDict, QemuOpts and
357786
QAPI types internally.  The precise flow is next to impossible to
357786
explain (I tried for this commit message, but gave up after wasting
357786
several hours).  What I can explain is a flaw in the BlockDriver
357786
interface that leads to this bug:
357786
357786
    $ qemu-system-x86_64 -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234
357786
    qemu-system-x86_64: -blockdev node-name=n1,driver=nfs,server.type=inet,server.host=localhost,path=/foo/bar,user=1234: Internal error: parameter user invalid
357786
357786
QMP blockdev-add is broken the same way.
357786
357786
Here's what happens.  The block layer passes configuration represented
357786
as flat QDict (with dotted keys) to BlockDriver methods
357786
.bdrv_file_open().  The QDict's members are typed according to the
357786
QAPI schema.
357786
357786
nfs_file_open() converts it to QAPI type BlockdevOptionsNfs, with
357786
qdict_crumple() and a qobject input visitor.
357786
357786
This visitor comes in two flavors.  The plain flavor requires scalars
357786
to be typed according to the QAPI schema.  That's the case here.  The
357786
keyval flavor requires string scalars.  That's not the case here.
357786
nfs_file_open() uses the latter, and promptly falls apart for members
357786
@user, @group, @tcp-syn-count, @readahead-size, @page-cache-size,
357786
@debug.
357786
357786
Switching to the plain flavor would fix -blockdev, but break -drive,
357786
because there the scalars arrive in nfs_file_open() as strings.
357786
357786
The proper fix would be to replace the QDict by QAPI type
357786
BlockdevOptions in the BlockDriver interface.  Sadly, that's beyond my
357786
reach right now.
357786
357786
Next best would be to fix the block layer to always pass correctly
357786
typed QDicts to the BlockDriver methods.  Also beyond my reach.
357786
357786
What I can do is throw another hack onto the pile: have
357786
nfs_file_open() convert all members to string, so use of the keyval
357786
flavor actually works, by replacing qdict_crumple() by new function
357786
qdict_crumple_for_keyval_qiv().
357786
357786
The pattern "pass result of qdict_crumple() to
357786
qobject_input_visitor_new_keyval()" occurs several times more:
357786
357786
* qemu_rbd_open()
357786
357786
  Same issue as nfs_file_open(), but since BlockdevOptionsRbd has only
357786
  string members, its only a latent bug.  Fix it anyway.
357786
357786
* parallels_co_create_opts(), qcow_co_create_opts(),
357786
  qcow2_co_create_opts(), bdrv_qed_co_create_opts(),
357786
  sd_co_create_opts(), vhdx_co_create_opts(), vpc_co_create_opts()
357786
357786
  These work, because they create the QDict with
357786
  qemu_opts_to_qdict_filtered(), which creates only string scalars.
357786
  The function sports a TODO comment asking for better typing; that's
357786
  going to be fun.  Use qdict_crumple_for_keyval_qiv() to be safe.
357786
357786
Signed-off-by: Markus Armbruster <armbru@redhat.com>
357786
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
357786
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
357786
(cherry picked from commit e5af0da1dcbfb1a4694150f9954554fb6df88819)
357786
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
357786
---
357786
 block/nfs.c           |  2 +-
357786
 block/parallels.c     |  2 +-
357786
 block/qcow.c          |  2 +-
357786
 block/qcow2.c         |  2 +-
357786
 block/qed.c           |  2 +-
357786
 block/rbd.c           |  2 +-
357786
 block/sheepdog.c      |  2 +-
357786
 block/vhdx.c          |  2 +-
357786
 block/vpc.c           |  2 +-
357786
 include/block/qdict.h |  1 +
357786
 qobject/block-qdict.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++
357786
 11 files changed, 67 insertions(+), 9 deletions(-)
357786
357786
diff --git a/block/nfs.c b/block/nfs.c
357786
index 5159ef0..4090d28 100644
357786
--- a/block/nfs.c
357786
+++ b/block/nfs.c
357786
@@ -560,7 +560,7 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
357786
     Visitor *v;
357786
     Error *local_err = NULL;
357786
 
357786
-    crumpled = qdict_crumple(options, errp);
357786
+    crumpled = qdict_crumple_for_keyval_qiv(options, errp);
357786
     if (crumpled == NULL) {
357786
         return NULL;
357786
     }
357786
diff --git a/block/parallels.c b/block/parallels.c
357786
index 0ee1f6a..aa58955 100644
357786
--- a/block/parallels.c
357786
+++ b/block/parallels.c
357786
@@ -651,7 +651,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
357786
     qdict_put_str(qdict, "driver", "parallels");
357786
     qdict_put_str(qdict, "file", bs->node_name);
357786
 
357786
-    qobj = qdict_crumple(qdict, errp);
357786
+    qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
357786
     qobject_unref(qdict);
357786
     qdict = qobject_to(QDict, qobj);
357786
     if (qdict == NULL) {
357786
diff --git a/block/qcow.c b/block/qcow.c
357786
index fb821ad..14b7296 100644
357786
--- a/block/qcow.c
357786
+++ b/block/qcow.c
357786
@@ -995,7 +995,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
357786
     qdict_put_str(qdict, "driver", "qcow");
357786
     qdict_put_str(qdict, "file", bs->node_name);
357786
 
357786
-    qobj = qdict_crumple(qdict, errp);
357786
+    qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
357786
     qobject_unref(qdict);
357786
     qdict = qobject_to(QDict, qobj);
357786
     if (qdict == NULL) {
357786
diff --git a/block/qcow2.c b/block/qcow2.c
357786
index fa9f557..fa06b41 100644
357786
--- a/block/qcow2.c
357786
+++ b/block/qcow2.c
357786
@@ -3139,7 +3139,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
357786
     qdict_put_str(qdict, "file", bs->node_name);
357786
 
357786
     /* Now get the QAPI type BlockdevCreateOptions */
357786
-    qobj = qdict_crumple(qdict, errp);
357786
+    qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
357786
     qobject_unref(qdict);
357786
     qdict = qobject_to(QDict, qobj);
357786
     if (qdict == NULL) {
357786
diff --git a/block/qed.c b/block/qed.c
357786
index 9a8997a..d8810f5 100644
357786
--- a/block/qed.c
357786
+++ b/block/qed.c
357786
@@ -763,7 +763,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
357786
     qdict_put_str(qdict, "driver", "qed");
357786
     qdict_put_str(qdict, "file", bs->node_name);
357786
 
357786
-    qobj = qdict_crumple(qdict, errp);
357786
+    qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
357786
     qobject_unref(qdict);
357786
     qdict = qobject_to(QDict, qobj);
357786
     if (qdict == NULL) {
357786
diff --git a/block/rbd.c b/block/rbd.c
357786
index e695cf2..0b5455f 100644
357786
--- a/block/rbd.c
357786
+++ b/block/rbd.c
357786
@@ -640,7 +640,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
357786
     }
357786
 
357786
     /* Convert the remaining options into a QAPI object */
357786
-    crumpled = qdict_crumple(options, errp);
357786
+    crumpled = qdict_crumple_for_keyval_qiv(options, errp);
357786
     if (crumpled == NULL) {
357786
         r = -EINVAL;
357786
         goto out;
357786
diff --git a/block/sheepdog.c b/block/sheepdog.c
357786
index fd3876f..821a3c4 100644
357786
--- a/block/sheepdog.c
357786
+++ b/block/sheepdog.c
357786
@@ -2218,7 +2218,7 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
357786
     }
357786
 
357786
     /* Get the QAPI object */
357786
-    crumpled = qdict_crumple(qdict, errp);
357786
+    crumpled = qdict_crumple_for_keyval_qiv(qdict, errp);
357786
     if (crumpled == NULL) {
357786
         ret = -EINVAL;
357786
         goto fail;
357786
diff --git a/block/vhdx.c b/block/vhdx.c
357786
index 26c05aa..32939c4 100644
357786
--- a/block/vhdx.c
357786
+++ b/block/vhdx.c
357786
@@ -2003,7 +2003,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename,
357786
     qdict_put_str(qdict, "driver", "vhdx");
357786
     qdict_put_str(qdict, "file", bs->node_name);
357786
 
357786
-    qobj = qdict_crumple(qdict, errp);
357786
+    qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
357786
     qobject_unref(qdict);
357786
     qdict = qobject_to(QDict, qobj);
357786
     if (qdict == NULL) {
357786
diff --git a/block/vpc.c b/block/vpc.c
357786
index 41c8c98..16178e5 100644
357786
--- a/block/vpc.c
357786
+++ b/block/vpc.c
357786
@@ -1119,7 +1119,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
357786
     qdict_put_str(qdict, "driver", "vpc");
357786
     qdict_put_str(qdict, "file", bs->node_name);
357786
 
357786
-    qobj = qdict_crumple(qdict, errp);
357786
+    qobj = qdict_crumple_for_keyval_qiv(qdict, errp);
357786
     qobject_unref(qdict);
357786
     qdict = qobject_to(QDict, qobj);
357786
     if (qdict == NULL) {
357786
diff --git a/include/block/qdict.h b/include/block/qdict.h
357786
index 71c037a..47d9638 100644
357786
--- a/include/block/qdict.h
357786
+++ b/include/block/qdict.h
357786
@@ -21,6 +21,7 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
357786
 void qdict_array_split(QDict *src, QList **dst);
357786
 int qdict_array_entries(QDict *src, const char *subqdict);
357786
 QObject *qdict_crumple(const QDict *src, Error **errp);
357786
+QObject *qdict_crumple_for_keyval_qiv(QDict *qdict, Error **errp);
357786
 void qdict_flatten(QDict *qdict);
357786
 
357786
 typedef struct QDictRenames {
357786
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
357786
index fb92010..aba372c 100644
357786
--- a/qobject/block-qdict.c
357786
+++ b/qobject/block-qdict.c
357786
@@ -9,7 +9,10 @@
357786
 
357786
 #include "qemu/osdep.h"
357786
 #include "block/qdict.h"
357786
+#include "qapi/qmp/qbool.h"
357786
 #include "qapi/qmp/qlist.h"
357786
+#include "qapi/qmp/qnum.h"
357786
+#include "qapi/qmp/qstring.h"
357786
 #include "qemu/cutils.h"
357786
 #include "qapi/error.h"
357786
 
357786
@@ -514,6 +517,60 @@ QObject *qdict_crumple(const QDict *src, Error **errp)
357786
 }
357786
 
357786
 /**
357786
+ * qdict_crumple_for_keyval_qiv:
357786
+ * @src: the flat dictionary (only scalar values) to crumple
357786
+ * @errp: location to store error
357786
+ *
357786
+ * Like qdict_crumple(), but additionally transforms scalar values so
357786
+ * the result can be passed to qobject_input_visitor_new_keyval().
357786
+ *
357786
+ * The block subsystem uses this function to prepare its flat QDict
357786
+ * with possibly confused scalar types for a visit.  It should not be
357786
+ * used for anything else, and it should go away once the block
357786
+ * subsystem has been cleaned up.
357786
+ */
357786
+QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp)
357786
+{
357786
+    QDict *tmp = NULL;
357786
+    char *buf;
357786
+    const char *s;
357786
+    const QDictEntry *ent;
357786
+    QObject *dst;
357786
+
357786
+    for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) {
357786
+        buf = NULL;
357786
+        switch (qobject_type(ent->value)) {
357786
+        case QTYPE_QNULL:
357786
+        case QTYPE_QSTRING:
357786
+            continue;
357786
+        case QTYPE_QNUM:
357786
+            s = buf = qnum_to_string(qobject_to(QNum, ent->value));
357786
+            break;
357786
+        case QTYPE_QDICT:
357786
+        case QTYPE_QLIST:
357786
+            /* @src isn't flat; qdict_crumple() will fail */
357786
+            continue;
357786
+        case QTYPE_QBOOL:
357786
+            s = qbool_get_bool(qobject_to(QBool, ent->value))
357786
+                ? "on" : "off";
357786
+            break;
357786
+        default:
357786
+            abort();
357786
+        }
357786
+
357786
+        if (!tmp) {
357786
+            tmp = qdict_clone_shallow(src);
357786
+        }
357786
+        qdict_put(tmp, ent->key, qstring_from_str(s));
357786
+        g_free(buf);
357786
+    }
357786
+
357786
+    dst = qdict_crumple(tmp ?: src, errp);
357786
+    qobject_unref(tmp);
357786
+    return dst;
357786
+}
357786
+
357786
+/**
357786
  * qdict_array_entries(): Returns the number of direct array entries if the
357786
  * sub-QDict of src specified by the prefix in subqdict (or src itself for
357786
  * prefix == "") is valid as an array, i.e. the length of the created list if
357786
-- 
357786
1.8.3.1
357786