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