This source file includes following definitions.
- add_xml_changes_to_patchset
- is_config_change
- xml_create_patchset_v2
- xml_create_patchset
- patchset_process_digest
- xml_patch_versions
- xml_patch_version_check
- first_matching_xml_child
- search_v2_xpath
- sort_change_obj_by_position
- apply_v2_patchset
- xml_apply_patchset
- pcmk__cib_element_in_patchset
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/cib_internal.h>
25 #include <crm/common/xml.h>
26 #include <crm/common/xml_internal.h>
27 #include "crmcommon_private.h"
28
29
30
31
32 static void
33 add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
34 {
35 xmlNode *cIter = NULL;
36 xmlAttr *pIter = NULL;
37 xmlNode *change = NULL;
38 xml_node_private_t *nodepriv = xml->_private;
39 const char *value = NULL;
40
41 if (nodepriv == NULL) {
42
43
44
45
46 return;
47 }
48
49
50 if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
51 GString *xpath = pcmk__element_xpath(xml->parent);
52
53 if (xpath != NULL) {
54 int position = pcmk__xml_position(xml, pcmk__xf_deleted);
55
56 change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
57
58 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_CREATE);
59 crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
60 crm_xml_add_int(change, PCMK_XE_POSITION, position);
61 pcmk__xml_copy(change, xml);
62 g_string_free(xpath, TRUE);
63 }
64
65 return;
66 }
67
68
69 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
70 pIter = pIter->next) {
71 xmlNode *attr = NULL;
72
73 nodepriv = pIter->_private;
74 if (!pcmk_any_flags_set(nodepriv->flags, pcmk__xf_deleted|pcmk__xf_dirty)) {
75 continue;
76 }
77
78 if (change == NULL) {
79 GString *xpath = pcmk__element_xpath(xml);
80
81 if (xpath != NULL) {
82 change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
83
84 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MODIFY);
85 crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
86
87 change = pcmk__xe_create(change, PCMK_XE_CHANGE_LIST);
88 g_string_free(xpath, TRUE);
89 }
90 }
91
92 attr = pcmk__xe_create(change, PCMK_XE_CHANGE_ATTR);
93
94 crm_xml_add(attr, PCMK_XA_NAME, (const char *) pIter->name);
95 if (nodepriv->flags & pcmk__xf_deleted) {
96 crm_xml_add(attr, PCMK_XA_OPERATION, "unset");
97
98 } else {
99 crm_xml_add(attr, PCMK_XA_OPERATION, "set");
100
101 value = pcmk__xml_attr_value(pIter);
102 crm_xml_add(attr, PCMK_XA_VALUE, value);
103 }
104 }
105
106 if (change) {
107 xmlNode *result = NULL;
108
109 change = pcmk__xe_create(change->parent, PCMK_XE_CHANGE_RESULT);
110 result = pcmk__xe_create(change, (const char *)xml->name);
111
112 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
113 pIter = pIter->next) {
114 nodepriv = pIter->_private;
115 if (!pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
116 value = crm_element_value(xml, (const char *) pIter->name);
117 crm_xml_add(result, (const char *)pIter->name, value);
118 }
119 }
120 }
121
122
123 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
124 cIter = pcmk__xml_next(cIter)) {
125 add_xml_changes_to_patchset(cIter, patchset);
126 }
127
128 nodepriv = xml->_private;
129 if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
130 GString *xpath = pcmk__element_xpath(xml);
131
132 crm_trace("%s.%s moved to position %d",
133 xml->name, pcmk__xe_id(xml),
134 pcmk__xml_position(xml, pcmk__xf_skip));
135
136 if (xpath != NULL) {
137 change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
138
139 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MOVE);
140 crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
141 crm_xml_add_int(change, PCMK_XE_POSITION,
142 pcmk__xml_position(xml, pcmk__xf_deleted));
143 g_string_free(xpath, TRUE);
144 }
145 }
146 }
147
148 static bool
149 is_config_change(xmlNode *xml)
150 {
151 GList *gIter = NULL;
152 xml_node_private_t *nodepriv = NULL;
153 xml_doc_private_t *docpriv;
154 xmlNode *config = pcmk__xe_first_child(xml, PCMK_XE_CONFIGURATION, NULL,
155 NULL);
156
157 if (config) {
158 nodepriv = config->_private;
159 }
160 if ((nodepriv != NULL) && pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
161 return TRUE;
162 }
163
164 if ((xml->doc != NULL) && (xml->doc->_private != NULL)) {
165 docpriv = xml->doc->_private;
166 for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
167 pcmk__deleted_xml_t *deleted_obj = gIter->data;
168
169 if (strstr(deleted_obj->path,
170 "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION) != NULL) {
171 return TRUE;
172 }
173 }
174 }
175 return FALSE;
176 }
177
178 static xmlNode *
179 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
180 {
181 int lpc = 0;
182 GList *gIter = NULL;
183 xml_doc_private_t *docpriv;
184
185 xmlNode *v = NULL;
186 xmlNode *version = NULL;
187 xmlNode *patchset = NULL;
188 const char *vfields[] = {
189 PCMK_XA_ADMIN_EPOCH,
190 PCMK_XA_EPOCH,
191 PCMK_XA_NUM_UPDATES,
192 };
193
194 pcmk__assert(target != NULL);
195 if (!xml_document_dirty(target)) {
196 return NULL;
197 }
198
199 pcmk__assert(target->doc != NULL);
200 docpriv = target->doc->_private;
201
202 patchset = pcmk__xe_create(NULL, PCMK_XE_DIFF);
203 crm_xml_add_int(patchset, PCMK_XA_FORMAT, 2);
204
205 version = pcmk__xe_create(patchset, PCMK_XE_VERSION);
206
207 v = pcmk__xe_create(version, PCMK_XE_SOURCE);
208 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
209 const char *value = crm_element_value(source, vfields[lpc]);
210
211 if (value == NULL) {
212 value = "1";
213 }
214 crm_xml_add(v, vfields[lpc], value);
215 }
216
217 v = pcmk__xe_create(version, PCMK_XE_TARGET);
218 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
219 const char *value = crm_element_value(target, vfields[lpc]);
220
221 if (value == NULL) {
222 value = "1";
223 }
224 crm_xml_add(v, vfields[lpc], value);
225 }
226
227 for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
228 pcmk__deleted_xml_t *deleted_obj = gIter->data;
229 xmlNode *change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
230
231 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_DELETE);
232 crm_xml_add(change, PCMK_XA_PATH, deleted_obj->path);
233 if (deleted_obj->position >= 0) {
234 crm_xml_add_int(change, PCMK_XE_POSITION, deleted_obj->position);
235 }
236 }
237
238 add_xml_changes_to_patchset(target, patchset);
239 return patchset;
240 }
241
242 xmlNode *
243 xml_create_patchset(int format, xmlNode *source, xmlNode *target,
244 bool *config_changed, bool manage_version)
245 {
246 bool local_config_changed = false;
247
248 if (format == 0) {
249 format = 2;
250 }
251 if (format != 2) {
252 crm_err("Unknown patch format: %d", format);
253 return NULL;
254 }
255
256 xml_acl_disable(target);
257 if (!xml_document_dirty(target)) {
258 crm_trace("No change %d", format);
259 return NULL;
260 }
261
262 if (config_changed == NULL) {
263 config_changed = &local_config_changed;
264 }
265 *config_changed = is_config_change(target);
266
267 if (manage_version) {
268 int counter = 0;
269
270 if (*config_changed) {
271 crm_xml_add(target, PCMK_XA_NUM_UPDATES, "0");
272
273 crm_element_value_int(target, PCMK_XA_EPOCH, &counter);
274 crm_xml_add_int(target, PCMK_XA_EPOCH, counter + 1);
275
276 } else {
277 crm_element_value_int(target, PCMK_XA_NUM_UPDATES, &counter);
278 crm_xml_add_int(target, PCMK_XA_NUM_UPDATES, counter + 1);
279 }
280 }
281
282 return xml_create_patchset_v2(source, target);
283 }
284
285 void
286 patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target,
287 bool with_digest)
288 {
289 char *digest = NULL;
290
291 if ((patch == NULL) || (source == NULL) || (target == NULL)
292 || !with_digest) {
293 return;
294 }
295
296
297
298
299 CRM_LOG_ASSERT(!xml_document_dirty(target));
300
301 digest = pcmk__digest_xml(target, true);
302
303 crm_xml_add(patch, PCMK__XA_DIGEST, digest);
304 free(digest);
305
306 return;
307 }
308
309
310 bool
311 xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
312 {
313 static const char *const vfields[] = {
314 PCMK_XA_ADMIN_EPOCH,
315 PCMK_XA_EPOCH,
316 PCMK_XA_NUM_UPDATES,
317 };
318
319 const xmlNode *version = pcmk__xe_first_child(patchset, PCMK_XE_VERSION,
320 NULL, NULL);
321 const xmlNode *source = pcmk__xe_first_child(version, PCMK_XE_SOURCE, NULL,
322 NULL);
323 const xmlNode *target = pcmk__xe_first_child(version, PCMK_XE_TARGET, NULL,
324 NULL);
325 int format = 1;
326
327 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
328 if (format != 2) {
329 crm_err("Unknown patch format: %d", format);
330 return -EINVAL;
331 }
332
333 if (source != NULL) {
334 for (int i = 0; i < PCMK__NELEM(vfields); i++) {
335 crm_element_value_int(source, vfields[i], &(del[i]));
336 crm_trace("Got %d for del[%s]", del[i], vfields[i]);
337 }
338 }
339
340 if (target != NULL) {
341 for (int i = 0; i < PCMK__NELEM(vfields); i++) {
342 crm_element_value_int(target, vfields[i], &(add[i]));
343 crm_trace("Got %d for add[%s]", add[i], vfields[i]);
344 }
345 }
346 return pcmk_ok;
347 }
348
349
350
351
352
353
354
355
356
357
358 static int
359 xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset)
360 {
361 int lpc = 0;
362 bool changed = FALSE;
363
364 int this[] = { 0, 0, 0 };
365 int add[] = { 0, 0, 0 };
366 int del[] = { 0, 0, 0 };
367
368 const char *vfields[] = {
369 PCMK_XA_ADMIN_EPOCH,
370 PCMK_XA_EPOCH,
371 PCMK_XA_NUM_UPDATES,
372 };
373
374 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
375 crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
376 crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
377 if (this[lpc] < 0) {
378 this[lpc] = 0;
379 }
380 }
381
382
383 add[0] = this[0];
384 add[1] = this[1];
385 add[2] = this[2] + 1;
386 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
387 del[lpc] = this[lpc];
388 }
389
390 xml_patch_versions(patchset, add, del);
391
392 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
393 if (this[lpc] < del[lpc]) {
394 crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)",
395 vfields[lpc], this[0], this[1], this[2],
396 del[0], del[1], del[2], add[0], add[1], add[2]);
397 return pcmk_rc_diff_resync;
398
399 } else if (this[lpc] > del[lpc]) {
400 crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p",
401 vfields[lpc], this[0], this[1], this[2],
402 del[0], del[1], del[2], add[0], add[1], add[2], patchset);
403 crm_log_xml_info(patchset, "OldPatch");
404 return pcmk_rc_old_data;
405 }
406 }
407
408 for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
409 if (add[lpc] > del[lpc]) {
410 changed = TRUE;
411 }
412 }
413
414 if (!changed) {
415 crm_notice("Versions did not change in patch %d.%d.%d",
416 add[0], add[1], add[2]);
417 return pcmk_rc_old_data;
418 }
419
420 crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
421 add[0], add[1], add[2], this[0], this[1], this[2]);
422 return pcmk_rc_ok;
423 }
424
425
426 static xmlNode *
427 first_matching_xml_child(const xmlNode *parent, const char *name,
428 const char *id, int position)
429 {
430 xmlNode *cIter = NULL;
431
432 for (cIter = pcmk__xml_first_child(parent); cIter != NULL;
433 cIter = pcmk__xml_next(cIter)) {
434 if (strcmp((const char *) cIter->name, name) != 0) {
435 continue;
436 } else if (id) {
437 const char *cid = pcmk__xe_id(cIter);
438
439 if ((cid == NULL) || (strcmp(cid, id) != 0)) {
440 continue;
441 }
442 }
443
444
445 if ((cIter->type == XML_COMMENT_NODE)
446 && (position >= 0)
447 && (pcmk__xml_position(cIter, pcmk__xf_skip) != position)) {
448 continue;
449 }
450
451 return cIter;
452 }
453 return NULL;
454 }
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469 static xmlNode *
470 search_v2_xpath(const xmlNode *top, const char *key, int target_position)
471 {
472 xmlNode *target = (xmlNode *) top->doc;
473 const char *current = key;
474 char *section;
475 char *remainder;
476 char *id;
477 char *tag;
478 char *path = NULL;
479 int rc;
480 size_t key_len;
481
482 CRM_CHECK(key != NULL, return NULL);
483 key_len = strlen(key);
484
485
486
487
488
489 remainder = pcmk__assert_alloc(key_len, sizeof(char));
490 section = pcmk__assert_alloc(key_len, sizeof(char));
491 id = pcmk__assert_alloc(key_len, sizeof(char));
492 tag = pcmk__assert_alloc(key_len, sizeof(char));
493
494 do {
495
496 rc = sscanf(current, "/%[^/]%s", section, remainder);
497 if (rc > 0) {
498
499 int f = sscanf(section, "%[^[][@" PCMK_XA_ID "='%[^']", tag, id);
500 int current_position = -1;
501
502
503
504
505 if ((rc == 1) && (target_position >= 0)) {
506 current_position = target_position;
507 }
508
509 switch (f) {
510 case 1:
511 target = first_matching_xml_child(target, tag, NULL,
512 current_position);
513 break;
514 case 2:
515 target = first_matching_xml_child(target, tag, id,
516 current_position);
517 break;
518 default:
519
520 target = NULL;
521 break;
522 }
523 current = remainder;
524 }
525
526
527 } while ((rc == 2) && target);
528
529 if (target) {
530 crm_trace("Found %s for %s",
531 (path = (char *) xmlGetNodePath(target)), key);
532 free(path);
533 } else {
534 crm_debug("No match for %s", key);
535 }
536
537 free(remainder);
538 free(section);
539 free(tag);
540 free(id);
541 return target;
542 }
543
544 typedef struct xml_change_obj_s {
545 const xmlNode *change;
546 xmlNode *match;
547 } xml_change_obj_t;
548
549 static gint
550 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
551 {
552 const xml_change_obj_t *change_obj_a = a;
553 const xml_change_obj_t *change_obj_b = b;
554 int position_a = -1;
555 int position_b = -1;
556
557 crm_element_value_int(change_obj_a->change, PCMK_XE_POSITION, &position_a);
558 crm_element_value_int(change_obj_b->change, PCMK_XE_POSITION, &position_b);
559
560 if (position_a < position_b) {
561 return -1;
562
563 } else if (position_a > position_b) {
564 return 1;
565 }
566
567 return 0;
568 }
569
570
571
572
573
574
575
576
577
578
579 static int
580 apply_v2_patchset(xmlNode *xml, const xmlNode *patchset)
581 {
582 int rc = pcmk_rc_ok;
583 const xmlNode *change = NULL;
584 GList *change_objs = NULL;
585 GList *gIter = NULL;
586
587 for (change = pcmk__xml_first_child(patchset); change != NULL;
588 change = pcmk__xml_next(change)) {
589 xmlNode *match = NULL;
590 const char *op = crm_element_value(change, PCMK_XA_OPERATION);
591 const char *xpath = crm_element_value(change, PCMK_XA_PATH);
592 int position = -1;
593
594 if (op == NULL) {
595 continue;
596 }
597
598 crm_trace("Processing %s %s", change->name, op);
599
600
601
602
603 if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
604 crm_element_value_int(change, PCMK_XE_POSITION, &position);
605 }
606 match = search_v2_xpath(xml, xpath, position);
607 crm_trace("Performing %s on %s with %p", op, xpath, match);
608
609 if ((match == NULL) && (strcmp(op, PCMK_VALUE_DELETE) == 0)) {
610 crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
611 continue;
612
613 } else if (match == NULL) {
614 crm_err("No %s match for %s in %p", op, xpath, xml->doc);
615 rc = pcmk_rc_diff_failed;
616 continue;
617
618 } else if (pcmk__str_any_of(op,
619 PCMK_VALUE_CREATE, PCMK_VALUE_MOVE, NULL)) {
620
621 xml_change_obj_t *change_obj =
622 pcmk__assert_alloc(1, sizeof(xml_change_obj_t));
623
624 change_obj->change = change;
625 change_obj->match = match;
626
627 change_objs = g_list_append(change_objs, change_obj);
628
629 if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
630
631 if ((match->parent != NULL) && (match->parent->last != NULL)) {
632 xmlAddNextSibling(match->parent->last, match);
633 }
634 }
635
636 } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
637 pcmk__xml_free(match);
638
639 } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
640 const xmlNode *child = pcmk__xe_first_child(change,
641 PCMK_XE_CHANGE_RESULT,
642 NULL, NULL);
643 const xmlNode *attrs = pcmk__xml_first_child(child);
644
645 if (attrs == NULL) {
646 rc = ENOMSG;
647 continue;
648 }
649 pcmk__xe_remove_matching_attrs(match, NULL, NULL);
650
651 for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
652 pIter = pIter->next) {
653 const char *name = (const char *) pIter->name;
654 const char *value = pcmk__xml_attr_value(pIter);
655
656 crm_xml_add(match, name, value);
657 }
658
659 } else {
660 crm_err("Unknown operation: %s", op);
661 rc = pcmk_rc_diff_failed;
662 }
663 }
664
665
666 change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
667
668 for (gIter = change_objs; gIter; gIter = gIter->next) {
669 xml_change_obj_t *change_obj = gIter->data;
670 xmlNode *match = change_obj->match;
671 const char *op = NULL;
672 const char *xpath = NULL;
673
674 change = change_obj->change;
675
676 op = crm_element_value(change, PCMK_XA_OPERATION);
677 xpath = crm_element_value(change, PCMK_XA_PATH);
678
679 crm_trace("Continue performing %s on %s with %p", op, xpath, match);
680
681 if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
682 int position = 0;
683 xmlNode *child = NULL;
684 xmlNode *match_child = NULL;
685
686 match_child = match->children;
687 crm_element_value_int(change, PCMK_XE_POSITION, &position);
688
689 while ((match_child != NULL)
690 && (position != pcmk__xml_position(match_child, pcmk__xf_skip))) {
691 match_child = match_child->next;
692 }
693
694 child = pcmk__xml_copy(match, change->children);
695
696 if (match_child != NULL) {
697 crm_trace("Adding %s at position %d", child->name, position);
698 xmlAddPrevSibling(match_child, child);
699
700 } else {
701 crm_trace("Adding %s at position %d (end)",
702 child->name, position);
703 }
704
705 } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
706 int position = 0;
707
708 crm_element_value_int(change, PCMK_XE_POSITION, &position);
709 if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
710 xmlNode *match_child = NULL;
711 int p = position;
712
713 if (p > pcmk__xml_position(match, pcmk__xf_skip)) {
714 p++;
715 }
716
717 pcmk__assert(match->parent != NULL);
718 match_child = match->parent->children;
719
720 while ((match_child != NULL)
721 && (p != pcmk__xml_position(match_child, pcmk__xf_skip))) {
722 match_child = match_child->next;
723 }
724
725 crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
726 match->name, position,
727 pcmk__xml_position(match, pcmk__xf_skip),
728 match->prev, (match_child? "next":"last"),
729 (match_child? match_child : match->parent->last));
730
731 if (match_child) {
732 xmlAddPrevSibling(match_child, match);
733
734 } else {
735 pcmk__assert(match->parent->last != NULL);
736 xmlAddNextSibling(match->parent->last, match);
737 }
738
739 } else {
740 crm_trace("%s is already in position %d",
741 match->name, position);
742 }
743
744 if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
745 crm_err("Moved %s.%s to position %d instead of %d (%p)",
746 match->name, pcmk__xe_id(match),
747 pcmk__xml_position(match, pcmk__xf_skip),
748 position, match->prev);
749 rc = pcmk_rc_diff_failed;
750 }
751 }
752 }
753
754 g_list_free_full(change_objs, free);
755 return rc;
756 }
757
758 int
759 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
760 {
761 int format = 1;
762 int rc = pcmk_ok;
763 xmlNode *old = NULL;
764 const char *digest = NULL;
765
766 if (patchset == NULL) {
767 return rc;
768 }
769
770 pcmk__log_xml_patchset(LOG_TRACE, patchset);
771
772 if (check_version) {
773 rc = pcmk_rc2legacy(xml_patch_version_check(xml, patchset));
774 if (rc != pcmk_ok) {
775 return rc;
776 }
777 }
778
779 digest = crm_element_value(patchset, PCMK__XA_DIGEST);
780 if (digest != NULL) {
781
782
783
784 pcmk__if_tracing(old = pcmk__xml_copy(NULL, xml), {});
785 }
786
787 if (rc == pcmk_ok) {
788 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
789
790 if (format != 2) {
791 crm_err("Unknown patch format: %d", format);
792 rc = -EINVAL;
793
794 } else {
795 rc = pcmk_rc2legacy(apply_v2_patchset(xml, patchset));
796 }
797 }
798
799 if ((rc == pcmk_ok) && (digest != NULL)) {
800 char *new_digest = NULL;
801
802 new_digest = pcmk__digest_xml(xml, true);
803 if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
804 crm_info("v%d digest mis-match: expected %s, calculated %s",
805 format, digest, new_digest);
806 rc = -pcmk_err_diff_failed;
807 pcmk__if_tracing(
808 {
809 save_xml_to_file(old, "PatchDigest:input", NULL);
810 save_xml_to_file(xml, "PatchDigest:result", NULL);
811 save_xml_to_file(patchset, "PatchDigest:diff", NULL);
812 },
813 {}
814 );
815
816 } else {
817 crm_trace("v%d digest matched: expected %s, calculated %s",
818 format, digest, new_digest);
819 }
820 free(new_digest);
821 }
822 pcmk__xml_free(old);
823 return rc;
824 }
825
826 bool
827 pcmk__cib_element_in_patchset(const xmlNode *patchset, const char *element)
828 {
829 const char *element_xpath = pcmk__cib_abs_xpath_for(element);
830 const char *parent_xpath = pcmk_cib_parent_name_for(element);
831 char *element_regex = NULL;
832 bool rc = false;
833 int format = 1;
834
835 pcmk__assert(patchset != NULL);
836
837 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
838 if (format != 2) {
839 crm_warn("Unknown patch format: %d", format);
840 return false;
841 }
842
843 CRM_CHECK(element_xpath != NULL, return false);
844
845
846
847
848
849
850
851
852 element_regex = crm_strdup_printf("^%s(/|$)", element_xpath);
853
854 for (const xmlNode *change = pcmk__xe_first_child(patchset, PCMK_XE_CHANGE,
855 NULL, NULL);
856 change != NULL; change = pcmk__xe_next(change, PCMK_XE_CHANGE)) {
857
858 const char *op = crm_element_value(change, PCMK_XA_OPERATION);
859 const char *diff_xpath = crm_element_value(change, PCMK_XA_PATH);
860
861 if (pcmk__str_eq(diff_xpath, element_regex, pcmk__str_regex)) {
862
863 rc = true;
864 break;
865 }
866
867 if (pcmk__str_eq(op, PCMK_VALUE_CREATE, pcmk__str_none)
868 && pcmk__str_eq(diff_xpath, parent_xpath, pcmk__str_none)
869 && pcmk__xe_is(pcmk__xe_first_child(change, NULL, NULL, NULL),
870 element)) {
871
872 rc = true;
873 break;
874 }
875 }
876
877 free(element_regex);
878 return rc;
879 }