Blame SOURCES/kvm-block-Fix-blockdev-blockdev-add-for-empty-objects-an.patch

1bdc94
From 91f887335e57e6cdd891d7341ac070ca9359ac82 Mon Sep 17 00:00:00 2001
1bdc94
From: Markus Armbruster <armbru@redhat.com>
1bdc94
Date: Mon, 18 Jun 2018 08:43:28 +0200
1bdc94
Subject: [PATCH 21/54] block: Fix -blockdev / blockdev-add for empty objects
1bdc94
 and arrays
1bdc94
1bdc94
RH-Author: Markus Armbruster <armbru@redhat.com>
1bdc94
Message-id: <20180618084330.30009-22-armbru@redhat.com>
1bdc94
Patchwork-id: 80726
1bdc94
O-Subject: [RHEL-7.6 qemu-kvm-rhev PATCH 21/23] block: Fix -blockdev / blockdev-add for empty objects and arrays
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
-blockdev and blockdev-add silently ignore empty objects and arrays in
1bdc94
their argument.  That's because qmp_blockdev_add() converts the
1bdc94
argument to a flat QDict, and qdict_flatten() eats empty QDict and
1bdc94
QList members.  For instance, we ignore an empty BlockdevOptions
1bdc94
member @cache.  No real harm, as absent means the same as empty there.
1bdc94
1bdc94
Thus, the flaw puts an artificial restriction on the QAPI schema: we
1bdc94
can't have potentially empty objects and arrays within
1bdc94
BlockdevOptions, except when they're optional and "empty" has the same
1bdc94
meaning as "absent".
1bdc94
1bdc94
Our QAPI schema satisfies this restriction (I checked), but it's a
1bdc94
trap for the unwary, and a temptation to employ awkward workarounds
1bdc94
for the wary.  Let's get rid of it.
1bdc94
1bdc94
Change qdict_flatten() and qdict_crumple() to treat empty dictionaries
1bdc94
and lists exactly like scalars.
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 2860b2b2cb883969c8f6464bd9f8bc88742c5c73)
1bdc94
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
1bdc94
---
1bdc94
 qobject/block-qdict.c     | 54 +++++++++++++++++++++++++++++------------------
1bdc94
 tests/check-block-qdict.c | 38 ++++++++++++++++++++++++++-------
1bdc94
 2 files changed, 63 insertions(+), 29 deletions(-)
1bdc94
1bdc94
diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c
1bdc94
index e51a3d2..df83308 100644
1bdc94
--- a/qobject/block-qdict.c
1bdc94
+++ b/qobject/block-qdict.c
1bdc94
@@ -56,6 +56,8 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
1bdc94
 {
1bdc94
     QObject *value;
1bdc94
     const QListEntry *entry;
1bdc94
+    QDict *dict_val;
1bdc94
+    QList *list_val;
1bdc94
     char *new_key;
1bdc94
     int i;
1bdc94
 
1bdc94
@@ -69,16 +71,18 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
1bdc94
 
1bdc94
     for (i = 0; entry; entry = qlist_next(entry), i++) {
1bdc94
         value = qlist_entry_obj(entry);
1bdc94
+        dict_val = qobject_to(QDict, value);
1bdc94
+        list_val = qobject_to(QList, value);
1bdc94
         new_key = g_strdup_printf("%s.%i", prefix, i);
1bdc94
 
1bdc94
         /*
1bdc94
          * Flatten non-empty QDict and QList recursively into @target,
1bdc94
          * copy other objects to @target
1bdc94
          */
1bdc94
-        if (qobject_type(value) == QTYPE_QDICT) {
1bdc94
-            qdict_flatten_qdict(qobject_to(QDict, value), target, new_key);
1bdc94
-        } else if (qobject_type(value) == QTYPE_QLIST) {
1bdc94
-            qdict_flatten_qlist(qobject_to(QList, value), target, new_key);
1bdc94
+        if (dict_val && qdict_size(dict_val)) {
1bdc94
+            qdict_flatten_qdict(dict_val, target, new_key);
1bdc94
+        } else if (list_val && !qlist_empty(list_val)) {
1bdc94
+            qdict_flatten_qlist(list_val, target, new_key);
1bdc94
         } else {
1bdc94
             qdict_put_obj(target, new_key, qobject_ref(value));
1bdc94
         }
1bdc94
@@ -91,6 +95,8 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
1bdc94
 {
1bdc94
     QObject *value;
1bdc94
     const QDictEntry *entry, *next;
1bdc94
+    QDict *dict_val;
1bdc94
+    QList *list_val;
1bdc94
     char *new_key;
1bdc94
 
1bdc94
     entry = qdict_first(qdict);
1bdc94
@@ -98,6 +104,8 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
1bdc94
     while (entry != NULL) {
1bdc94
         next = qdict_next(qdict, entry);
1bdc94
         value = qdict_entry_value(entry);
1bdc94
+        dict_val = qobject_to(QDict, value);
1bdc94
+        list_val = qobject_to(QList, value);
1bdc94
         new_key = NULL;
1bdc94
 
1bdc94
         if (prefix) {
1bdc94
@@ -108,12 +116,12 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
1bdc94
          * Flatten non-empty QDict and QList recursively into @target,
1bdc94
          * copy other objects to @target
1bdc94
          */
1bdc94
-        if (qobject_type(value) == QTYPE_QDICT) {
1bdc94
-            qdict_flatten_qdict(qobject_to(QDict, value), target,
1bdc94
+        if (dict_val && qdict_size(dict_val)) {
1bdc94
+            qdict_flatten_qdict(dict_val, target,
1bdc94
                                 new_key ? new_key : entry->key);
1bdc94
             qdict_del(qdict, entry->key);
1bdc94
-        } else if (qobject_type(value) == QTYPE_QLIST) {
1bdc94
-            qdict_flatten_qlist(qobject_to(QList, value), target,
1bdc94
+        } else if (list_val && !qlist_empty(list_val)) {
1bdc94
+            qdict_flatten_qlist(list_val, target,
1bdc94
                                 new_key ? new_key : entry->key);
1bdc94
             qdict_del(qdict, entry->key);
1bdc94
         } else if (target != qdict) {
1bdc94
@@ -127,10 +135,11 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
1bdc94
 }
1bdc94
 
1bdc94
 /**
1bdc94
- * qdict_flatten(): For each nested QDict with key x, all fields with key y
1bdc94
- * are moved to this QDict and their key is renamed to "x.y". For each nested
1bdc94
- * QList with key x, the field at index y is moved to this QDict with the key
1bdc94
- * "x.y" (i.e., the reverse of what qdict_array_split() does).
1bdc94
+ * qdict_flatten(): For each nested non-empty QDict with key x, all
1bdc94
+ * fields with key y are moved to this QDict and their key is renamed
1bdc94
+ * to "x.y". For each nested non-empty QList with key x, the field at
1bdc94
+ * index y is moved to this QDict with the key "x.y" (i.e., the
1bdc94
+ * reverse of what qdict_array_split() does).
1bdc94
  * This operation is applied recursively for nested QDicts and QLists.
1bdc94
  */
1bdc94
 void qdict_flatten(QDict *qdict)
1bdc94
@@ -361,8 +370,8 @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
1bdc94
  * @src: the original flat dictionary (only scalar values) to crumple
1bdc94
  *
1bdc94
  * Takes a flat dictionary whose keys use '.' separator to indicate
1bdc94
- * nesting, and values are scalars, and crumples it into a nested
1bdc94
- * structure.
1bdc94
+ * nesting, and values are scalars, empty dictionaries or empty lists,
1bdc94
+ * and crumples it into a nested structure.
1bdc94
  *
1bdc94
  * To include a literal '.' in a key name, it must be escaped as '..'
1bdc94
  *
1bdc94
@@ -399,6 +408,8 @@ QObject *qdict_crumple(const QDict *src, Error **errp)
1bdc94
 {
1bdc94
     const QDictEntry *ent;
1bdc94
     QDict *two_level, *multi_level = NULL, *child_dict;
1bdc94
+    QDict *dict_val;
1bdc94
+    QList *list_val;
1bdc94
     QObject *dst = NULL, *child;
1bdc94
     size_t i;
1bdc94
     char *prefix = NULL;
1bdc94
@@ -409,10 +420,11 @@ QObject *qdict_crumple(const QDict *src, Error **errp)
1bdc94
 
1bdc94
     /* Step 1: split our totally flat dict into a two level dict */
1bdc94
     for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) {
1bdc94
-        if (qobject_type(ent->value) == QTYPE_QDICT ||
1bdc94
-            qobject_type(ent->value) == QTYPE_QLIST) {
1bdc94
-            error_setg(errp, "Value %s is not a scalar",
1bdc94
-                       ent->key);
1bdc94
+        dict_val = qobject_to(QDict, ent->value);
1bdc94
+        list_val = qobject_to(QList, ent->value);
1bdc94
+        if ((dict_val && qdict_size(dict_val))
1bdc94
+            || (list_val && !qlist_empty(list_val))) {
1bdc94
+            error_setg(errp, "Value %s is not flat", ent->key);
1bdc94
             goto error;
1bdc94
         }
1bdc94
 
1bdc94
@@ -451,9 +463,9 @@ QObject *qdict_crumple(const QDict *src, Error **errp)
1bdc94
     multi_level = qdict_new();
1bdc94
     for (ent = qdict_first(two_level); ent != NULL;
1bdc94
          ent = qdict_next(two_level, ent)) {
1bdc94
-        QDict *dict = qobject_to(QDict, ent->value);
1bdc94
-        if (dict) {
1bdc94
-            child = qdict_crumple(dict, errp);
1bdc94
+        dict_val = qobject_to(QDict, ent->value);
1bdc94
+        if (dict_val && qdict_size(dict_val)) {
1bdc94
+            child = qdict_crumple(dict_val, errp);
1bdc94
             if (!child) {
1bdc94
                 goto error;
1bdc94
             }
1bdc94
diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c
1bdc94
index 2da16f0..1d20fcc 100644
1bdc94
--- a/tests/check-block-qdict.c
1bdc94
+++ b/tests/check-block-qdict.c
1bdc94
@@ -79,10 +79,10 @@ static void qdict_flatten_test(void)
1bdc94
      *     "e.1.2.b": 1,
1bdc94
      *     "f.c": 2,
1bdc94
      *     "f.d": 3,
1bdc94
-     *     "g": 4
1bdc94
+     *     "g": 4,
1bdc94
+     *     "y.0": {},
1bdc94
+     *     "z.a": []
1bdc94
      * }
1bdc94
-     *
1bdc94
-     * Note that "y" and "z" get eaten.
1bdc94
      */
1bdc94
 
1bdc94
     qdict_put_int(e_1_2, "a", 0);
1bdc94
@@ -117,8 +117,10 @@ static void qdict_flatten_test(void)
1bdc94
     g_assert(qdict_get_int(root, "f.c") == 2);
1bdc94
     g_assert(qdict_get_int(root, "f.d") == 3);
1bdc94
     g_assert(qdict_get_int(root, "g") == 4);
1bdc94
+    g_assert(!qdict_size(qdict_get_qdict(root, "y.0")));
1bdc94
+    g_assert(qlist_empty(qdict_get_qlist(root, "z.a")));
1bdc94
 
1bdc94
-    g_assert(qdict_size(root) == 8);
1bdc94
+    g_assert(qdict_size(root) == 10);
1bdc94
 
1bdc94
     qobject_unref(root);
1bdc94
 }
1bdc94
@@ -387,7 +389,8 @@ static void qdict_join_test(void)
1bdc94
 static void qdict_crumple_test_recursive(void)
1bdc94
 {
1bdc94
     QDict *src, *dst, *rule, *vnc, *acl, *listen;
1bdc94
-    QList *rules;
1bdc94
+    QDict *empty, *empty_dict, *empty_list_0;
1bdc94
+    QList *rules, *empty_list, *empty_dict_a;
1bdc94
 
1bdc94
     src = qdict_new();
1bdc94
     qdict_put_str(src, "vnc.listen.addr", "127.0.0.1");
1bdc94
@@ -399,10 +402,12 @@ static void qdict_crumple_test_recursive(void)
1bdc94
     qdict_put_str(src, "vnc.acl.default", "deny");
1bdc94
     qdict_put_str(src, "vnc.acl..name", "acl0");
1bdc94
     qdict_put_str(src, "vnc.acl.rule..name", "acl0");
1bdc94
+    qdict_put(src, "empty.dict.a", qlist_new());
1bdc94
+    qdict_put(src, "empty.list.0", qdict_new());
1bdc94
 
1bdc94
     dst = qobject_to(QDict, qdict_crumple(src, &error_abort));
1bdc94
     g_assert(dst);
1bdc94
-    g_assert_cmpint(qdict_size(dst), ==, 1);
1bdc94
+    g_assert_cmpint(qdict_size(dst), ==, 2);
1bdc94
 
1bdc94
     vnc = qdict_get_qdict(dst, "vnc");
1bdc94
     g_assert(vnc);
1bdc94
@@ -440,6 +445,21 @@ static void qdict_crumple_test_recursive(void)
1bdc94
     g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name"));
1bdc94
     g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name"));
1bdc94
 
1bdc94
+    empty = qdict_get_qdict(dst, "empty");
1bdc94
+    g_assert(empty);
1bdc94
+    g_assert_cmpint(qdict_size(empty), ==, 2);
1bdc94
+    empty_dict = qdict_get_qdict(empty, "dict");
1bdc94
+    g_assert(empty_dict);
1bdc94
+    g_assert_cmpint(qdict_size(empty_dict), ==, 1);
1bdc94
+    empty_dict_a = qdict_get_qlist(empty_dict, "a");
1bdc94
+    g_assert(empty_dict_a && qlist_empty(empty_dict_a));
1bdc94
+    empty_list = qdict_get_qlist(empty, "list");
1bdc94
+    g_assert(empty_list);
1bdc94
+    g_assert_cmpint(qlist_size(empty_list), ==, 1);
1bdc94
+    empty_list_0 = qobject_to(QDict, qlist_pop(empty_list));
1bdc94
+    g_assert(empty_list_0);
1bdc94
+    g_assert_cmpint(qdict_size(empty_list_0), ==, 0);
1bdc94
+
1bdc94
     qobject_unref(src);
1bdc94
     qobject_unref(dst);
1bdc94
 }
1bdc94
@@ -587,7 +607,7 @@ static void qdict_rename_keys_test(void)
1bdc94
 
1bdc94
 static void qdict_crumple_test_bad_inputs(void)
1bdc94
 {
1bdc94
-    QDict *src;
1bdc94
+    QDict *src, *nested;
1bdc94
     Error *error = NULL;
1bdc94
 
1bdc94
     src = qdict_new();
1bdc94
@@ -614,7 +634,9 @@ static void qdict_crumple_test_bad_inputs(void)
1bdc94
 
1bdc94
     src = qdict_new();
1bdc94
     /* The input should be flat, ie no dicts or lists */
1bdc94
-    qdict_put(src, "rule.a", qdict_new());
1bdc94
+    nested = qdict_new();
1bdc94
+    qdict_put(nested, "x", qdict_new());
1bdc94
+    qdict_put(src, "rule.a", nested);
1bdc94
     qdict_put_str(src, "rule.b", "allow");
1bdc94
 
1bdc94
     g_assert(qdict_crumple(src, &error) == NULL);
1bdc94
-- 
1bdc94
1.8.3.1
1bdc94