This source file includes following definitions.
- add_xml_changes_to_patchset
- is_config_change
- xml_repair_v1_diff
- xml_create_patchset_v1
- xml_create_patchset_v2
- xml_create_patchset
- patchset_process_digest
- subtract_v1_xml_comment
- subtract_v1_xml_object
- not_id
- process_v1_removals
- process_v1_additions
- find_patch_xml_node
- xml_patch_versions
- xml_patch_version_check
- purge_v1_diff_markers
- apply_v1_patchset
- first_matching_xml_child
- search_v2_xpath
- sort_change_obj_by_position
- apply_v2_patchset
- xml_apply_patchset
- can_prune_leaf_v1
- pcmk__diff_v1_xml_object
- apply_xml_diff
- purge_diff_markers
- diff_xml_object
- subtract_xml_object
- can_prune_leaf
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <time.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <bzlib.h>
20
21 #include <libxml/tree.h>
22
23 #include <crm/crm.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/xml_internal.h>
26 #include "crmcommon_private.h"
27
28
29
30
31 static void
32 add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
33 {
34 xmlNode *cIter = NULL;
35 xmlAttr *pIter = NULL;
36 xmlNode *change = NULL;
37 xml_node_private_t *nodepriv = xml->_private;
38 const char *value = NULL;
39
40 if (nodepriv == NULL) {
41
42
43
44
45 return;
46 }
47
48
49 if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
50 GString *xpath = pcmk__element_xpath(xml->parent);
51
52 if (xpath != NULL) {
53 int position = pcmk__xml_position(xml, pcmk__xf_deleted);
54
55 change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
56
57 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_CREATE);
58 crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
59 crm_xml_add_int(change, PCMK_XE_POSITION, position);
60 pcmk__xml_copy(change, xml);
61 g_string_free(xpath, TRUE);
62 }
63
64 return;
65 }
66
67
68 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
69 pIter = pIter->next) {
70 xmlNode *attr = NULL;
71
72 nodepriv = pIter->_private;
73 if (!pcmk_any_flags_set(nodepriv->flags, pcmk__xf_deleted|pcmk__xf_dirty)) {
74 continue;
75 }
76
77 if (change == NULL) {
78 GString *xpath = pcmk__element_xpath(xml);
79
80 if (xpath != NULL) {
81 change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
82
83 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MODIFY);
84 crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
85
86 change = pcmk__xe_create(change, PCMK_XE_CHANGE_LIST);
87 g_string_free(xpath, TRUE);
88 }
89 }
90
91 attr = pcmk__xe_create(change, PCMK_XE_CHANGE_ATTR);
92
93 crm_xml_add(attr, PCMK_XA_NAME, (const char *) pIter->name);
94 if (nodepriv->flags & pcmk__xf_deleted) {
95 crm_xml_add(attr, PCMK_XA_OPERATION, "unset");
96
97 } else {
98 crm_xml_add(attr, PCMK_XA_OPERATION, "set");
99
100 value = pcmk__xml_attr_value(pIter);
101 crm_xml_add(attr, PCMK_XA_VALUE, value);
102 }
103 }
104
105 if (change) {
106 xmlNode *result = NULL;
107
108 change = pcmk__xe_create(change->parent, PCMK_XE_CHANGE_RESULT);
109 result = pcmk__xe_create(change, (const char *)xml->name);
110
111 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
112 pIter = pIter->next) {
113 nodepriv = pIter->_private;
114 if (!pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
115 value = crm_element_value(xml, (const char *) pIter->name);
116 crm_xml_add(result, (const char *)pIter->name, value);
117 }
118 }
119 }
120
121
122 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
123 cIter = pcmk__xml_next(cIter)) {
124 add_xml_changes_to_patchset(cIter, patchset);
125 }
126
127 nodepriv = xml->_private;
128 if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
129 GString *xpath = pcmk__element_xpath(xml);
130
131 crm_trace("%s.%s moved to position %d",
132 xml->name, pcmk__xe_id(xml),
133 pcmk__xml_position(xml, pcmk__xf_skip));
134
135 if (xpath != NULL) {
136 change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
137
138 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MOVE);
139 crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
140 crm_xml_add_int(change, PCMK_XE_POSITION,
141 pcmk__xml_position(xml, pcmk__xf_deleted));
142 g_string_free(xpath, TRUE);
143 }
144 }
145 }
146
147 static bool
148 is_config_change(xmlNode *xml)
149 {
150 GList *gIter = NULL;
151 xml_node_private_t *nodepriv = NULL;
152 xml_doc_private_t *docpriv;
153 xmlNode *config = pcmk__xe_first_child(xml, PCMK_XE_CONFIGURATION, NULL,
154 NULL);
155
156 if (config) {
157 nodepriv = config->_private;
158 }
159 if ((nodepriv != NULL) && pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
160 return TRUE;
161 }
162
163 if ((xml->doc != NULL) && (xml->doc->_private != NULL)) {
164 docpriv = xml->doc->_private;
165 for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
166 pcmk__deleted_xml_t *deleted_obj = gIter->data;
167
168 if (strstr(deleted_obj->path,
169 "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION) != NULL) {
170 return TRUE;
171 }
172 }
173 }
174 return FALSE;
175 }
176
177
178 static void
179 xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
180 gboolean changed)
181 {
182 int lpc = 0;
183 xmlNode *cib = NULL;
184 xmlNode *diff_child = NULL;
185
186 const char *tag = NULL;
187
188 const char *vfields[] = {
189 PCMK_XA_ADMIN_EPOCH,
190 PCMK_XA_EPOCH,
191 PCMK_XA_NUM_UPDATES,
192 };
193
194 if (local_diff == NULL) {
195 crm_trace("Nothing to do");
196 return;
197 }
198
199 tag = PCMK__XE_DIFF_REMOVED;
200 diff_child = pcmk__xe_first_child(local_diff, tag, NULL, NULL);
201 if (diff_child == NULL) {
202 diff_child = pcmk__xe_create(local_diff, tag);
203 }
204
205 tag = PCMK_XE_CIB;
206 cib = pcmk__xe_first_child(diff_child, tag, NULL, NULL);
207 if (cib == NULL) {
208 cib = pcmk__xe_create(diff_child, tag);
209 }
210
211 for (lpc = 0; (last != NULL) && (lpc < PCMK__NELEM(vfields)); lpc++) {
212 const char *value = crm_element_value(last, vfields[lpc]);
213
214 crm_xml_add(diff_child, vfields[lpc], value);
215 if (changed || lpc == 2) {
216 crm_xml_add(cib, vfields[lpc], value);
217 }
218 }
219
220 tag = PCMK__XE_DIFF_ADDED;
221 diff_child = pcmk__xe_first_child(local_diff, tag, NULL, NULL);
222 if (diff_child == NULL) {
223 diff_child = pcmk__xe_create(local_diff, tag);
224 }
225
226 tag = PCMK_XE_CIB;
227 cib = pcmk__xe_first_child(diff_child, tag, NULL, NULL);
228 if (cib == NULL) {
229 cib = pcmk__xe_create(diff_child, tag);
230 }
231
232 for (lpc = 0; next && lpc < PCMK__NELEM(vfields); lpc++) {
233 const char *value = crm_element_value(next, vfields[lpc]);
234
235 crm_xml_add(diff_child, vfields[lpc], value);
236 }
237
238 for (xmlAttrPtr a = pcmk__xe_first_attr(next); a != NULL; a = a->next) {
239
240 const char *p_value = pcmk__xml_attr_value(a);
241
242 pcmk__xe_set_attr_force(cib, (const char *) a->name, p_value);
243 }
244
245 crm_log_xml_explicit(local_diff, "Repaired-diff");
246 }
247
248
249 static xmlNode *
250 xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config,
251 bool suppress)
252 {
253 xmlNode *patchset = pcmk__diff_v1_xml_object(source, target, suppress);
254
255 if (patchset) {
256 CRM_LOG_ASSERT(xml_document_dirty(target));
257 xml_repair_v1_diff(source, target, patchset, config);
258 crm_xml_add(patchset, PCMK_XA_FORMAT, "1");
259 }
260 return patchset;
261 }
262
263 static xmlNode *
264 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
265 {
266 int lpc = 0;
267 GList *gIter = NULL;
268 xml_doc_private_t *docpriv;
269
270 xmlNode *v = NULL;
271 xmlNode *version = NULL;
272 xmlNode *patchset = NULL;
273 const char *vfields[] = {
274 PCMK_XA_ADMIN_EPOCH,
275 PCMK_XA_EPOCH,
276 PCMK_XA_NUM_UPDATES,
277 };
278
279 pcmk__assert(target != NULL);
280 if (!xml_document_dirty(target)) {
281 return NULL;
282 }
283
284 pcmk__assert(target->doc != NULL);
285 docpriv = target->doc->_private;
286
287 patchset = pcmk__xe_create(NULL, PCMK_XE_DIFF);
288 crm_xml_add_int(patchset, PCMK_XA_FORMAT, 2);
289
290 version = pcmk__xe_create(patchset, PCMK_XE_VERSION);
291
292 v = pcmk__xe_create(version, PCMK_XE_SOURCE);
293 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
294 const char *value = crm_element_value(source, vfields[lpc]);
295
296 if (value == NULL) {
297 value = "1";
298 }
299 crm_xml_add(v, vfields[lpc], value);
300 }
301
302 v = pcmk__xe_create(version, PCMK_XE_TARGET);
303 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
304 const char *value = crm_element_value(target, vfields[lpc]);
305
306 if (value == NULL) {
307 value = "1";
308 }
309 crm_xml_add(v, vfields[lpc], value);
310 }
311
312 for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
313 pcmk__deleted_xml_t *deleted_obj = gIter->data;
314 xmlNode *change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
315
316 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_DELETE);
317 crm_xml_add(change, PCMK_XA_PATH, deleted_obj->path);
318 if (deleted_obj->position >= 0) {
319 crm_xml_add_int(change, PCMK_XE_POSITION, deleted_obj->position);
320 }
321 }
322
323 add_xml_changes_to_patchset(target, patchset);
324 return patchset;
325 }
326
327 xmlNode *
328 xml_create_patchset(int format, xmlNode *source, xmlNode *target,
329 bool *config_changed, bool manage_version)
330 {
331 int counter = 0;
332 bool config = FALSE;
333 xmlNode *patch = NULL;
334 const char *version = crm_element_value(source, PCMK_XA_CRM_FEATURE_SET);
335
336 xml_acl_disable(target);
337 if (!xml_document_dirty(target)) {
338 crm_trace("No change %d", format);
339 return NULL;
340 }
341
342 config = is_config_change(target);
343 if (config_changed) {
344 *config_changed = config;
345 }
346
347 if (manage_version && config) {
348 crm_trace("Config changed %d", format);
349 crm_xml_add(target, PCMK_XA_NUM_UPDATES, "0");
350
351 crm_element_value_int(target, PCMK_XA_EPOCH, &counter);
352 crm_xml_add_int(target, PCMK_XA_EPOCH, counter+1);
353
354 } else if (manage_version) {
355 crm_element_value_int(target, PCMK_XA_NUM_UPDATES, &counter);
356 crm_trace("Status changed %d - %d %s", format, counter,
357 crm_element_value(source, PCMK_XA_NUM_UPDATES));
358 crm_xml_add_int(target, PCMK_XA_NUM_UPDATES, (counter + 1));
359 }
360
361 if (format == 0) {
362 if (compare_version("3.0.8", version) < 0) {
363 format = 2;
364 } else {
365 format = 1;
366 }
367 crm_trace("Using patch format %d for version: %s", format, version);
368 }
369
370 switch (format) {
371 case 1:
372
373 patch = xml_create_patchset_v1(source, target, config, FALSE);
374 break;
375 case 2:
376 patch = xml_create_patchset_v2(source, target);
377 break;
378 default:
379 crm_err("Unknown patch format: %d", format);
380 return NULL;
381 }
382 return patch;
383 }
384
385 void
386 patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target,
387 bool with_digest)
388 {
389 int format = 1;
390 const char *version = NULL;
391 char *digest = NULL;
392
393 if ((patch == NULL) || (source == NULL) || (target == NULL)) {
394 return;
395 }
396
397
398
399
400 CRM_LOG_ASSERT(!xml_document_dirty(target));
401
402 crm_element_value_int(patch, PCMK_XA_FORMAT, &format);
403 if ((format > 1) && !with_digest) {
404 return;
405 }
406
407 version = crm_element_value(source, PCMK_XA_CRM_FEATURE_SET);
408 digest = calculate_xml_versioned_digest(target, FALSE, TRUE, version);
409
410 crm_xml_add(patch, PCMK__XA_DIGEST, digest);
411 free(digest);
412
413 return;
414 }
415
416
417 static xmlNode *
418 subtract_v1_xml_comment(xmlNode *parent, xmlNode *left, xmlNode *right,
419 gboolean *changed)
420 {
421 CRM_CHECK(left != NULL, return NULL);
422 CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL);
423
424 if ((right == NULL) || !pcmk__str_eq((const char *)left->content,
425 (const char *)right->content,
426 pcmk__str_casei)) {
427 xmlNode *deleted = NULL;
428
429 deleted = pcmk__xml_copy(parent, left);
430 *changed = TRUE;
431
432 return deleted;
433 }
434
435 return NULL;
436 }
437
438
439 static xmlNode *
440 subtract_v1_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
441 bool full, gboolean *changed, const char *marker)
442 {
443 gboolean dummy = FALSE;
444 xmlNode *diff = NULL;
445 xmlNode *right_child = NULL;
446 xmlNode *left_child = NULL;
447 xmlAttrPtr xIter = NULL;
448
449 const char *id = NULL;
450 const char *name = NULL;
451 const char *value = NULL;
452 const char *right_val = NULL;
453
454 if (changed == NULL) {
455 changed = &dummy;
456 }
457
458 if (left == NULL) {
459 return NULL;
460 }
461
462 if (left->type == XML_COMMENT_NODE) {
463 return subtract_v1_xml_comment(parent, left, right, changed);
464 }
465
466 id = pcmk__xe_id(left);
467 name = (const char *) left->name;
468 if (right == NULL) {
469 xmlNode *deleted = NULL;
470
471 crm_trace("Processing <%s " PCMK_XA_ID "=%s> (complete copy)",
472 name, id);
473 deleted = pcmk__xml_copy(parent, left);
474 crm_xml_add(deleted, PCMK__XA_CRM_DIFF_MARKER, marker);
475
476 *changed = TRUE;
477 return deleted;
478 }
479
480 CRM_CHECK(name != NULL, return NULL);
481 CRM_CHECK(pcmk__xe_is(left, (const char *) right->name), return NULL);
482
483
484 value = crm_element_value(right, PCMK__XA_CRM_DIFF_MARKER);
485 if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
486 crm_trace("We are the root of the deletion: %s.id=%s", name, id);
487 *changed = TRUE;
488 return NULL;
489 }
490
491
492 diff = pcmk__xe_create(parent, name);
493
494
495 for (left_child = pcmk__xml_first_child(left); left_child != NULL;
496 left_child = pcmk__xml_next(left_child)) {
497 gboolean child_changed = FALSE;
498
499 right_child = pcmk__xml_match(right, left_child, false);
500 subtract_v1_xml_object(diff, left_child, right_child, full,
501 &child_changed, marker);
502 if (child_changed) {
503 *changed = TRUE;
504 }
505 }
506
507 if (!*changed) {
508
509
510 } else if (full) {
511 xmlAttrPtr pIter = NULL;
512
513 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
514 pIter = pIter->next) {
515 const char *p_name = (const char *)pIter->name;
516 const char *p_value = pcmk__xml_attr_value(pIter);
517
518 pcmk__xe_set_attr_force(diff, p_name, p_value);
519 }
520
521
522 goto done;
523 }
524
525
526 for (xIter = pcmk__xe_first_attr(left); xIter != NULL;
527 xIter = xIter->next) {
528 const char *prop_name = (const char *) xIter->name;
529 xmlAttrPtr right_attr = NULL;
530 xml_node_private_t *nodepriv = NULL;
531
532 if (strcmp(prop_name, PCMK_XA_ID) == 0) {
533
534 pcmk__xe_set_attr_force(diff, PCMK_XA_ID, id);
535 continue;
536 }
537
538 if (pcmk__xa_filterable(prop_name)) {
539 continue;
540 }
541
542 right_attr = xmlHasProp(right, (pcmkXmlStr) prop_name);
543 if (right_attr) {
544 nodepriv = right_attr->_private;
545 }
546
547 right_val = crm_element_value(right, prop_name);
548 if ((right_val == NULL) || (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted))) {
549
550 *changed = TRUE;
551 if (full) {
552 xmlAttrPtr pIter = NULL;
553
554 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
555 pIter = pIter->next) {
556 const char *p_name = (const char *) pIter->name;
557 const char *p_value = pcmk__xml_attr_value(pIter);
558
559 pcmk__xe_set_attr_force(diff, p_name, p_value);
560 }
561 break;
562
563 } else {
564 const char *left_value = pcmk__xml_attr_value(xIter);
565
566 pcmk__xe_set_attr_force(diff, prop_name, value);
567 crm_xml_add(diff, prop_name, left_value);
568 }
569
570 } else {
571
572 const char *left_value = pcmk__xml_attr_value(xIter);
573
574 if (strcmp(left_value, right_val) == 0) {
575
576
577 } else {
578 *changed = TRUE;
579 if (full) {
580 xmlAttrPtr pIter = NULL;
581
582 crm_trace("Changes detected to %s in "
583 "<%s " PCMK_XA_ID "=%s>", prop_name, name, id);
584 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
585 pIter = pIter->next) {
586 const char *p_name = (const char *) pIter->name;
587 const char *p_value = pcmk__xml_attr_value(pIter);
588
589 pcmk__xe_set_attr_force(diff, p_name, p_value);
590 }
591 break;
592
593 } else {
594 crm_trace("Changes detected to %s (%s -> %s) in "
595 "<%s " PCMK_XA_ID "=%s>",
596 prop_name, left_value, right_val, name, id);
597 crm_xml_add(diff, prop_name, left_value);
598 }
599 }
600 }
601 }
602
603 if (!*changed) {
604 free_xml(diff);
605 return NULL;
606
607 } else if (!full && (id != NULL)) {
608 crm_xml_add(diff, PCMK_XA_ID, id);
609 }
610 done:
611 return diff;
612 }
613
614
615
616
617
618 static bool
619 not_id(xmlAttrPtr attr, void *user_data)
620 {
621 return strcmp((const char *) attr->name, PCMK_XA_ID) != 0;
622 }
623
624
625
626
627
628 static void
629 process_v1_removals(xmlNode *target, xmlNode *patch)
630 {
631 xmlNode *patch_child = NULL;
632 xmlNode *cIter = NULL;
633
634 char *id = NULL;
635 const char *value = NULL;
636
637 if ((target == NULL) || (patch == NULL)) {
638 return;
639 }
640
641 if (target->type == XML_COMMENT_NODE) {
642 gboolean dummy;
643
644 subtract_v1_xml_comment(target->parent, target, patch, &dummy);
645 }
646
647 CRM_CHECK(pcmk__xe_is(target, (const char *) patch->name), return);
648 CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch),
649 pcmk__str_none),
650 return);
651
652
653 id = crm_element_value_copy(target, PCMK_XA_ID);
654 value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER);
655 if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
656 crm_trace("We are the root of the deletion: %s.id=%s",
657 target->name, id);
658 free_xml(target);
659 free(id);
660 return;
661 }
662
663
664 pcmk__xe_remove_matching_attrs(patch, not_id, NULL);
665
666
667 cIter = pcmk__xml_first_child(target);
668 while (cIter) {
669 xmlNode *target_child = cIter;
670
671 cIter = pcmk__xml_next(cIter);
672 patch_child = pcmk__xml_match(patch, target_child, false);
673 process_v1_removals(target_child, patch_child);
674 }
675 free(id);
676 }
677
678
679
680
681
682 static void
683 process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch)
684 {
685 xmlNode *patch_child = NULL;
686 xmlNode *target_child = NULL;
687 xmlAttrPtr xIter = NULL;
688
689 const char *id = NULL;
690 const char *name = NULL;
691 const char *value = NULL;
692
693 if (patch == NULL) {
694 return;
695 } else if ((parent == NULL) && (target == NULL)) {
696 return;
697 }
698
699
700 name = (const char *) patch->name;
701 value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER);
702 if ((target == NULL) && (value != NULL)
703 && (strcmp(value, "added:top") == 0)) {
704 id = pcmk__xe_id(patch);
705 crm_trace("We are the root of the addition: %s.id=%s", name, id);
706 pcmk__xml_copy(parent, patch);
707 return;
708
709 } else if (target == NULL) {
710 id = pcmk__xe_id(patch);
711 crm_err("Could not locate: %s.id=%s", name, id);
712 return;
713 }
714
715 if (target->type == XML_COMMENT_NODE) {
716 pcmk__xc_update(parent, target, patch);
717 }
718
719 CRM_CHECK(pcmk__xe_is(target, name), return);
720 CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch),
721 pcmk__str_none),
722 return);
723
724 for (xIter = pcmk__xe_first_attr(patch); xIter != NULL;
725 xIter = xIter->next) {
726 const char *p_name = (const char *) xIter->name;
727 const char *p_value = pcmk__xml_attr_value(xIter);
728
729 pcmk__xe_remove_attr(target, p_name);
730 crm_xml_add(target, p_name, p_value);
731 }
732
733
734 for (patch_child = pcmk__xml_first_child(patch); patch_child != NULL;
735 patch_child = pcmk__xml_next(patch_child)) {
736
737 target_child = pcmk__xml_match(target, patch_child, false);
738 process_v1_additions(target, target_child, patch_child);
739 }
740 }
741
742
743
744
745
746
747
748
749
750
751
752
753 static bool
754 find_patch_xml_node(const xmlNode *patchset, int format, bool added,
755 xmlNode **patch_node)
756 {
757 xmlNode *cib_node;
758 const char *label;
759
760 switch (format) {
761 case 1:
762
763 label = added? PCMK__XE_DIFF_ADDED : PCMK__XE_DIFF_REMOVED;
764 *patch_node = pcmk__xe_first_child(patchset, label, NULL, NULL);
765 cib_node = pcmk__xe_first_child(*patch_node, PCMK_XE_CIB, NULL,
766 NULL);
767 if (cib_node != NULL) {
768 *patch_node = cib_node;
769 }
770 break;
771 case 2:
772 label = added? PCMK_XE_TARGET : PCMK_XE_SOURCE;
773 *patch_node = pcmk__xe_first_child(patchset, PCMK_XE_VERSION, NULL,
774 NULL);
775 *patch_node = pcmk__xe_first_child(*patch_node, label, NULL, NULL);
776 break;
777 default:
778 crm_warn("Unknown patch format: %d", format);
779 *patch_node = NULL;
780 return FALSE;
781 }
782 return TRUE;
783 }
784
785
786 bool
787 xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
788 {
789 int lpc = 0;
790 int format = 1;
791 xmlNode *tmp = NULL;
792
793 const char *vfields[] = {
794 PCMK_XA_ADMIN_EPOCH,
795 PCMK_XA_EPOCH,
796 PCMK_XA_NUM_UPDATES,
797 };
798
799
800 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
801
802
803 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
804 return -EINVAL;
805 }
806 if (tmp != NULL) {
807 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
808 crm_element_value_int(tmp, vfields[lpc], &(del[lpc]));
809 crm_trace("Got %d for del[%s]", del[lpc], vfields[lpc]);
810 }
811 }
812
813
814 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
815 return -EINVAL;
816 }
817 if (tmp != NULL) {
818 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
819 crm_element_value_int(tmp, vfields[lpc], &(add[lpc]));
820 crm_trace("Got %d for add[%s]", add[lpc], vfields[lpc]);
821 }
822 }
823 return pcmk_ok;
824 }
825
826
827
828
829
830
831
832
833
834
835 static int
836 xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset)
837 {
838 int lpc = 0;
839 bool changed = FALSE;
840
841 int this[] = { 0, 0, 0 };
842 int add[] = { 0, 0, 0 };
843 int del[] = { 0, 0, 0 };
844
845 const char *vfields[] = {
846 PCMK_XA_ADMIN_EPOCH,
847 PCMK_XA_EPOCH,
848 PCMK_XA_NUM_UPDATES,
849 };
850
851 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
852 crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
853 crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
854 if (this[lpc] < 0) {
855 this[lpc] = 0;
856 }
857 }
858
859
860 add[0] = this[0];
861 add[1] = this[1];
862 add[2] = this[2] + 1;
863 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
864 del[lpc] = this[lpc];
865 }
866
867 xml_patch_versions(patchset, add, del);
868
869 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
870 if (this[lpc] < del[lpc]) {
871 crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)",
872 vfields[lpc], this[0], this[1], this[2],
873 del[0], del[1], del[2], add[0], add[1], add[2]);
874 return pcmk_rc_diff_resync;
875
876 } else if (this[lpc] > del[lpc]) {
877 crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p",
878 vfields[lpc], this[0], this[1], this[2],
879 del[0], del[1], del[2], add[0], add[1], add[2], patchset);
880 crm_log_xml_info(patchset, "OldPatch");
881 return pcmk_rc_old_data;
882 }
883 }
884
885 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
886 if (add[lpc] > del[lpc]) {
887 changed = TRUE;
888 }
889 }
890
891 if (!changed) {
892 crm_notice("Versions did not change in patch %d.%d.%d",
893 add[0], add[1], add[2]);
894 return pcmk_rc_old_data;
895 }
896
897 crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
898 add[0], add[1], add[2], this[0], this[1], this[2]);
899 return pcmk_rc_ok;
900 }
901
902
903 static void
904 purge_v1_diff_markers(xmlNode *node)
905 {
906 xmlNode *child = NULL;
907
908 CRM_CHECK(node != NULL, return);
909
910 pcmk__xe_remove_attr(node, PCMK__XA_CRM_DIFF_MARKER);
911 for (child = pcmk__xml_first_child(node); child != NULL;
912 child = pcmk__xml_next(child)) {
913 purge_v1_diff_markers(child);
914 }
915 }
916
917
918
919
920
921
922
923
924
925
926
927 static int
928 apply_v1_patchset(xmlNode *xml, const xmlNode *patchset)
929 {
930 int rc = pcmk_rc_ok;
931 int root_nodes_seen = 0;
932
933 xmlNode *child_diff = NULL;
934 xmlNode *added = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_ADDED, NULL,
935 NULL);
936 xmlNode *removed = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_REMOVED,
937 NULL, NULL);
938 xmlNode *old = pcmk__xml_copy(NULL, xml);
939
940 crm_trace("Subtraction Phase");
941 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
942 child_diff = pcmk__xml_next(child_diff)) {
943 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
944 if (root_nodes_seen == 0) {
945 process_v1_removals(xml, child_diff);
946 }
947 root_nodes_seen++;
948 }
949
950 if (root_nodes_seen > 1) {
951 crm_err("(-) Diffs cannot contain more than one change set... saw %d",
952 root_nodes_seen);
953 rc = ENOTUNIQ;
954 }
955
956 root_nodes_seen = 0;
957 crm_trace("Addition Phase");
958 if (rc == pcmk_rc_ok) {
959 xmlNode *child_diff = NULL;
960
961 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
962 child_diff = pcmk__xml_next(child_diff)) {
963 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
964 if (root_nodes_seen == 0) {
965 process_v1_additions(NULL, xml, child_diff);
966 }
967 root_nodes_seen++;
968 }
969 }
970
971 if (root_nodes_seen > 1) {
972 crm_err("(+) Diffs cannot contain more than one change set... saw %d",
973 root_nodes_seen);
974 rc = ENOTUNIQ;
975 }
976
977 purge_v1_diff_markers(xml);
978
979 free_xml(old);
980 return rc;
981 }
982
983
984 static xmlNode *
985 first_matching_xml_child(const xmlNode *parent, const char *name,
986 const char *id, int position)
987 {
988 xmlNode *cIter = NULL;
989
990 for (cIter = pcmk__xml_first_child(parent); cIter != NULL;
991 cIter = pcmk__xml_next(cIter)) {
992 if (strcmp((const char *) cIter->name, name) != 0) {
993 continue;
994 } else if (id) {
995 const char *cid = pcmk__xe_id(cIter);
996
997 if ((cid == NULL) || (strcmp(cid, id) != 0)) {
998 continue;
999 }
1000 }
1001
1002
1003 if ((cIter->type == XML_COMMENT_NODE)
1004 && (position >= 0)
1005 && (pcmk__xml_position(cIter, pcmk__xf_skip) != position)) {
1006 continue;
1007 }
1008
1009 return cIter;
1010 }
1011 return NULL;
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027 static xmlNode *
1028 search_v2_xpath(const xmlNode *top, const char *key, int target_position)
1029 {
1030 xmlNode *target = (xmlNode *) top->doc;
1031 const char *current = key;
1032 char *section;
1033 char *remainder;
1034 char *id;
1035 char *tag;
1036 char *path = NULL;
1037 int rc;
1038 size_t key_len;
1039
1040 CRM_CHECK(key != NULL, return NULL);
1041 key_len = strlen(key);
1042
1043
1044
1045
1046
1047 remainder = pcmk__assert_alloc(key_len, sizeof(char));
1048 section = pcmk__assert_alloc(key_len, sizeof(char));
1049 id = pcmk__assert_alloc(key_len, sizeof(char));
1050 tag = pcmk__assert_alloc(key_len, sizeof(char));
1051
1052 do {
1053
1054 rc = sscanf(current, "/%[^/]%s", section, remainder);
1055 if (rc > 0) {
1056
1057 int f = sscanf(section, "%[^[][@" PCMK_XA_ID "='%[^']", tag, id);
1058 int current_position = -1;
1059
1060
1061
1062
1063 if ((rc == 1) && (target_position >= 0)) {
1064 current_position = target_position;
1065 }
1066
1067 switch (f) {
1068 case 1:
1069
1070 target = first_matching_xml_child(target, tag, NULL,
1071 current_position);
1072 break;
1073 case 2:
1074 target = first_matching_xml_child(target, tag, id,
1075 current_position);
1076 break;
1077 default:
1078
1079 target = NULL;
1080 break;
1081 }
1082 current = remainder;
1083 }
1084
1085
1086 } while ((rc == 2) && target);
1087
1088 if (target) {
1089 crm_trace("Found %s for %s",
1090 (path = (char *) xmlGetNodePath(target)), key);
1091 free(path);
1092 } else {
1093 crm_debug("No match for %s", key);
1094 }
1095
1096 free(remainder);
1097 free(section);
1098 free(tag);
1099 free(id);
1100 return target;
1101 }
1102
1103 typedef struct xml_change_obj_s {
1104 const xmlNode *change;
1105 xmlNode *match;
1106 } xml_change_obj_t;
1107
1108 static gint
1109 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
1110 {
1111 const xml_change_obj_t *change_obj_a = a;
1112 const xml_change_obj_t *change_obj_b = b;
1113 int position_a = -1;
1114 int position_b = -1;
1115
1116 crm_element_value_int(change_obj_a->change, PCMK_XE_POSITION, &position_a);
1117 crm_element_value_int(change_obj_b->change, PCMK_XE_POSITION, &position_b);
1118
1119 if (position_a < position_b) {
1120 return -1;
1121
1122 } else if (position_a > position_b) {
1123 return 1;
1124 }
1125
1126 return 0;
1127 }
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138 static int
1139 apply_v2_patchset(xmlNode *xml, const xmlNode *patchset)
1140 {
1141 int rc = pcmk_rc_ok;
1142 const xmlNode *change = NULL;
1143 GList *change_objs = NULL;
1144 GList *gIter = NULL;
1145
1146 for (change = pcmk__xml_first_child(patchset); change != NULL;
1147 change = pcmk__xml_next(change)) {
1148 xmlNode *match = NULL;
1149 const char *op = crm_element_value(change, PCMK_XA_OPERATION);
1150 const char *xpath = crm_element_value(change, PCMK_XA_PATH);
1151 int position = -1;
1152
1153 if (op == NULL) {
1154 continue;
1155 }
1156
1157 crm_trace("Processing %s %s", change->name, op);
1158
1159
1160
1161
1162 if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
1163 crm_element_value_int(change, PCMK_XE_POSITION, &position);
1164 }
1165 match = search_v2_xpath(xml, xpath, position);
1166 crm_trace("Performing %s on %s with %p", op, xpath, match);
1167
1168 if ((match == NULL) && (strcmp(op, PCMK_VALUE_DELETE) == 0)) {
1169 crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
1170 continue;
1171
1172 } else if (match == NULL) {
1173 crm_err("No %s match for %s in %p", op, xpath, xml->doc);
1174 rc = pcmk_rc_diff_failed;
1175 continue;
1176
1177 } else if (pcmk__str_any_of(op,
1178 PCMK_VALUE_CREATE, PCMK_VALUE_MOVE, NULL)) {
1179
1180 xml_change_obj_t *change_obj =
1181 pcmk__assert_alloc(1, sizeof(xml_change_obj_t));
1182
1183 change_obj->change = change;
1184 change_obj->match = match;
1185
1186 change_objs = g_list_append(change_objs, change_obj);
1187
1188 if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
1189
1190 if ((match->parent != NULL) && (match->parent->last != NULL)) {
1191 xmlAddNextSibling(match->parent->last, match);
1192 }
1193 }
1194
1195 } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
1196 free_xml(match);
1197
1198 } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
1199 const xmlNode *child = pcmk__xe_first_child(change,
1200 PCMK_XE_CHANGE_RESULT,
1201 NULL, NULL);
1202 const xmlNode *attrs = pcmk__xml_first_child(child);
1203
1204 if (attrs == NULL) {
1205 rc = ENOMSG;
1206 continue;
1207 }
1208 pcmk__xe_remove_matching_attrs(match, NULL, NULL);
1209
1210 for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
1211 pIter = pIter->next) {
1212 const char *name = (const char *) pIter->name;
1213 const char *value = pcmk__xml_attr_value(pIter);
1214
1215 crm_xml_add(match, name, value);
1216 }
1217
1218 } else {
1219 crm_err("Unknown operation: %s", op);
1220 rc = pcmk_rc_diff_failed;
1221 }
1222 }
1223
1224
1225 change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
1226
1227 for (gIter = change_objs; gIter; gIter = gIter->next) {
1228 xml_change_obj_t *change_obj = gIter->data;
1229 xmlNode *match = change_obj->match;
1230 const char *op = NULL;
1231 const char *xpath = NULL;
1232
1233 change = change_obj->change;
1234
1235 op = crm_element_value(change, PCMK_XA_OPERATION);
1236 xpath = crm_element_value(change, PCMK_XA_PATH);
1237
1238 crm_trace("Continue performing %s on %s with %p", op, xpath, match);
1239
1240 if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
1241 int position = 0;
1242 xmlNode *child = NULL;
1243 xmlNode *match_child = NULL;
1244
1245 match_child = match->children;
1246 crm_element_value_int(change, PCMK_XE_POSITION, &position);
1247
1248 while ((match_child != NULL)
1249 && (position != pcmk__xml_position(match_child, pcmk__xf_skip))) {
1250 match_child = match_child->next;
1251 }
1252
1253 child = pcmk__xml_copy(match, change->children);
1254
1255 if (match_child != NULL) {
1256 crm_trace("Adding %s at position %d", child->name, position);
1257 xmlAddPrevSibling(match_child, child);
1258
1259 } else {
1260 crm_trace("Adding %s at position %d (end)",
1261 child->name, position);
1262 }
1263
1264 } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
1265 int position = 0;
1266
1267 crm_element_value_int(change, PCMK_XE_POSITION, &position);
1268 if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
1269 xmlNode *match_child = NULL;
1270 int p = position;
1271
1272 if (p > pcmk__xml_position(match, pcmk__xf_skip)) {
1273 p++;
1274 }
1275
1276 pcmk__assert(match->parent != NULL);
1277 match_child = match->parent->children;
1278
1279 while ((match_child != NULL)
1280 && (p != pcmk__xml_position(match_child, pcmk__xf_skip))) {
1281 match_child = match_child->next;
1282 }
1283
1284 crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
1285 match->name, position,
1286 pcmk__xml_position(match, pcmk__xf_skip),
1287 match->prev, (match_child? "next":"last"),
1288 (match_child? match_child : match->parent->last));
1289
1290 if (match_child) {
1291 xmlAddPrevSibling(match_child, match);
1292
1293 } else {
1294 pcmk__assert(match->parent->last != NULL);
1295 xmlAddNextSibling(match->parent->last, match);
1296 }
1297
1298 } else {
1299 crm_trace("%s is already in position %d",
1300 match->name, position);
1301 }
1302
1303 if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
1304 crm_err("Moved %s.%s to position %d instead of %d (%p)",
1305 match->name, pcmk__xe_id(match),
1306 pcmk__xml_position(match, pcmk__xf_skip),
1307 position, match->prev);
1308 rc = pcmk_rc_diff_failed;
1309 }
1310 }
1311 }
1312
1313 g_list_free_full(change_objs, free);
1314 return rc;
1315 }
1316
1317 int
1318 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
1319 {
1320 int format = 1;
1321 int rc = pcmk_ok;
1322 xmlNode *old = NULL;
1323 const char *digest = NULL;
1324
1325 if (patchset == NULL) {
1326 return rc;
1327 }
1328
1329 pcmk__log_xml_patchset(LOG_TRACE, patchset);
1330
1331 if (check_version) {
1332 rc = pcmk_rc2legacy(xml_patch_version_check(xml, patchset));
1333 if (rc != pcmk_ok) {
1334 return rc;
1335 }
1336 }
1337
1338 digest = crm_element_value(patchset, PCMK__XA_DIGEST);
1339 if (digest != NULL) {
1340
1341
1342
1343 pcmk__if_tracing(old = pcmk__xml_copy(NULL, xml), {});
1344 }
1345
1346 if (rc == pcmk_ok) {
1347 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
1348 switch (format) {
1349 case 1:
1350
1351 rc = pcmk_rc2legacy(apply_v1_patchset(xml, patchset));
1352 break;
1353 case 2:
1354 rc = pcmk_rc2legacy(apply_v2_patchset(xml, patchset));
1355 break;
1356 default:
1357 crm_err("Unknown patch format: %d", format);
1358 rc = -EINVAL;
1359 }
1360 }
1361
1362 if ((rc == pcmk_ok) && (digest != NULL)) {
1363 char *new_digest = NULL;
1364 char *version = crm_element_value_copy(xml, PCMK_XA_CRM_FEATURE_SET);
1365
1366 new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
1367 if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
1368 crm_info("v%d digest mis-match: expected %s, calculated %s",
1369 format, digest, new_digest);
1370 rc = -pcmk_err_diff_failed;
1371 pcmk__if_tracing(
1372 {
1373 save_xml_to_file(old, "PatchDigest:input", NULL);
1374 save_xml_to_file(xml, "PatchDigest:result", NULL);
1375 save_xml_to_file(patchset, "PatchDigest:diff", NULL);
1376 },
1377 {}
1378 );
1379
1380 } else {
1381 crm_trace("v%d digest matched: expected %s, calculated %s",
1382 format, digest, new_digest);
1383 }
1384 free(new_digest);
1385 free(version);
1386 }
1387 free_xml(old);
1388 return rc;
1389 }
1390
1391
1392 static bool
1393 can_prune_leaf_v1(xmlNode *node)
1394 {
1395 xmlNode *cIter = NULL;
1396 bool can_prune = true;
1397
1398 CRM_CHECK(node != NULL, return false);
1399
1400
1401
1402
1403 if (pcmk__strcase_any_of((const char *) node->name,
1404 PCMK_XE_RESOURCE_REF, PCMK_XE_OBJ_REF,
1405 PCMK_XE_ROLE, PCMK__XE_ROLE_REF,
1406 NULL)) {
1407 return false;
1408 }
1409
1410 for (xmlAttrPtr a = pcmk__xe_first_attr(node); a != NULL; a = a->next) {
1411 const char *p_name = (const char *) a->name;
1412
1413 if (strcmp(p_name, PCMK_XA_ID) == 0) {
1414 continue;
1415 }
1416 can_prune = false;
1417 }
1418
1419 cIter = pcmk__xml_first_child(node);
1420 while (cIter) {
1421 xmlNode *child = cIter;
1422
1423 cIter = pcmk__xml_next(cIter);
1424 if (can_prune_leaf_v1(child)) {
1425 free_xml(child);
1426 } else {
1427 can_prune = false;
1428 }
1429 }
1430 return can_prune;
1431 }
1432
1433
1434 xmlNode *
1435 pcmk__diff_v1_xml_object(xmlNode *old, xmlNode *new, bool suppress)
1436 {
1437 xmlNode *tmp1 = NULL;
1438 xmlNode *diff = pcmk__xe_create(NULL, PCMK_XE_DIFF);
1439 xmlNode *removed = pcmk__xe_create(diff, PCMK__XE_DIFF_REMOVED);
1440 xmlNode *added = pcmk__xe_create(diff, PCMK__XE_DIFF_ADDED);
1441
1442 crm_xml_add(diff, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
1443
1444 tmp1 = subtract_v1_xml_object(removed, old, new, false, NULL,
1445 "removed:top");
1446 if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
1447 free_xml(tmp1);
1448 }
1449
1450 tmp1 = subtract_v1_xml_object(added, new, old, true, NULL, "added:top");
1451 if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
1452 free_xml(tmp1);
1453 }
1454
1455 if ((added->children == NULL) && (removed->children == NULL)) {
1456 free_xml(diff);
1457 diff = NULL;
1458 }
1459
1460 return diff;
1461 }
1462
1463
1464
1465
1466 #include <crm/common/xml_compat.h>
1467
1468 gboolean
1469 apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
1470 {
1471 gboolean result = TRUE;
1472 int root_nodes_seen = 0;
1473 const char *digest = crm_element_value(diff, PCMK__XA_DIGEST);
1474 const char *version = crm_element_value(diff, PCMK_XA_CRM_FEATURE_SET);
1475
1476 xmlNode *child_diff = NULL;
1477 xmlNode *added = pcmk__xe_first_child(diff, PCMK__XE_DIFF_ADDED, NULL,
1478 NULL);
1479 xmlNode *removed = pcmk__xe_first_child(diff, PCMK__XE_DIFF_REMOVED, NULL,
1480 NULL);
1481
1482 CRM_CHECK(new_xml != NULL, return FALSE);
1483
1484 crm_trace("Subtraction Phase");
1485 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
1486 child_diff = pcmk__xml_next(child_diff)) {
1487 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
1488 if (root_nodes_seen == 0) {
1489 *new_xml = subtract_v1_xml_object(NULL, old_xml, child_diff, false,
1490 NULL, NULL);
1491 }
1492 root_nodes_seen++;
1493 }
1494
1495 if (root_nodes_seen == 0) {
1496 *new_xml = pcmk__xml_copy(NULL, old_xml);
1497
1498 } else if (root_nodes_seen > 1) {
1499 crm_err("(-) Diffs cannot contain more than one change set... saw %d",
1500 root_nodes_seen);
1501 result = FALSE;
1502 }
1503
1504 root_nodes_seen = 0;
1505 crm_trace("Addition Phase");
1506 if (result) {
1507 xmlNode *child_diff = NULL;
1508
1509 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
1510 child_diff = pcmk__xml_next(child_diff)) {
1511 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
1512 if (root_nodes_seen == 0) {
1513 pcmk__xml_update(NULL, *new_xml, child_diff, pcmk__xaf_none,
1514 true);
1515 }
1516 root_nodes_seen++;
1517 }
1518 }
1519
1520 if (root_nodes_seen > 1) {
1521 crm_err("(+) Diffs cannot contain more than one change set... saw %d",
1522 root_nodes_seen);
1523 result = FALSE;
1524
1525 } else if (result && (digest != NULL)) {
1526 char *new_digest = NULL;
1527
1528 purge_v1_diff_markers(*new_xml);
1529 new_digest = calculate_xml_versioned_digest(*new_xml, FALSE, TRUE,
1530 version);
1531 if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
1532 crm_info("Digest mis-match: expected %s, calculated %s",
1533 digest, new_digest);
1534 result = FALSE;
1535
1536 pcmk__if_tracing(
1537 {
1538 save_xml_to_file(old_xml, "diff:original", NULL);
1539 save_xml_to_file(diff, "diff:input", NULL);
1540 save_xml_to_file(*new_xml, "diff:new", NULL);
1541 },
1542 {}
1543 );
1544
1545 } else {
1546 crm_trace("Digest matched: expected %s, calculated %s",
1547 digest, new_digest);
1548 }
1549 free(new_digest);
1550
1551 } else if (result) {
1552 purge_v1_diff_markers(*new_xml);
1553 }
1554
1555 return result;
1556 }
1557
1558 void
1559 purge_diff_markers(xmlNode *a_node)
1560 {
1561 purge_v1_diff_markers(a_node);
1562 }
1563
1564 xmlNode *
1565 diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
1566 {
1567 return pcmk__diff_v1_xml_object(old, new, suppress);
1568 }
1569
1570 xmlNode *
1571 subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
1572 gboolean full, gboolean *changed, const char *marker)
1573 {
1574 return subtract_v1_xml_object(parent, left, right, full, changed, marker);
1575 }
1576
1577 gboolean
1578 can_prune_leaf(xmlNode *xml_node)
1579 {
1580 return can_prune_leaf_v1(xml_node);
1581 }
1582
1583
1584