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

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