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 xmlSetProp(cib, a->name, (pcmkXmlStr) 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 CRM_ASSERT(target);
280 if (!xml_document_dirty(target)) {
281 return NULL;
282 }
283
284 CRM_ASSERT(target->doc);
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 xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) 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 xmlSetProp(diff, (pcmkXmlStr) PCMK_XA_ID, (pcmkXmlStr) 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 xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
560 }
561 break;
562
563 } else {
564 const char *left_value = pcmk__xml_attr_value(xIter);
565
566 xmlSetProp(diff, (pcmkXmlStr) prop_name, (pcmkXmlStr) 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 xmlSetProp(diff, (pcmkXmlStr) p_name,
590 (pcmkXmlStr) p_value);
591 }
592 break;
593
594 } else {
595 crm_trace("Changes detected to %s (%s -> %s) in "
596 "<%s " PCMK_XA_ID "=%s>",
597 prop_name, left_value, right_val, name, id);
598 crm_xml_add(diff, prop_name, left_value);
599 }
600 }
601 }
602 }
603
604 if (!*changed) {
605 free_xml(diff);
606 return NULL;
607
608 } else if (!full && (id != NULL)) {
609 crm_xml_add(diff, PCMK_XA_ID, id);
610 }
611 done:
612 return diff;
613 }
614
615
616
617
618
619 static bool
620 not_id(xmlAttrPtr attr, void *user_data)
621 {
622 return strcmp((const char *) attr->name, PCMK_XA_ID) != 0;
623 }
624
625
626
627
628
629 static void
630 process_v1_removals(xmlNode *target, xmlNode *patch)
631 {
632 xmlNode *patch_child = NULL;
633 xmlNode *cIter = NULL;
634
635 char *id = NULL;
636 const char *value = NULL;
637
638 if ((target == NULL) || (patch == NULL)) {
639 return;
640 }
641
642 if (target->type == XML_COMMENT_NODE) {
643 gboolean dummy;
644
645 subtract_v1_xml_comment(target->parent, target, patch, &dummy);
646 }
647
648 CRM_CHECK(pcmk__xe_is(target, (const char *) patch->name), return);
649 CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch),
650 pcmk__str_none),
651 return);
652
653
654 id = crm_element_value_copy(target, PCMK_XA_ID);
655 value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER);
656 if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
657 crm_trace("We are the root of the deletion: %s.id=%s",
658 target->name, id);
659 free_xml(target);
660 free(id);
661 return;
662 }
663
664
665 pcmk__xe_remove_matching_attrs(patch, not_id, NULL);
666
667
668 cIter = pcmk__xml_first_child(target);
669 while (cIter) {
670 xmlNode *target_child = cIter;
671
672 cIter = pcmk__xml_next(cIter);
673 patch_child = pcmk__xml_match(patch, target_child, false);
674 process_v1_removals(target_child, patch_child);
675 }
676 free(id);
677 }
678
679
680
681
682
683 static void
684 process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch)
685 {
686 xmlNode *patch_child = NULL;
687 xmlNode *target_child = NULL;
688 xmlAttrPtr xIter = NULL;
689
690 const char *id = NULL;
691 const char *name = NULL;
692 const char *value = NULL;
693
694 if (patch == NULL) {
695 return;
696 } else if ((parent == NULL) && (target == NULL)) {
697 return;
698 }
699
700
701 name = (const char *) patch->name;
702 value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER);
703 if ((target == NULL) && (value != NULL)
704 && (strcmp(value, "added:top") == 0)) {
705 id = pcmk__xe_id(patch);
706 crm_trace("We are the root of the addition: %s.id=%s", name, id);
707 pcmk__xml_copy(parent, patch);
708 return;
709
710 } else if (target == NULL) {
711 id = pcmk__xe_id(patch);
712 crm_err("Could not locate: %s.id=%s", name, id);
713 return;
714 }
715
716 if (target->type == XML_COMMENT_NODE) {
717 pcmk__xc_update(parent, target, patch);
718 }
719
720 CRM_CHECK(pcmk__xe_is(target, name), return);
721 CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch),
722 pcmk__str_none),
723 return);
724
725 for (xIter = pcmk__xe_first_attr(patch); xIter != NULL;
726 xIter = xIter->next) {
727 const char *p_name = (const char *) xIter->name;
728 const char *p_value = pcmk__xml_attr_value(xIter);
729
730 pcmk__xe_remove_attr(target, p_name);
731 crm_xml_add(target, p_name, p_value);
732 }
733
734
735 for (patch_child = pcmk__xml_first_child(patch); patch_child != NULL;
736 patch_child = pcmk__xml_next(patch_child)) {
737
738 target_child = pcmk__xml_match(target, patch_child, false);
739 process_v1_additions(target, target_child, patch_child);
740 }
741 }
742
743
744
745
746
747
748
749
750
751
752
753
754 static bool
755 find_patch_xml_node(const xmlNode *patchset, int format, bool added,
756 xmlNode **patch_node)
757 {
758 xmlNode *cib_node;
759 const char *label;
760
761 switch (format) {
762 case 1:
763
764 label = added? PCMK__XE_DIFF_ADDED : PCMK__XE_DIFF_REMOVED;
765 *patch_node = pcmk__xe_first_child(patchset, label, NULL, NULL);
766 cib_node = pcmk__xe_first_child(*patch_node, PCMK_XE_CIB, NULL,
767 NULL);
768 if (cib_node != NULL) {
769 *patch_node = cib_node;
770 }
771 break;
772 case 2:
773 label = added? PCMK_XE_TARGET : PCMK_XE_SOURCE;
774 *patch_node = pcmk__xe_first_child(patchset, PCMK_XE_VERSION, NULL,
775 NULL);
776 *patch_node = pcmk__xe_first_child(*patch_node, label, NULL, NULL);
777 break;
778 default:
779 crm_warn("Unknown patch format: %d", format);
780 *patch_node = NULL;
781 return FALSE;
782 }
783 return TRUE;
784 }
785
786
787 bool
788 xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
789 {
790 int lpc = 0;
791 int format = 1;
792 xmlNode *tmp = NULL;
793
794 const char *vfields[] = {
795 PCMK_XA_ADMIN_EPOCH,
796 PCMK_XA_EPOCH,
797 PCMK_XA_NUM_UPDATES,
798 };
799
800
801 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
802
803
804 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
805 return -EINVAL;
806 }
807 if (tmp != NULL) {
808 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
809 crm_element_value_int(tmp, vfields[lpc], &(del[lpc]));
810 crm_trace("Got %d for del[%s]", del[lpc], vfields[lpc]);
811 }
812 }
813
814
815 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
816 return -EINVAL;
817 }
818 if (tmp != NULL) {
819 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
820 crm_element_value_int(tmp, vfields[lpc], &(add[lpc]));
821 crm_trace("Got %d for add[%s]", add[lpc], vfields[lpc]);
822 }
823 }
824 return pcmk_ok;
825 }
826
827
828
829
830
831
832
833
834
835
836 static int
837 xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset)
838 {
839 int lpc = 0;
840 bool changed = FALSE;
841
842 int this[] = { 0, 0, 0 };
843 int add[] = { 0, 0, 0 };
844 int del[] = { 0, 0, 0 };
845
846 const char *vfields[] = {
847 PCMK_XA_ADMIN_EPOCH,
848 PCMK_XA_EPOCH,
849 PCMK_XA_NUM_UPDATES,
850 };
851
852 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
853 crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
854 crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
855 if (this[lpc] < 0) {
856 this[lpc] = 0;
857 }
858 }
859
860
861 add[0] = this[0];
862 add[1] = this[1];
863 add[2] = this[2] + 1;
864 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
865 del[lpc] = this[lpc];
866 }
867
868 xml_patch_versions(patchset, add, del);
869
870 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
871 if (this[lpc] < del[lpc]) {
872 crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)",
873 vfields[lpc], this[0], this[1], this[2],
874 del[0], del[1], del[2], add[0], add[1], add[2]);
875 return pcmk_rc_diff_resync;
876
877 } else if (this[lpc] > del[lpc]) {
878 crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p",
879 vfields[lpc], this[0], this[1], this[2],
880 del[0], del[1], del[2], add[0], add[1], add[2], patchset);
881 crm_log_xml_info(patchset, "OldPatch");
882 return pcmk_rc_old_data;
883 }
884 }
885
886 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
887 if (add[lpc] > del[lpc]) {
888 changed = TRUE;
889 }
890 }
891
892 if (!changed) {
893 crm_notice("Versions did not change in patch %d.%d.%d",
894 add[0], add[1], add[2]);
895 return pcmk_rc_old_data;
896 }
897
898 crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
899 add[0], add[1], add[2], this[0], this[1], this[2]);
900 return pcmk_rc_ok;
901 }
902
903
904 static void
905 purge_v1_diff_markers(xmlNode *node)
906 {
907 xmlNode *child = NULL;
908
909 CRM_CHECK(node != NULL, return);
910
911 pcmk__xe_remove_attr(node, PCMK__XA_CRM_DIFF_MARKER);
912 for (child = pcmk__xml_first_child(node); child != NULL;
913 child = pcmk__xml_next(child)) {
914 purge_v1_diff_markers(child);
915 }
916 }
917
918
919
920
921
922
923
924
925
926
927
928 static int
929 apply_v1_patchset(xmlNode *xml, const xmlNode *patchset)
930 {
931 int rc = pcmk_rc_ok;
932 int root_nodes_seen = 0;
933
934 xmlNode *child_diff = NULL;
935 xmlNode *added = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_ADDED, NULL,
936 NULL);
937 xmlNode *removed = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_REMOVED,
938 NULL, NULL);
939 xmlNode *old = pcmk__xml_copy(NULL, xml);
940
941 crm_trace("Subtraction Phase");
942 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
943 child_diff = pcmk__xml_next(child_diff)) {
944 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
945 if (root_nodes_seen == 0) {
946 process_v1_removals(xml, child_diff);
947 }
948 root_nodes_seen++;
949 }
950
951 if (root_nodes_seen > 1) {
952 crm_err("(-) Diffs cannot contain more than one change set... saw %d",
953 root_nodes_seen);
954 rc = ENOTUNIQ;
955 }
956
957 root_nodes_seen = 0;
958 crm_trace("Addition Phase");
959 if (rc == pcmk_rc_ok) {
960 xmlNode *child_diff = NULL;
961
962 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
963 child_diff = pcmk__xml_next(child_diff)) {
964 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
965 if (root_nodes_seen == 0) {
966 process_v1_additions(NULL, xml, child_diff);
967 }
968 root_nodes_seen++;
969 }
970 }
971
972 if (root_nodes_seen > 1) {
973 crm_err("(+) Diffs cannot contain more than one change set... saw %d",
974 root_nodes_seen);
975 rc = ENOTUNIQ;
976 }
977
978 purge_v1_diff_markers(xml);
979
980 free_xml(old);
981 return rc;
982 }
983
984
985 static xmlNode *
986 first_matching_xml_child(const xmlNode *parent, const char *name,
987 const char *id, int position)
988 {
989 xmlNode *cIter = NULL;
990
991 for (cIter = pcmk__xml_first_child(parent); cIter != NULL;
992 cIter = pcmk__xml_next(cIter)) {
993 if (strcmp((const char *) cIter->name, name) != 0) {
994 continue;
995 } else if (id) {
996 const char *cid = pcmk__xe_id(cIter);
997
998 if ((cid == NULL) || (strcmp(cid, id) != 0)) {
999 continue;
1000 }
1001 }
1002
1003
1004 if ((cIter->type == XML_COMMENT_NODE)
1005 && (position >= 0)
1006 && (pcmk__xml_position(cIter, pcmk__xf_skip) != position)) {
1007 continue;
1008 }
1009
1010 return cIter;
1011 }
1012 return NULL;
1013 }
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028 static xmlNode *
1029 search_v2_xpath(const xmlNode *top, const char *key, int target_position)
1030 {
1031 xmlNode *target = (xmlNode *) top->doc;
1032 const char *current = key;
1033 char *section;
1034 char *remainder;
1035 char *id;
1036 char *tag;
1037 char *path = NULL;
1038 int rc;
1039 size_t key_len;
1040
1041 CRM_CHECK(key != NULL, return NULL);
1042 key_len = strlen(key);
1043
1044
1045
1046
1047
1048 remainder = pcmk__assert_alloc(key_len, sizeof(char));
1049 section = pcmk__assert_alloc(key_len, sizeof(char));
1050 id = pcmk__assert_alloc(key_len, sizeof(char));
1051 tag = pcmk__assert_alloc(key_len, sizeof(char));
1052
1053 do {
1054
1055 rc = sscanf(current, "/%[^/]%s", section, remainder);
1056 if (rc > 0) {
1057
1058 int f = sscanf(section, "%[^[][@" PCMK_XA_ID "='%[^']", tag, id);
1059 int current_position = -1;
1060
1061
1062
1063
1064 if ((rc == 1) && (target_position >= 0)) {
1065 current_position = target_position;
1066 }
1067
1068 switch (f) {
1069 case 1:
1070
1071 target = first_matching_xml_child(target, tag, NULL,
1072 current_position);
1073 break;
1074 case 2:
1075 target = first_matching_xml_child(target, tag, id,
1076 current_position);
1077 break;
1078 default:
1079
1080 target = NULL;
1081 break;
1082 }
1083 current = remainder;
1084 }
1085
1086
1087 } while ((rc == 2) && target);
1088
1089 if (target) {
1090 crm_trace("Found %s for %s",
1091 (path = (char *) xmlGetNodePath(target)), key);
1092 free(path);
1093 } else {
1094 crm_debug("No match for %s", key);
1095 }
1096
1097 free(remainder);
1098 free(section);
1099 free(tag);
1100 free(id);
1101 return target;
1102 }
1103
1104 typedef struct xml_change_obj_s {
1105 const xmlNode *change;
1106 xmlNode *match;
1107 } xml_change_obj_t;
1108
1109 static gint
1110 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
1111 {
1112 const xml_change_obj_t *change_obj_a = a;
1113 const xml_change_obj_t *change_obj_b = b;
1114 int position_a = -1;
1115 int position_b = -1;
1116
1117 crm_element_value_int(change_obj_a->change, PCMK_XE_POSITION, &position_a);
1118 crm_element_value_int(change_obj_b->change, PCMK_XE_POSITION, &position_b);
1119
1120 if (position_a < position_b) {
1121 return -1;
1122
1123 } else if (position_a > position_b) {
1124 return 1;
1125 }
1126
1127 return 0;
1128 }
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139 static int
1140 apply_v2_patchset(xmlNode *xml, const xmlNode *patchset)
1141 {
1142 int rc = pcmk_rc_ok;
1143 const xmlNode *change = NULL;
1144 GList *change_objs = NULL;
1145 GList *gIter = NULL;
1146
1147 for (change = pcmk__xml_first_child(patchset); change != NULL;
1148 change = pcmk__xml_next(change)) {
1149 xmlNode *match = NULL;
1150 const char *op = crm_element_value(change, PCMK_XA_OPERATION);
1151 const char *xpath = crm_element_value(change, PCMK_XA_PATH);
1152 int position = -1;
1153
1154 if (op == NULL) {
1155 continue;
1156 }
1157
1158 crm_trace("Processing %s %s", change->name, op);
1159
1160
1161
1162
1163 if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
1164 crm_element_value_int(change, PCMK_XE_POSITION, &position);
1165 }
1166 match = search_v2_xpath(xml, xpath, position);
1167 crm_trace("Performing %s on %s with %p", op, xpath, match);
1168
1169 if ((match == NULL) && (strcmp(op, PCMK_VALUE_DELETE) == 0)) {
1170 crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
1171 continue;
1172
1173 } else if (match == NULL) {
1174 crm_err("No %s match for %s in %p", op, xpath, xml->doc);
1175 rc = pcmk_rc_diff_failed;
1176 continue;
1177
1178 } else if (pcmk__str_any_of(op,
1179 PCMK_VALUE_CREATE, PCMK_VALUE_MOVE, NULL)) {
1180
1181 xml_change_obj_t *change_obj =
1182 pcmk__assert_alloc(1, sizeof(xml_change_obj_t));
1183
1184 change_obj->change = change;
1185 change_obj->match = match;
1186
1187 change_objs = g_list_append(change_objs, change_obj);
1188
1189 if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
1190
1191 if ((match->parent != NULL) && (match->parent->last != NULL)) {
1192 xmlAddNextSibling(match->parent->last, match);
1193 }
1194 }
1195
1196 } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
1197 free_xml(match);
1198
1199 } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
1200 const xmlNode *child = pcmk__xe_first_child(change,
1201 PCMK_XE_CHANGE_RESULT,
1202 NULL, NULL);
1203 const xmlNode *attrs = pcmk__xml_first_child(child);
1204
1205 if (attrs == NULL) {
1206 rc = ENOMSG;
1207 continue;
1208 }
1209 pcmk__xe_remove_matching_attrs(match, NULL, NULL);
1210
1211 for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
1212 pIter = pIter->next) {
1213 const char *name = (const char *) pIter->name;
1214 const char *value = pcmk__xml_attr_value(pIter);
1215
1216 crm_xml_add(match, name, value);
1217 }
1218
1219 } else {
1220 crm_err("Unknown operation: %s", op);
1221 rc = pcmk_rc_diff_failed;
1222 }
1223 }
1224
1225
1226 change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
1227
1228 for (gIter = change_objs; gIter; gIter = gIter->next) {
1229 xml_change_obj_t *change_obj = gIter->data;
1230 xmlNode *match = change_obj->match;
1231 const char *op = NULL;
1232 const char *xpath = NULL;
1233
1234 change = change_obj->change;
1235
1236 op = crm_element_value(change, PCMK_XA_OPERATION);
1237 xpath = crm_element_value(change, PCMK_XA_PATH);
1238
1239 crm_trace("Continue performing %s on %s with %p", op, xpath, match);
1240
1241 if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
1242 int position = 0;
1243 xmlNode *child = NULL;
1244 xmlNode *match_child = NULL;
1245
1246 match_child = match->children;
1247 crm_element_value_int(change, PCMK_XE_POSITION, &position);
1248
1249 while ((match_child != NULL)
1250 && (position != pcmk__xml_position(match_child, pcmk__xf_skip))) {
1251 match_child = match_child->next;
1252 }
1253
1254 child = xmlDocCopyNode(change->children, match->doc, 1);
1255 if (child == NULL) {
1256 return ENOMEM;
1257 }
1258
1259 if (match_child) {
1260 crm_trace("Adding %s at position %d", child->name, position);
1261 xmlAddPrevSibling(match_child, child);
1262
1263 } else if (match->last) {
1264 crm_trace("Adding %s at position %d (end)",
1265 child->name, position);
1266 xmlAddNextSibling(match->last, child);
1267
1268 } else {
1269 crm_trace("Adding %s at position %d (first)",
1270 child->name, position);
1271 CRM_LOG_ASSERT(position == 0);
1272 xmlAddChild(match, child);
1273 }
1274 pcmk__xml_mark_created(child);
1275
1276 } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
1277 int position = 0;
1278
1279 crm_element_value_int(change, PCMK_XE_POSITION, &position);
1280 if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
1281 xmlNode *match_child = NULL;
1282 int p = position;
1283
1284 if (p > pcmk__xml_position(match, pcmk__xf_skip)) {
1285 p++;
1286 }
1287
1288 CRM_ASSERT(match->parent != NULL);
1289 match_child = match->parent->children;
1290
1291 while ((match_child != NULL)
1292 && (p != pcmk__xml_position(match_child, pcmk__xf_skip))) {
1293 match_child = match_child->next;
1294 }
1295
1296 crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
1297 match->name, position,
1298 pcmk__xml_position(match, pcmk__xf_skip),
1299 match->prev, (match_child? "next":"last"),
1300 (match_child? match_child : match->parent->last));
1301
1302 if (match_child) {
1303 xmlAddPrevSibling(match_child, match);
1304
1305 } else {
1306 CRM_ASSERT(match->parent->last != NULL);
1307 xmlAddNextSibling(match->parent->last, match);
1308 }
1309
1310 } else {
1311 crm_trace("%s is already in position %d",
1312 match->name, position);
1313 }
1314
1315 if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
1316 crm_err("Moved %s.%s to position %d instead of %d (%p)",
1317 match->name, pcmk__xe_id(match),
1318 pcmk__xml_position(match, pcmk__xf_skip),
1319 position, match->prev);
1320 rc = pcmk_rc_diff_failed;
1321 }
1322 }
1323 }
1324
1325 g_list_free_full(change_objs, free);
1326 return rc;
1327 }
1328
1329 int
1330 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
1331 {
1332 int format = 1;
1333 int rc = pcmk_ok;
1334 xmlNode *old = NULL;
1335 const char *digest = NULL;
1336
1337 if (patchset == NULL) {
1338 return rc;
1339 }
1340
1341 pcmk__log_xml_patchset(LOG_TRACE, patchset);
1342
1343 if (check_version) {
1344 rc = pcmk_rc2legacy(xml_patch_version_check(xml, patchset));
1345 if (rc != pcmk_ok) {
1346 return rc;
1347 }
1348 }
1349
1350 digest = crm_element_value(patchset, PCMK__XA_DIGEST);
1351 if (digest != NULL) {
1352
1353
1354
1355 pcmk__if_tracing(old = pcmk__xml_copy(NULL, xml), {});
1356 }
1357
1358 if (rc == pcmk_ok) {
1359 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
1360 switch (format) {
1361 case 1:
1362
1363 rc = pcmk_rc2legacy(apply_v1_patchset(xml, patchset));
1364 break;
1365 case 2:
1366 rc = pcmk_rc2legacy(apply_v2_patchset(xml, patchset));
1367 break;
1368 default:
1369 crm_err("Unknown patch format: %d", format);
1370 rc = -EINVAL;
1371 }
1372 }
1373
1374 if ((rc == pcmk_ok) && (digest != NULL)) {
1375 char *new_digest = NULL;
1376 char *version = crm_element_value_copy(xml, PCMK_XA_CRM_FEATURE_SET);
1377
1378 new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
1379 if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
1380 crm_info("v%d digest mis-match: expected %s, calculated %s",
1381 format, digest, new_digest);
1382 rc = -pcmk_err_diff_failed;
1383 pcmk__if_tracing(
1384 {
1385 save_xml_to_file(old, "PatchDigest:input", NULL);
1386 save_xml_to_file(xml, "PatchDigest:result", NULL);
1387 save_xml_to_file(patchset, "PatchDigest:diff", NULL);
1388 },
1389 {}
1390 );
1391
1392 } else {
1393 crm_trace("v%d digest matched: expected %s, calculated %s",
1394 format, digest, new_digest);
1395 }
1396 free(new_digest);
1397 free(version);
1398 }
1399 free_xml(old);
1400 return rc;
1401 }
1402
1403
1404 static bool
1405 can_prune_leaf_v1(xmlNode *node)
1406 {
1407 xmlNode *cIter = NULL;
1408 bool can_prune = true;
1409
1410 CRM_CHECK(node != NULL, return false);
1411
1412
1413
1414
1415 if (pcmk__strcase_any_of((const char *) node->name,
1416 PCMK_XE_RESOURCE_REF, PCMK_XE_OBJ_REF,
1417 PCMK_XE_ROLE, PCMK__XE_ROLE_REF,
1418 NULL)) {
1419 return false;
1420 }
1421
1422 for (xmlAttrPtr a = pcmk__xe_first_attr(node); a != NULL; a = a->next) {
1423 const char *p_name = (const char *) a->name;
1424
1425 if (strcmp(p_name, PCMK_XA_ID) == 0) {
1426 continue;
1427 }
1428 can_prune = false;
1429 }
1430
1431 cIter = pcmk__xml_first_child(node);
1432 while (cIter) {
1433 xmlNode *child = cIter;
1434
1435 cIter = pcmk__xml_next(cIter);
1436 if (can_prune_leaf_v1(child)) {
1437 free_xml(child);
1438 } else {
1439 can_prune = false;
1440 }
1441 }
1442 return can_prune;
1443 }
1444
1445
1446 xmlNode *
1447 pcmk__diff_v1_xml_object(xmlNode *old, xmlNode *new, bool suppress)
1448 {
1449 xmlNode *tmp1 = NULL;
1450 xmlNode *diff = pcmk__xe_create(NULL, PCMK_XE_DIFF);
1451 xmlNode *removed = pcmk__xe_create(diff, PCMK__XE_DIFF_REMOVED);
1452 xmlNode *added = pcmk__xe_create(diff, PCMK__XE_DIFF_ADDED);
1453
1454 crm_xml_add(diff, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
1455
1456 tmp1 = subtract_v1_xml_object(removed, old, new, false, NULL,
1457 "removed:top");
1458 if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
1459 free_xml(tmp1);
1460 }
1461
1462 tmp1 = subtract_v1_xml_object(added, new, old, true, NULL, "added:top");
1463 if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
1464 free_xml(tmp1);
1465 }
1466
1467 if ((added->children == NULL) && (removed->children == NULL)) {
1468 free_xml(diff);
1469 diff = NULL;
1470 }
1471
1472 return diff;
1473 }
1474
1475
1476
1477
1478 #include <crm/common/xml_compat.h>
1479
1480 gboolean
1481 apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
1482 {
1483 gboolean result = TRUE;
1484 int root_nodes_seen = 0;
1485 const char *digest = crm_element_value(diff, PCMK__XA_DIGEST);
1486 const char *version = crm_element_value(diff, PCMK_XA_CRM_FEATURE_SET);
1487
1488 xmlNode *child_diff = NULL;
1489 xmlNode *added = pcmk__xe_first_child(diff, PCMK__XE_DIFF_ADDED, NULL,
1490 NULL);
1491 xmlNode *removed = pcmk__xe_first_child(diff, PCMK__XE_DIFF_REMOVED, NULL,
1492 NULL);
1493
1494 CRM_CHECK(new_xml != NULL, return FALSE);
1495
1496 crm_trace("Subtraction Phase");
1497 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
1498 child_diff = pcmk__xml_next(child_diff)) {
1499 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
1500 if (root_nodes_seen == 0) {
1501 *new_xml = subtract_v1_xml_object(NULL, old_xml, child_diff, false,
1502 NULL, NULL);
1503 }
1504 root_nodes_seen++;
1505 }
1506
1507 if (root_nodes_seen == 0) {
1508 *new_xml = pcmk__xml_copy(NULL, old_xml);
1509
1510 } else if (root_nodes_seen > 1) {
1511 crm_err("(-) Diffs cannot contain more than one change set... saw %d",
1512 root_nodes_seen);
1513 result = FALSE;
1514 }
1515
1516 root_nodes_seen = 0;
1517 crm_trace("Addition Phase");
1518 if (result) {
1519 xmlNode *child_diff = NULL;
1520
1521 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
1522 child_diff = pcmk__xml_next(child_diff)) {
1523 CRM_CHECK(root_nodes_seen == 0, result = FALSE);
1524 if (root_nodes_seen == 0) {
1525 pcmk__xml_update(NULL, *new_xml, child_diff, pcmk__xaf_none,
1526 true);
1527 }
1528 root_nodes_seen++;
1529 }
1530 }
1531
1532 if (root_nodes_seen > 1) {
1533 crm_err("(+) Diffs cannot contain more than one change set... saw %d",
1534 root_nodes_seen);
1535 result = FALSE;
1536
1537 } else if (result && (digest != NULL)) {
1538 char *new_digest = NULL;
1539
1540 purge_v1_diff_markers(*new_xml);
1541 new_digest = calculate_xml_versioned_digest(*new_xml, FALSE, TRUE,
1542 version);
1543 if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
1544 crm_info("Digest mis-match: expected %s, calculated %s",
1545 digest, new_digest);
1546 result = FALSE;
1547
1548 pcmk__if_tracing(
1549 {
1550 save_xml_to_file(old_xml, "diff:original", NULL);
1551 save_xml_to_file(diff, "diff:input", NULL);
1552 save_xml_to_file(*new_xml, "diff:new", NULL);
1553 },
1554 {}
1555 );
1556
1557 } else {
1558 crm_trace("Digest matched: expected %s, calculated %s",
1559 digest, new_digest);
1560 }
1561 free(new_digest);
1562
1563 } else if (result) {
1564 purge_v1_diff_markers(*new_xml);
1565 }
1566
1567 return result;
1568 }
1569
1570 void
1571 purge_diff_markers(xmlNode *a_node)
1572 {
1573 purge_v1_diff_markers(a_node);
1574 }
1575
1576 xmlNode *
1577 diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
1578 {
1579 return pcmk__diff_v1_xml_object(old, new, suppress);
1580 }
1581
1582 xmlNode *
1583 subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
1584 gboolean full, gboolean *changed, const char *marker)
1585 {
1586 return subtract_v1_xml_object(parent, left, right, full, changed, marker);
1587 }
1588
1589 gboolean
1590 can_prune_leaf(xmlNode *xml_node)
1591 {
1592 return can_prune_leaf_v1(xml_node);
1593 }
1594
1595
1596