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