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

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