4c8e44
From 66e5e4d83e90be3cecab7bf5f50d0e10fbaa7cea Mon Sep 17 00:00:00 2001
4c8e44
From: "Gao,Yan" <ygao@suse.com>
4c8e44
Date: Fri, 26 Apr 2019 11:52:59 +0200
4c8e44
Subject: [PATCH 1/3] Fix: libcrmcommon: correctly apply XML diffs with
4c8e44
 multiple move/create changes
4c8e44
4c8e44
Given a resource group:
4c8e44
```
4c8e44
<group id="dummies">
4c8e44
  <primitive id="dummy0"/>
4c8e44
  <primitive id="dummy1"/>
4c8e44
  <primitive id="dummy2"/>
4c8e44
  <primitive id="dummy3"/>
4c8e44
  <primitive id="dummy4"/>
4c8e44
</group>
4c8e44
```
4c8e44
4c8e44
, if we'd like to change it to:
4c8e44
```
4c8e44
<group id="dummies">
4c8e44
  <primitive id="dummy3"/>
4c8e44
  <primitive id="dummy4"/>
4c8e44
  <primitive id="dummy2"/>
4c8e44
  <primitive id="dummy0"/>
4c8e44
  <primitive id="dummy1"/>
4c8e44
</group>
4c8e44
```
4c8e44
4c8e44
, the generated XML diff would be like:
4c8e44
```
4c8e44
<diff format="2">
4c8e44
  <change operation="move" path="//primitive[@id=dummy3]" position="0"/>
4c8e44
  <change operation="move" path="//primitive[@id=dummy4]" position="1"/>
4c8e44
  <change operation="move" path="//primitive[@id=dummy0]" position="3"/>
4c8e44
  <change operation="move" path="//primitive[@id=dummy1]" position="4"/>
4c8e44
</diff>
4c8e44
```
4c8e44
4c8e44
Previously after applying the XML diff, the resulting XML would be a mess:
4c8e44
```
4c8e44
<group id="dummies">
4c8e44
  <primitive id="dummy3"/>
4c8e44
  <primitive id="dummy4"/>
4c8e44
  <primitive id="dummy0"/>
4c8e44
  <primitive id="dummy2"/>
4c8e44
  <primitive id="dummy1"/>
4c8e44
</group>
4c8e44
```
4c8e44
It's because the positions of the already moved XML objects could be
4c8e44
affected by the later moved objects.
4c8e44
4c8e44
This commit fixes it by temporarily putting "move" objects after the
4c8e44
last sibling and also delaying the adding of any "create" objects, then
4c8e44
placing them to the target positions in the right order.
4c8e44
---
4c8e44
 lib/common/xml.c | 126 ++++++++++++++++++++++++++++++++++++++++++-------------
4c8e44
 1 file changed, 97 insertions(+), 29 deletions(-)
4c8e44
4c8e44
diff --git a/lib/common/xml.c b/lib/common/xml.c
4c8e44
index 66b5f66..d815a48 100644
4c8e44
--- a/lib/common/xml.c
4c8e44
+++ b/lib/common/xml.c
4c8e44
@@ -1466,11 +1466,40 @@ __xml_find_path(xmlNode *top, const char *key, int target_position)
4c8e44
     return target;
4c8e44
 }
4c8e44
 
4c8e44
+typedef struct xml_change_obj_s {
4c8e44
+    xmlNode *change;
4c8e44
+    xmlNode *match;
4c8e44
+} xml_change_obj_t;
4c8e44
+
4c8e44
+static gint
4c8e44
+sort_change_obj_by_position(gconstpointer a, gconstpointer b)
4c8e44
+{
4c8e44
+    const xml_change_obj_t *change_obj_a = a;
4c8e44
+    const xml_change_obj_t *change_obj_b = b;
4c8e44
+    int position_a = -1;
4c8e44
+    int position_b = -1;
4c8e44
+
4c8e44
+    crm_element_value_int(change_obj_a->change, XML_DIFF_POSITION, &position_a);
4c8e44
+    crm_element_value_int(change_obj_b->change, XML_DIFF_POSITION, &position_b);
4c8e44
+
4c8e44
+    if (position_a < position_b) {
4c8e44
+        return -1;
4c8e44
+
4c8e44
+    } else if (position_a > position_b) {
4c8e44
+        return 1;
4c8e44
+    }
4c8e44
+
4c8e44
+    return 0;
4c8e44
+}
4c8e44
+
4c8e44
 static int
4c8e44
 xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
4c8e44
 {
4c8e44
     int rc = pcmk_ok;
4c8e44
     xmlNode *change = NULL;
4c8e44
+    GListPtr change_objs = NULL;
4c8e44
+    GListPtr gIter = NULL;
4c8e44
+
4c8e44
     for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
4c8e44
         xmlNode *match = NULL;
4c8e44
         const char *op = crm_element_value(change, XML_DIFF_OP);
4c8e44
@@ -1482,6 +1511,7 @@ xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
4c8e44
             continue;
4c8e44
         }
4c8e44
 
4c8e44
+        // "delete" changes for XML comments are generated with "position"
4c8e44
         if(strcmp(op, "delete") == 0) {
4c8e44
             crm_element_value_int(change, XML_DIFF_POSITION, &position);
4c8e44
         }
4c8e44
@@ -1497,7 +1527,71 @@ xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
4c8e44
             rc = -pcmk_err_diff_failed;
4c8e44
             continue;
4c8e44
 
4c8e44
-        } else if(strcmp(op, "create") == 0) {
4c8e44
+        } else if (strcmp(op, "create") == 0 || strcmp(op, "move") == 0) {
4c8e44
+            // Delay the adding of a "create" object
4c8e44
+            xml_change_obj_t *change_obj = calloc(1, sizeof(xml_change_obj_t));
4c8e44
+
4c8e44
+            CRM_ASSERT(change_obj != NULL);
4c8e44
+
4c8e44
+            change_obj->change = change;
4c8e44
+            change_obj->match = match;
4c8e44
+
4c8e44
+            change_objs = g_list_append(change_objs, change_obj);
4c8e44
+
4c8e44
+            if (strcmp(op, "move") == 0) {
4c8e44
+                // Temporarily put the "move" object after the last sibling
4c8e44
+                if (match->parent != NULL && match->parent->last != NULL) {
4c8e44
+                    xmlAddNextSibling(match->parent->last, match);
4c8e44
+                }
4c8e44
+            }
4c8e44
+
4c8e44
+        } else if(strcmp(op, "delete") == 0) {
4c8e44
+            free_xml(match);
4c8e44
+
4c8e44
+        } else if(strcmp(op, "modify") == 0) {
4c8e44
+            xmlAttr *pIter = pcmk__first_xml_attr(match);
4c8e44
+            xmlNode *attrs = __xml_first_child(first_named_child(change, XML_DIFF_RESULT));
4c8e44
+
4c8e44
+            if(attrs == NULL) {
4c8e44
+                rc = -ENOMSG;
4c8e44
+                continue;
4c8e44
+            }
4c8e44
+            while(pIter != NULL) {
4c8e44
+                const char *name = (const char *)pIter->name;
4c8e44
+
4c8e44
+                pIter = pIter->next;
4c8e44
+                xml_remove_prop(match, name);
4c8e44
+            }
4c8e44
+
4c8e44
+            for (pIter = pcmk__first_xml_attr(attrs); pIter != NULL; pIter = pIter->next) {
4c8e44
+                const char *name = (const char *)pIter->name;
4c8e44
+                const char *value = crm_element_value(attrs, name);
4c8e44
+
4c8e44
+                crm_xml_add(match, name, value);
4c8e44
+            }
4c8e44
+
4c8e44
+        } else {
4c8e44
+            crm_err("Unknown operation: %s", op);
4c8e44
+        }
4c8e44
+    }
4c8e44
+
4c8e44
+    // Changes should be generated in the right order. Double checking.
4c8e44
+    change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
4c8e44
+
4c8e44
+    for (gIter = change_objs; gIter; gIter = gIter->next) {
4c8e44
+        xml_change_obj_t *change_obj = gIter->data;
4c8e44
+        xmlNode *match = change_obj->match;
4c8e44
+        const char *op = NULL;
4c8e44
+        const char *xpath = NULL;
4c8e44
+
4c8e44
+        change = change_obj->change;
4c8e44
+
4c8e44
+        op = crm_element_value(change, XML_DIFF_OP);
4c8e44
+        xpath = crm_element_value(change, XML_DIFF_PATH);
4c8e44
+
4c8e44
+        crm_trace("Continue performing %s on %s with %p", op, xpath, match);
4c8e44
+
4c8e44
+        if(strcmp(op, "create") == 0) {
4c8e44
             int position = 0;
4c8e44
             xmlNode *child = NULL;
4c8e44
             xmlNode *match_child = NULL;
4c8e44
@@ -1565,36 +1659,10 @@ xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
4c8e44
                         match->name, ID(match), __xml_offset(match), position, match->prev);
4c8e44
                 rc = -pcmk_err_diff_failed;
4c8e44
             }
4c8e44
-
4c8e44
-        } else if(strcmp(op, "delete") == 0) {
4c8e44
-            free_xml(match);
4c8e44
-
4c8e44
-        } else if(strcmp(op, "modify") == 0) {
4c8e44
-            xmlAttr *pIter = pcmk__first_xml_attr(match);
4c8e44
-            xmlNode *attrs = __xml_first_child(first_named_child(change, XML_DIFF_RESULT));
4c8e44
-
4c8e44
-            if(attrs == NULL) {
4c8e44
-                rc = -ENOMSG;
4c8e44
-                continue;
4c8e44
-            }
4c8e44
-            while(pIter != NULL) {
4c8e44
-                const char *name = (const char *)pIter->name;
4c8e44
-
4c8e44
-                pIter = pIter->next;
4c8e44
-                xml_remove_prop(match, name);
4c8e44
-            }
4c8e44
-
4c8e44
-            for (pIter = pcmk__first_xml_attr(attrs); pIter != NULL; pIter = pIter->next) {
4c8e44
-                const char *name = (const char *)pIter->name;
4c8e44
-                const char *value = crm_element_value(attrs, name);
4c8e44
-
4c8e44
-                crm_xml_add(match, name, value);
4c8e44
-            }
4c8e44
-
4c8e44
-        } else {
4c8e44
-            crm_err("Unknown operation: %s", op);
4c8e44
         }
4c8e44
     }
4c8e44
+
4c8e44
+    g_list_free_full(change_objs, free);
4c8e44
     return rc;
4c8e44
 }
4c8e44
 
4c8e44
-- 
4c8e44
1.8.3.1
4c8e44
4c8e44
4c8e44
From f8d008d8d3a29900ee0c6decbb71a243fa4c2d8c Mon Sep 17 00:00:00 2001
4c8e44
From: "Gao,Yan" <ygao@suse.com>
4c8e44
Date: Tue, 30 Apr 2019 00:15:03 +0200
4c8e44
Subject: [PATCH 2/3] Fix: libcrmcommon: avoid possible use-of-NULL when
4c8e44
 applying XML diffs
4c8e44
4c8e44
---
4c8e44
 lib/common/xml.c | 3 ++-
4c8e44
 1 file changed, 2 insertions(+), 1 deletion(-)
4c8e44
4c8e44
diff --git a/lib/common/xml.c b/lib/common/xml.c
4c8e44
index d815a48..fe87de6 100644
4c8e44
--- a/lib/common/xml.c
4c8e44
+++ b/lib/common/xml.c
4c8e44
@@ -1506,11 +1506,12 @@ xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
4c8e44
         const char *xpath = crm_element_value(change, XML_DIFF_PATH);
4c8e44
         int position = -1;
4c8e44
 
4c8e44
-        crm_trace("Processing %s %s", change->name, op);
4c8e44
         if(op == NULL) {
4c8e44
             continue;
4c8e44
         }
4c8e44
 
4c8e44
+        crm_trace("Processing %s %s", change->name, op);
4c8e44
+
4c8e44
         // "delete" changes for XML comments are generated with "position"
4c8e44
         if(strcmp(op, "delete") == 0) {
4c8e44
             crm_element_value_int(change, XML_DIFF_POSITION, &position);
4c8e44
-- 
4c8e44
1.8.3.1
4c8e44
4c8e44
4c8e44
From e6b2bf0cf7e7ed839583d529b190a7a6cd1bd594 Mon Sep 17 00:00:00 2001
4c8e44
From: "Gao,Yan" <ygao@suse.com>
4c8e44
Date: Tue, 30 Apr 2019 00:19:46 +0200
4c8e44
Subject: [PATCH 3/3] Fix: libcrmcommon: return error when applying XML diffs
4c8e44
 containing unknown operations
4c8e44
4c8e44
---
4c8e44
 lib/common/xml.c | 1 +
4c8e44
 1 file changed, 1 insertion(+)
4c8e44
4c8e44
diff --git a/lib/common/xml.c b/lib/common/xml.c
4c8e44
index fe87de6..940c4b9 100644
4c8e44
--- a/lib/common/xml.c
4c8e44
+++ b/lib/common/xml.c
4c8e44
@@ -1573,6 +1573,7 @@ xml_apply_patchset_v2(xmlNode *xml, xmlNode *patchset)
4c8e44
 
4c8e44
         } else {
4c8e44
             crm_err("Unknown operation: %s", op);
4c8e44
+            rc = -pcmk_err_diff_failed;
4c8e44
         }
4c8e44
     }
4c8e44
 
4c8e44
-- 
4c8e44
1.8.3.1
4c8e44