|
|
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 |
|