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

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