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

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