blob: cef0713d5c062d5bd8f3dd55f4a65f5182b9b64f [file] [log] [blame]
Michal Vaskod59035b2020-07-08 12:00:06 +02001/**
2 * @file diff.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief diff functions
5 *
6 * Copyright (c) 2020 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14#define _XOPEN_SOURCE 500
Radek Krejci33185cb2020-07-11 23:15:22 +020015#define _POSIX_C_SOURCE 200809L
Michal Vaskod59035b2020-07-08 12:00:06 +020016
17#include "diff.h"
18
19#include <assert.h>
20#include <stddef.h>
21#include <string.h>
22
23#include "common.h"
24#include "log.h"
25#include "tree_data_internal.h"
26#include "tree_schema.h"
27#include "tree_schema_internal.h"
28
29static const char *
30lyd_diff_op2str(enum lyd_diff_op op)
31{
32 switch (op) {
33 case LYD_DIFF_OP_CREATE:
34 return "create";
35 case LYD_DIFF_OP_DELETE:
36 return "delete";
37 case LYD_DIFF_OP_REPLACE:
38 return "replace";
39 case LYD_DIFF_OP_NONE:
40 return "none";
41 }
42
43 LOGINT(NULL);
44 return NULL;
45}
46
Michal Vaskoe6323f62020-07-09 15:49:02 +020047static enum lyd_diff_op
48lyd_diff_str2op(const char *str)
49{
50 switch (str[0]) {
51 case 'c':
52 assert(!strcmp(str, "create"));
53 return LYD_DIFF_OP_CREATE;
54 case 'd':
55 assert(!strcmp(str, "delete"));
56 return LYD_DIFF_OP_DELETE;
57 case 'r':
58 assert(!strcmp(str, "replace"));
59 return LYD_DIFF_OP_REPLACE;
60 case 'n':
61 assert(!strcmp(str, "none"));
62 return LYD_DIFF_OP_NONE;
63 }
64
65 LOGINT(NULL);
66 return 0;
67}
68
Michal Vaskod59035b2020-07-08 12:00:06 +020069LY_ERR
70lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
71 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
72{
73 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
74 const struct lyd_node *parent = NULL;
75 const struct lys_module *yang_mod;
76
77 assert(diff);
78
79 /* find the first existing parent */
80 siblings = *diff;
81 while (1) {
82 /* find next node parent */
83 parent = node;
84 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
85 parent = (struct lyd_node *)parent->parent;
86 }
87 if (parent == node) {
88 /* no more parents to find */
89 break;
90 }
91
92 /* check whether it exists in the diff */
93 if (lyd_find_sibling_first(siblings, parent, &match)) {
94 break;
95 }
96
97 /* another parent found */
98 diff_parent = match;
99
100 /* move down in the diff */
101 siblings = LYD_CHILD(match);
102 }
103
104 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200105 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
106 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200107
108 /* find the first duplicated parent */
109 if (!diff_parent) {
110 diff_parent = (struct lyd_node *)dup->parent;
111 while (diff_parent && diff_parent->parent) {
112 diff_parent = (struct lyd_node *)diff_parent->parent;
113 }
114 } else {
115 diff_parent = (struct lyd_node *)dup;
116 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
117 diff_parent = (struct lyd_node *)diff_parent->parent;
118 }
119 }
120
121 /* no parent existed, must be manually connected */
122 if (!diff_parent) {
123 /* there actually was no parent to duplicate */
124 if (*diff) {
125 lyd_insert_sibling(*diff, dup);
126 } else {
127 *diff = dup;
128 }
129 } else if (!diff_parent->parent) {
130 if (*diff) {
131 lyd_insert_sibling(*diff, diff_parent);
132 } else {
133 *diff = diff_parent;
134 }
135 }
136
137 /* get module with the operation metadata */
138 yang_mod = LYD_NODE_CTX(node)->list.objs[1];
139 assert(!strcmp(yang_mod->name, "yang"));
140
141 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200142 if (diff_parent && (diff_parent != dup)) {
143 LY_CHECK_RET(lyd_new_meta(diff_parent, yang_mod, "operation", "none", NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200144 }
145
146 /* add subtree operation */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200147 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "operation", lyd_diff_op2str(op), NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200148
149 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200150 if (orig_default) {
151 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-default", orig_default, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200152 }
153
154 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200155 if (orig_value) {
156 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-value", orig_value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200157 }
158
159 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200160 if (key) {
161 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "key", key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200162 }
163
164 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200165 if (value) {
166 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "value", value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200167 }
168
169 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200170 if (orig_key) {
171 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-key", orig_key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200172 }
173
174 return LY_SUCCESS;
175}
176
177/**
178 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
179 *
180 * @param[in] first
181 * @param[in] schema Schema node of the list/leaf-list.
182 * @param[in,out] userord Sized array of userord items.
183 * @return Userord item for all the user-ordered list/leaf-list instances.
184 */
185static struct lyd_diff_userord *
186lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
187{
188 struct lyd_diff_userord *item;
189 const struct lyd_node *iter, **node;
190 LY_ARRAY_COUNT_TYPE u;
191
192 LY_ARRAY_FOR(*userord, u) {
193 if ((*userord)[u].schema == schema) {
194 return &(*userord)[u];
195 }
196 }
197
198 /* it was not added yet, add it now */
199 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
200
201 item->schema = schema;
202 item->pos = 0;
203 item->inst = NULL;
204
205 /* store all the instance pointers in the current order */
206 if (first) {
207 if (first->parent) {
208 iter = first->parent->child;
209 } else {
210 for (iter = first; iter->prev->next; iter = iter->prev);
211 }
212 for (; iter; iter = iter->next) {
213 if (iter->schema == first->schema) {
214 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
215 *node = iter;
216 }
217 }
218 }
219
220 return item;
221}
222
223/**
224 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
225 * lists/leaf-lists.
226 *
227 * @param[in] first Node from the first tree, can be NULL (on create).
228 * @param[in] second Node from the second tree, can be NULL (on delete).
229 * @param[in] options Diff options.
230 * @param[in,out] userord Sized array of userord items for keeping the current node order.
231 * @param[out] op Operation.
232 * @param[out] orig_default Original default metadata.
233 * @param[out] value Value metadata.
234 * @param[out] orig_value Original value metadata
235 * @param[out] key Key metadata.
236 * @param[out] orig_key Original key metadata.
237 * @return LY_SUCCESS on success,
238 * @return LY_ENOT if there is no change to be added into diff,
239 * @return LY_ERR value on other errors.
240 */
241static LY_ERR
242lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, int options,
243 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
244 char **orig_value, char **key, char **orig_key)
245{
246 const struct lysc_node *schema;
247 int dynamic;
248 size_t buflen, bufused, first_pos, second_pos;
249 struct lyd_diff_userord *userord_item;
250
251 assert(first || second);
252
253 *orig_default = NULL;
254 *value = NULL;
255 *orig_value = NULL;
256 *key = NULL;
257 *orig_key = NULL;
258
259 schema = first ? first->schema : second->schema;
260 assert(lysc_is_userordered(schema));
261
262 /* get userord entry */
263 userord_item = lyd_diff_userord_get(first, schema, userord);
264 LY_CHECK_RET(!userord_item, LY_EMEM);
265
266 /* prepare position of the next instance */
267 second_pos = userord_item->pos++;
268
269 /* find user-ordered first position */
270 if (first) {
271 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
272 if (userord_item->inst[first_pos] == first) {
273 break;
274 }
275 }
276 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
277 } else {
278 first_pos = 0;
279 }
280
281 /* learn operation first */
282 if (!second) {
283 *op = LYD_DIFF_OP_DELETE;
284 } else if (!first) {
285 *op = LYD_DIFF_OP_CREATE;
286 } else {
287 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
288 if (lyd_compare(second, userord_item->inst[second_pos], 0)) {
289 /* in first, there is a different instance on the second position, we are going to move 'first' node */
290 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200291 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200292 /* default flag change */
293 *op = LYD_DIFF_OP_NONE;
294 } else {
295 /* no changes */
296 return LY_ENOT;
297 }
298 }
299
300 /*
301 * set each attribute correctly based on the operation and node type
302 */
303
304 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200305 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype == LYS_LEAFLIST)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200306 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
307 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200308 if (first->flags & LYD_DEFAULT) {
309 *orig_default = "true";
310 } else {
311 *orig_default = "false";
312 }
313 }
314
315 /* value */
316 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
317 if (second_pos) {
318 *value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[second_pos - 1], &dynamic);
319 if (!dynamic) {
320 *value = strdup(*value);
321 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
322 }
323 } else {
324 *value = strdup("");
325 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
326 }
327 }
328
329 /* orig-value */
330 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
331 if (first_pos) {
332 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[first_pos - 1], &dynamic);
333 if (!dynamic) {
334 *orig_value = strdup(*orig_value);
335 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
336 }
337 } else {
338 *orig_value = strdup("");
339 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
340 }
341 }
342
343 /* key */
344 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op ==LYD_DIFF_OP_CREATE))) {
345 if (second_pos) {
346 buflen = bufused = 0;
347 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
348 } else {
349 *key = strdup("");
350 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
351 }
352 }
353
354 /* orig-key */
355 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
356 if (first_pos) {
357 buflen = bufused = 0;
358 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
359 } else {
360 *orig_key = strdup("");
361 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
362 }
363 }
364
365 /*
366 * update our instances - apply the change
367 */
368 if (*op == LYD_DIFF_OP_CREATE) {
369 /* insert the instance */
370 LY_ARRAY_RESIZE_ERR_RET(schema->module->ctx, userord_item->inst, LY_ARRAY_COUNT(userord_item->inst) + 1,
371 ;, LY_EMEM);
372 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
373 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
374 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
375 }
376 LY_ARRAY_INCREMENT(userord_item->inst);
377 userord_item->inst[second_pos] = second;
378
379 } else if (*op == LYD_DIFF_OP_DELETE) {
380 /* remove the instance */
381 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
382 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
383 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
384 }
385 LY_ARRAY_DECREMENT(userord_item->inst);
386
387 } else if (*op == LYD_DIFF_OP_REPLACE) {
388 /* move the instances */
389 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
390 (first_pos - second_pos) * sizeof *userord_item->inst);
391 userord_item->inst[second_pos] = first;
392 }
393
394 return LY_SUCCESS;
395}
396
397/**
398 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
399 * lists/leaf-lists.
400 *
401 * @param[in] first Node from the first tree, can be NULL (on create).
402 * @param[in] second Node from the second tree, can be NULL (on delete).
403 * @param[in] options Diff options.
404 * @param[out] op Operation.
405 * @param[out] orig_default Original default metadata.
406 * @param[out] orig_value Original value metadata.
407 * @return LY_SUCCESS on success,
408 * @return LY_ENOT if there is no change to be added into diff,
409 * @return LY_ERR value on other errors.
410 */
411static LY_ERR
412lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, int options, enum lyd_diff_op *op,
413 const char **orig_default, char **orig_value)
414{
415 const struct lysc_node *schema;
416 int dynamic;
417
418 assert(first || second);
419
420 *orig_default = NULL;
421 *orig_value = NULL;
422
423 schema = first ? first->schema : second->schema;
424 assert(!lysc_is_userordered(schema));
425
426 /* learn operation first */
427 if (!second) {
428 *op = LYD_DIFF_OP_DELETE;
429 } else if (!first) {
430 *op = LYD_DIFF_OP_CREATE;
431 } else {
432 switch (schema->nodetype) {
433 case LYS_CONTAINER:
434 case LYS_RPC:
435 case LYS_ACTION:
436 case LYS_NOTIF:
437 /* no changes */
438 return LY_ENOT;
439 case LYS_LIST:
440 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200441 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200442 /* default flag change */
443 *op = LYD_DIFF_OP_NONE;
444 } else {
445 /* no changes */
446 return LY_ENOT;
447 }
448 break;
449 case LYS_LEAF:
450 case LYS_ANYXML:
451 case LYS_ANYDATA:
452 if (lyd_compare(first, second, 0)) {
453 /* different values */
454 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200455 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200456 /* default flag change */
457 *op = LYD_DIFF_OP_NONE;
458 } else {
459 /* no changes */
460 return LY_ENOT;
461 }
462 break;
463 default:
464 LOGINT_RET(schema->module->ctx);
465 }
466 }
467
468 /*
469 * set each attribute correctly based on the operation and node type
470 */
471
472 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200473 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype & LYD_NODE_TERM)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200474 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
475 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200476 if (first->flags & LYD_DEFAULT) {
477 *orig_default = "true";
478 } else {
479 *orig_default = "false";
480 }
481 }
482
483 /* orig-value */
484 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
485 /* leaf */
486 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)first, &dynamic);
487 if (!dynamic) {
488 *orig_value = strdup(*orig_value);
489 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
490 }
491 }
492
493 return LY_SUCCESS;
494}
495
496/**
497 * @brief Perform diff for all siblings at certain depth, recursively.
498 *
499 * For user-ordered lists/leaf-lists a specific structure is used for storing
500 * the current order. The idea is to apply all the generated diff changes
501 * virtually on the first tree so that we can continue to generate correct
502 * changes after some were already generated.
503 *
504 * The algorithm then uses second tree position-based changes with a before
505 * (preceding) item anchor.
506 *
507 * Example:
508 *
509 * Virtual first tree leaf-list order:
510 * 1 2 [3] 4 5
511 *
512 * Second tree leaf-list order:
513 * 1 2 [5] 3 4
514 *
515 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
516 * match - they do not - move nodes so that the 3rd position node is final ->
517 * -> move node 5 to the 3rd position -> move node 5 after node 2.
518 *
519 * Required properties:
520 * Stored operations (move) should not be affected by later operations -
521 * - would cause a redundantly long list of operations, possibly inifinite.
522 *
523 * Implemenation justification:
524 * First, all delete operations and only then move/create operations are stored.
525 * Also, preceding anchor is used and after each iteration another node is
526 * at its final position. That results in the invariant that all preceding
527 * nodes are final and will not be changed by the later operations, meaning
528 * they can safely be used as anchors for the later operations.
529 *
530 * @param[in] first First tree first sibling.
531 * @param[in] second Second tree first sibling.
532 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200533 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200534 * @param[in,out] diff Diff to append to.
535 * @return LY_ERR value.
536 */
537static LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +0200538lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, int options, int nosiblings,
539 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200540{
541 LY_ERR ret = LY_SUCCESS;
542 const struct lyd_node *iter_first, *iter_second;
543 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200544 struct lyd_diff_userord *userord = NULL;
545 LY_ARRAY_COUNT_TYPE u;
546 enum lyd_diff_op op;
547 const char *orig_default;
548 char *orig_value, *key, *value, *orig_key;
549
Michal Vaskod59035b2020-07-08 12:00:06 +0200550 /* compare first tree to the second tree - delete, replace, none */
551 LY_LIST_FOR(first, iter_first) {
552 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200553 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200554 /* skip default nodes */
555 continue;
556 }
557
558 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
559 /* try to find the exact instance */
560 lyd_find_sibling_first(second, iter_first, &match_second);
561 } else {
562 /* try to simply find the node, there cannot be more instances */
563 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
564 }
565
Michal Vasko3a41dff2020-07-15 14:30:28 +0200566 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200567 /* ignore default nodes */
568 match_second = NULL;
569 }
570
571 if (lysc_is_userordered(iter_first->schema)) {
572 if (match_second) {
573 /* we are handling only user-ordered node delete now */
574 continue;
575 }
576
577 /* get all the attributes */
578 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
579 &value, &orig_value, &key, &orig_key), cleanup);
580
581 /* there must be changes, it is deleted */
582 assert(op == LYD_DIFF_OP_DELETE);
583 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
584
585 free(orig_value);
586 free(key);
587 free(value);
588 free(orig_key);
589 LY_CHECK_GOTO(ret, cleanup);
590 } else {
591 /* get all the attributes */
592 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
593
594 /* add into diff if there are any changes */
595 if (!ret) {
596 if (op == LYD_DIFF_OP_DELETE) {
597 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
598 } else {
599 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
600 }
601
602 free(orig_value);
603 LY_CHECK_GOTO(ret, cleanup);
604 } else if (ret == LY_ENOT) {
605 ret = LY_SUCCESS;
606 } else {
607 goto cleanup;
608 }
609 }
610
611 /* check descendants, if any, recursively */
612 if (match_second) {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200613 LY_CHECK_GOTO(lyd_diff_siblings_r(LYD_CHILD(iter_first), LYD_CHILD(match_second), options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200614 }
615
616 if (nosiblings) {
617 break;
618 }
619 }
620
621 /* reset all cached positions */
622 LY_ARRAY_FOR(userord, u) {
623 userord[u].pos = 0;
624 }
625
626 /* compare second tree to the first tree - create, user-ordered move */
627 LY_LIST_FOR(second, iter_second) {
628 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200629 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200630 /* skip default nodes */
631 continue;
632 }
633
634 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
635 lyd_find_sibling_first(first, iter_second, &match_first);
636 } else {
637 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
638 }
639
Michal Vasko3a41dff2020-07-15 14:30:28 +0200640 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200641 /* ignore default nodes */
642 match_first = NULL;
643 }
644
645 if (lysc_is_userordered(iter_second->schema)) {
646 /* get all the attributes */
647 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
648 &value, &orig_value, &key, &orig_key);
649
650 /* add into diff if there are any changes */
651 if (!ret) {
652 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
653
654 free(orig_value);
655 free(key);
656 free(value);
657 free(orig_key);
658 LY_CHECK_GOTO(ret, cleanup);
659 } else if (ret == LY_ENOT) {
660 ret = LY_SUCCESS;
661 } else {
662 goto cleanup;
663 }
664 } else if (!match_first) {
665 /* get all the attributes */
666 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
667
668 /* there must be changes, it is created */
669 assert(op == LYD_DIFF_OP_CREATE);
670 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
671
672 free(orig_value);
673 LY_CHECK_GOTO(ret, cleanup);
674 } /* else was handled */
675
676 if (nosiblings) {
677 break;
678 }
679 }
680
681cleanup:
682 LY_ARRAY_FOR(userord, u) {
683 LY_ARRAY_FREE(userord[u].inst);
684 }
685 LY_ARRAY_FREE(userord);
686 return ret;
687}
688
Michal Vasko3a41dff2020-07-15 14:30:28 +0200689static LY_ERR
690lyd_diff(const struct lyd_node *first, const struct lyd_node *second, int options, int nosiblings, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200691{
692 const struct ly_ctx *ctx;
693
694 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
695
696 if (first) {
697 ctx = LYD_NODE_CTX(first);
698 } else if (second) {
699 ctx = LYD_NODE_CTX(second);
700 } else {
701 ctx = NULL;
702 }
703
704 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
705 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
706 return LY_EINVAL;
707 }
708
709 *diff = NULL;
710
Michal Vasko3a41dff2020-07-15 14:30:28 +0200711 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
712}
713
714API LY_ERR
715lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
716{
717 return lyd_diff(first, second, options, 1, diff);
718}
719
720API LY_ERR
721lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
722{
723 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200724}
725
726/**
727 * @brief Find a matching node in data tree for a diff node.
728 *
729 * @param[in] first_node First sibling in the data tree.
730 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200731 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200732 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200733static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200734lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
735{
736 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
737 /* try to find the exact instance */
738 lyd_find_sibling_first(first_node, diff_node, match_p);
739 } else {
740 /* try to simply find the node, there cannot be more instances */
741 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
742 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200743}
744
745/**
746 * @brief Learn operation of a diff node.
747 *
748 * @param[in] diff_node Diff node.
749 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200750 * @return LY_ERR value.
751 */
752static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200753lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200754{
755 struct lyd_meta *meta = NULL;
756 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200757 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200758 int dynamic;
759
760 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
761 LY_LIST_FOR(diff_parent->meta, meta) {
762 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
763 str = lyd_meta2str(meta, &dynamic);
764 assert(!dynamic);
765 if ((str[0] == 'r') && (diff_parent != diff_node)) {
766 /* we do not care about this operation if it's in our parent */
767 continue;
768 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200769 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200770 break;
771 }
772 }
773 if (meta) {
774 break;
775 }
776 }
777 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
778
Michal Vaskod59035b2020-07-08 12:00:06 +0200779 return LY_SUCCESS;
780}
781
782/**
783 * @brief Insert a diff node into a data tree.
784 *
785 * @param[in,out] first_node First sibling of the data tree.
786 * @param[in] parent_node Data tree sibling parent node.
787 * @param[in] new_node Node to insert.
788 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
789 * instance will be inserted at the first position.
790 * @return err_info, NULL on success.
791 */
792static LY_ERR
793lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
794 const char *key_or_value)
795{
796 LY_ERR ret;
797 struct lyd_node *anchor;
798
799 assert(new_node);
800
801 if (!*first_node) {
802 if (!parent_node) {
803 /* no parent or siblings */
804 *first_node = new_node;
805 return LY_SUCCESS;
806 }
807
808 /* simply insert into parent, no other children */
809 if (key_or_value) {
810 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
811 new_node->schema->name);
812 return LY_EINVAL;
813 }
814 return lyd_insert(parent_node, new_node);
815 }
816
817 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
818
819 /* simple insert */
820 if (!lysc_is_userordered(new_node->schema)) {
821 /* insert at the end */
822 return lyd_insert_sibling(*first_node, new_node);
823 }
824
825 if (key_or_value) {
826 /* find the anchor sibling */
827 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
828 if (ret == LY_ENOTFOUND) {
829 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
830 new_node->schema->name);
831 return LY_EINVAL;
832 } else if (ret) {
833 return ret;
834 }
835
836 /* insert after */
837 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
838 assert(new_node->prev == anchor);
839 if (*first_node == new_node) {
840 *first_node = anchor;
841 }
842 } else {
843 if ((*first_node)->schema->flags & LYS_KEY) {
844 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
845
846 /* find last key */
847 anchor = *first_node;
848 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
849 anchor = anchor->next;
850 }
851 /* insert after the last key */
852 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
853 } else {
854 /* insert at the beginning */
855 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
856 *first_node = new_node;
857 }
858 }
859
860 return LY_SUCCESS;
861}
862
863/**
864 * @brief Apply diff subtree on data tree nodes, recursively.
865 *
866 * @param[in,out] first_node First sibling of the data tree.
867 * @param[in] parent_node Parent of the first sibling.
868 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200869 * @param[in] diff_cb Optional diff callback.
870 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200871 * @return LY_ERR value.
872 */
873static LY_ERR
874lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
875 lyd_diff_cb diff_cb, void *cb_data)
876{
877 LY_ERR ret;
878 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200879 const char *str_val;
880 enum lyd_diff_op op;
881 struct lyd_meta *meta;
Michal Vaskod59035b2020-07-08 12:00:06 +0200882 int dynamic;
883 const struct ly_ctx *ctx = LYD_NODE_CTX(diff_node);
884
885 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200886 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200887
Michal Vaskoe6323f62020-07-09 15:49:02 +0200888 /* handle specific user-ordered (leaf-)lists operations separately */
889 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
890 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200891 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200892 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200893 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200894 /* duplicate the node */
895 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200896 }
897
Michal Vaskoe6323f62020-07-09 15:49:02 +0200898 /* get "key" or "value" metadata string value */
899 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
900 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
901 str_val = lyd_meta2str(meta, &dynamic);
902
Michal Vaskod59035b2020-07-08 12:00:06 +0200903 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200904 if (str_val[0]) {
905 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200906 } else {
907 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
908 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200909 if (dynamic) {
910 free((char *)str_val);
911 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200912 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200913 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200914 lyd_free_tree(match);
915 }
916 return ret;
917 }
918
919 goto next_iter_r;
920 }
921
922 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200923 switch (op) {
924 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200925 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200926 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200927
928 if (match->schema->nodetype & LYD_NODE_TERM) {
929 /* special case of only dflt flag change */
930 if (diff_node->flags & LYD_DEFAULT) {
931 match->flags |= LYD_DEFAULT;
932 } else {
933 match->flags &= ~LYD_DEFAULT;
934 }
935 } else {
936 /* none operation on nodes without children is redundant and hence forbidden */
937 if (!LYD_CHILD(diff_node)) {
938 LOGINT_RET(ctx);
939 }
940 }
941 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200942 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200943 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200944 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200945
946 /* insert it at the end */
947 ret = 0;
948 if (*first_node) {
949 ret = lyd_insert_after((*first_node)->prev, match);
950 } else if (parent_node) {
951 ret = lyd_insert(parent_node, match);
952 } else {
953 *first_node = match;
954 }
955 if (ret) {
956 lyd_free_tree(match);
957 return ret;
958 }
959
960 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200961 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200962 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200963 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200964
965 /* remove it */
966 if ((match == *first_node) && !match->parent) {
967 assert(!parent_node);
968 /* we have removed the top-level node */
969 *first_node = (*first_node)->next;
970 }
971 lyd_free_tree(match);
972
973 /* we are not going recursively in this case, the whole subtree was already deleted */
974 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200975 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200976 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
977
978 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200979 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200980
981 /* update its value */
982 str_val = lyd_value2str((struct lyd_node_term *)diff_node, &dynamic);
983 ret = lyd_change_term(match, str_val);
984 if (dynamic) {
985 free((char *)str_val);
986 }
987 if (ret && (ret != LY_EEXIST)) {
988 LOGINT_RET(ctx);
989 }
990
991 /* with flags */
992 match->flags = diff_node->flags;
993 break;
994 default:
995 LOGINT_RET(ctx);
996 }
997
998next_iter_r:
999 if (diff_cb) {
1000 /* call callback */
1001 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1002 }
1003
1004 /* apply diff recursively */
1005 LY_LIST_FOR(LYD_CHILD(diff_node), diff_child) {
1006 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
1007 }
1008
1009 return LY_SUCCESS;
1010}
1011
1012API LY_ERR
1013lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
1014 lyd_diff_cb diff_cb, void *cb_data)
1015{
1016 const struct lyd_node *root;
1017
1018 LY_LIST_FOR(diff, root) {
1019 if (mod && (lyd_owner_module(root) != mod)) {
1020 /* skip data nodes from different modules */
1021 continue;
1022 }
1023
1024 /* apply relevant nodes from the diff datatree */
1025 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1026 }
1027
1028 return LY_SUCCESS;
1029}
1030
1031API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001032lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001033{
1034 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1035}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001036
1037/**
1038 * @brief Update operations on a diff node when the new operation is NONE.
1039 *
1040 * @param[in] diff_match Node from the diff.
1041 * @param[in] cur_op Current operation of the diff node.
1042 * @param[in] src_diff Current source diff node.
1043 * @return LY_ERR value.
1044 */
1045static LY_ERR
1046lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1047{
1048 switch (cur_op) {
1049 case LYD_DIFF_OP_NONE:
1050 case LYD_DIFF_OP_CREATE:
1051 case LYD_DIFF_OP_REPLACE:
1052 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1053 /* NONE on a term means only its dflt flag was changed */
1054 diff_match->flags &= ~LYD_DEFAULT;
1055 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1056 }
1057 break;
1058 default:
1059 /* delete operation is not valid */
1060 LOGINT_RET(LYD_NODE_CTX(src_diff));
1061 }
1062
1063 return LY_SUCCESS;
1064}
1065
1066/**
1067 * @brief Remove an attribute from a node.
1068 *
1069 * @param[in] node Node with the metadata.
1070 * @param[in] name Metadata name.
1071 */
1072static void
1073lyd_diff_del_meta(struct lyd_node *node, const char *name)
1074{
1075 struct lyd_meta *meta;
1076
1077 LY_LIST_FOR(node->meta, meta) {
1078 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001079 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001080 return;
1081 }
1082 }
1083
1084 assert(0);
1085}
1086
1087/**
1088 * @brief Set a specific operation of a node. Delete the previous operation, if any.
1089 *
1090 * @param[in] node Node to change.
1091 * @param[in] op Operation to set.
1092 * @return LY_ERR value.
1093 */
1094static LY_ERR
1095lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1096{
1097 struct lyd_meta *meta;
1098
1099 LY_LIST_FOR(node->meta, meta) {
1100 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001101 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001102 break;
1103 }
1104 }
1105
Michal Vasko3a41dff2020-07-15 14:30:28 +02001106 return lyd_new_meta(node, NULL, "yang:operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001107}
1108
1109/**
1110 * @brief Update operations on a diff node when the new operation is REPLACE.
1111 *
1112 * @param[in] diff_match Node from the diff.
1113 * @param[in] cur_op Current operation of the diff node.
1114 * @param[in] src_diff Current source diff node.
1115 * @return LY_ERR value.
1116 */
1117static LY_ERR
1118lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1119{
1120 LY_ERR ret;
1121 int dynamic;
1122 const char *str_val, *meta_name;
1123 struct lyd_meta *meta;
1124 const struct lys_module *mod;
1125 const struct lyd_node_any *any;
1126
1127 /* get "yang" module for the metadata */
1128 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff_match), "yang");
1129 assert(mod);
1130
1131 switch (cur_op) {
1132 case LYD_DIFF_OP_REPLACE:
1133 case LYD_DIFF_OP_CREATE:
1134 switch (diff_match->schema->nodetype) {
1135 case LYS_LIST:
1136 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001137 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001138 * keep orig_key/orig_value (only replace oper) and replace key/value */
1139 assert(lysc_is_userordered(diff_match->schema));
1140 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1141
1142 lyd_diff_del_meta(diff_match, meta_name);
1143 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
1144 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001145 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001146 break;
1147 case LYS_LEAF:
1148 /* replaced with the exact same value, impossible */
1149 if (!lyd_compare(diff_match, src_diff, 0)) {
1150 LOGINT_RET(LYD_NODE_CTX(src_diff));
1151 }
1152
1153 /* get leaf value */
1154 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1155
1156 /* modify the node value */
1157 ret = lyd_change_term(diff_match, str_val);
1158 if (dynamic) {
1159 free((char *)str_val);
1160 }
1161 if (ret) {
1162 LOGINT_RET(LYD_NODE_CTX(src_diff));
1163 }
1164
1165 /* compare values whether there is any change at all */
1166 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1167 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_match)), LY_EINT);
1168 str_val = lyd_meta2str(meta, &dynamic);
1169 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val), lydjson_resolve_prefix,
1170 NULL, LYD_JSON, NULL);
1171 if (dynamic) {
1172 free((char *)str_val);
1173 }
1174 if (!ret) {
1175 /* values are the same, remove orig-value meta and set oper to NONE */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001176 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001177 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1178 }
1179
1180 /* modify the default flag */
1181 diff_match->flags &= ~LYD_DEFAULT;
1182 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1183 break;
1184 case LYS_ANYXML:
1185 case LYS_ANYDATA:
1186 if (!lyd_compare(diff_match, src_diff, 0)) {
1187 /* replaced with the exact same value, impossible */
1188 LOGINT_RET(LYD_NODE_CTX(src_diff));
1189 }
1190
1191 /* modify the node value */
1192 any = (struct lyd_node_any *)src_diff;
1193 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1194 break;
1195 default:
1196 LOGINT_RET(LYD_NODE_CTX(src_diff));
1197 }
1198 break;
1199 case LYD_DIFF_OP_NONE:
1200 /* it is moved now */
1201 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1202
1203 /* change the operation */
1204 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1205
1206 /* set orig-key and key metadata */
1207 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
1208 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001209 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001210
1211 meta = lyd_find_meta(src_diff->meta, mod, "key");
1212 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001213 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001214 break;
1215 default:
1216 /* delete operation is not valid */
1217 LOGINT_RET(LYD_NODE_CTX(src_diff));
1218 }
1219
1220 return LY_SUCCESS;
1221}
1222
1223/**
1224 * @brief Update operations in a diff node when the new operation is CREATE.
1225 *
1226 * @param[in] diff_match Node from the diff.
1227 * @param[in] cur_op Current operation of the diff node.
1228 * @param[in] src_diff Current source diff node.
1229 * @return LY_ERR value.
1230 */
1231static LY_ERR
1232lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1233{
1234 struct lyd_node *child;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001235 const char *str_val;
1236 int dynamic;
1237 LY_ERR ret;
1238
1239 switch (cur_op) {
1240 case LYD_DIFF_OP_DELETE:
1241 /* delete operation, if any */
1242 if (!lyd_compare(diff_match, src_diff, 0)) {
1243 /* deleted + created -> operation NONE */
1244 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1245
1246 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1247 /* add orig-dflt metadata */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001248 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1249 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001250 }
1251 } else {
1252 assert(diff_match->schema->nodetype == LYS_LEAF);
1253 /* we deleted it, but it was created with a different value -> operation REPLACE */
1254 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1255
1256 /* current value is the previous one (meta) */
1257 str_val = lyd_value2str((struct lyd_node_term *)diff_match, &dynamic);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001258 ret = lyd_new_meta(diff_match, NULL, "yang:orig-value", str_val, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001259 if (dynamic) {
1260 free((char *)str_val);
1261 }
Michal Vasko3a41dff2020-07-15 14:30:28 +02001262 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001263
1264 /* update the value itself */
1265 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1266 ret = lyd_change_term(diff_match, str_val);
1267 if (dynamic) {
1268 free((char *)str_val);
1269 }
1270 LY_CHECK_RET(ret);
1271 }
1272
1273 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1274 /* update dflt flag itself */
1275 diff_match->flags &= ~LYD_DEFAULT;
1276 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1277 } else {
1278 /* but the operation of its children should remain DELETE */
1279 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1280 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1281 }
1282 }
1283 break;
1284 default:
1285 /* create and replace operations are not valid */
1286 LOGINT_RET(LYD_NODE_CTX(src_diff));
1287 }
1288
1289 return LY_SUCCESS;
1290}
1291
1292/**
1293 * @brief Update operations on a diff node when the new operation is DELETE.
1294 *
1295 * @param[in] diff_match Node from the diff.
1296 * @param[in] cur_op Current operation of the diff node.
1297 * @param[in] src_diff Current source diff node.
1298 * @return LY_ERR value.
1299 */
1300static LY_ERR
1301lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1302{
1303 struct lyd_node *next, *child;
1304
1305 /* we can delete only exact existing nodes */
1306 LY_CHECK_ERR_RET(lyd_compare(diff_match, src_diff, 0), LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
1307
1308 switch (cur_op) {
1309 case LYD_DIFF_OP_CREATE:
1310 /* it was created, but then deleted -> set NONE operation */
1311 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1312
1313 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1314 /* add orig-default meta because it is expected */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001315 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1316 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001317 } else {
1318 /* keep operation for all descendants (for now) */
1319 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1320 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1321 }
1322 }
1323 break;
1324 case LYD_DIFF_OP_REPLACE:
1325 /* similar to none operation but also remove the redundant attribute */
1326 lyd_diff_del_meta(diff_match, "orig-value");
1327 /* fallthrough */
1328 case LYD_DIFF_OP_NONE:
1329 /* it was not modified, but should be deleted -> set DELETE operation */
1330 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1331
1332 /* all descendants will be deleted even without being in the diff, so remove them */
1333 LY_LIST_FOR_SAFE(LYD_CHILD(diff_match), next, child) {
1334 lyd_free_tree(child);
1335 }
1336 break;
1337 default:
1338 /* delete operation is not valid */
1339 LOGINT_RET(LYD_NODE_CTX(src_diff));
1340 }
1341
1342 return LY_SUCCESS;
1343}
1344
1345/**
1346 * @brief Check whether this diff node is redundant (does not change data).
1347 *
1348 * @param[in] diff Diff node.
1349 * @return 0 if not, non-zero if it is.
1350 */
1351static int
1352lyd_diff_is_redundant(struct lyd_node *diff)
1353{
1354 enum lyd_diff_op op;
1355 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1356 struct lyd_node *child;
1357 const struct lys_module *mod;
1358 const char *str;
1359 int dynamic;
1360
1361 assert(diff);
1362
1363 child = LYD_CHILD(diff);
1364 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff), "yang");
1365 assert(mod);
1366
1367 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001368 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001369
1370 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1371 /* check for redundant move */
1372 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1373 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1374 assert(orig_val_meta && val_meta);
1375
1376 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1377 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001378 lyd_free_meta_single(orig_val_meta);
1379 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001380 if (child) {
1381 /* change operation to NONE, we have siblings */
1382 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1383 return 0;
1384 }
1385
1386 /* redundant node, BUT !!
1387 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1388 * because the data that this is applied on should not change for the diff lifetime.
1389 * However, when we are merging 2 diffs, this conversion is actually lossy because
1390 * if the data change, the move operation can also change its meaning. In this specific
1391 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1392 */
1393 return 1;
1394 }
1395 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1396 /* check whether at least the default flags are different */
1397 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1398 assert(meta);
1399 str = lyd_meta2str(meta, &dynamic);
1400 assert(!dynamic);
1401
1402 /* if previous and current dflt flags are the same, this node is redundant */
1403 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1404 return 1;
1405 }
1406 return 0;
1407 }
1408
1409 if (!child && (op == LYD_DIFF_OP_NONE)) {
1410 return 1;
1411 }
1412
1413 return 0;
1414}
1415
1416/**
1417 * @brief Merge sysrepo diff with another diff, recursively.
1418 *
1419 * @param[in] src_diff Source diff node.
1420 * @param[in] diff_parent Current sysrepo diff parent.
1421 * @param[in] diff_cb Optional diff callback.
1422 * @param[in] cb_data User data for @p diff_cb.
1423 * @param[in,out] diff Diff root node.
1424 * @return LY_ERR value.
1425 */
1426static LY_ERR
1427lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
1428 struct lyd_node **diff)
1429{
1430 LY_ERR ret = LY_SUCCESS;
1431 struct lyd_node *child, *diff_node = NULL;
1432 enum lyd_diff_op src_op, cur_op;
1433
1434 /* get source node operation */
1435 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1436
1437 /* find an equal node in the current diff */
1438 lyd_diff_find_node(diff_parent ? LYD_CHILD(diff_parent) : *diff, src_diff, &diff_node);
1439
1440 if (diff_node) {
1441 /* get target (current) operation */
1442 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1443
1444 /* merge operations */
1445 switch (src_op) {
1446 case LYD_DIFF_OP_REPLACE:
1447 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1448 break;
1449 case LYD_DIFF_OP_CREATE:
1450 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff);
1451 break;
1452 case LYD_DIFF_OP_DELETE:
1453 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1454 break;
1455 case LYD_DIFF_OP_NONE:
1456 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1457 break;
1458 default:
1459 LOGINT_RET(LYD_NODE_CTX(src_diff));
1460 }
1461 if (ret) {
1462 LOGERR(LYD_NODE_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
1463 return ret;
1464 }
1465
1466 if (diff_cb) {
1467 /* call callback */
1468 LY_CHECK_RET(diff_cb(diff_node, NULL, cb_data));
1469 }
1470
1471 /* update diff parent */
1472 diff_parent = diff_node;
1473
1474 /* merge src_diff recursively */
1475 LY_LIST_FOR(LYD_CHILD(src_diff), child) {
1476 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, diff));
1477 }
1478 } else {
1479 /* add new diff node with all descendants */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001480 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE, &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001481
1482 /* insert node into diff if not already */
1483 if (!diff_parent) {
1484 if (*diff) {
1485 lyd_insert_sibling(*diff, diff_node);
1486 } else {
1487 *diff = diff_node;
1488 }
1489 }
1490
1491 /* update operation */
1492 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1493
1494 if (diff_cb) {
1495 /* call callback */
1496 LY_CHECK_RET(diff_cb(diff_node, NULL, cb_data));
1497 }
1498
1499 /* update diff parent */
1500 diff_parent = diff_node;
1501 }
1502
1503 /* remove any redundant nodes */
1504 if (diff_parent && lyd_diff_is_redundant(diff_parent)) {
1505 if (diff_parent == *diff) {
1506 *diff = (*diff)->next;
1507 }
1508 lyd_free_tree(diff_parent);
1509 }
1510
1511 return LY_SUCCESS;
1512}
1513
1514API LY_ERR
1515lyd_diff_merge_module(const struct lyd_node *src_diff, const struct lys_module *mod, lyd_diff_cb diff_cb, void *cb_data,
1516 struct lyd_node **diff)
1517{
1518 const struct lyd_node *src_root;
1519
1520 LY_LIST_FOR(src_diff, src_root) {
1521 if (mod && (lyd_owner_module(src_root) != mod)) {
1522 /* skip data nodes from different modules */
1523 continue;
1524 }
1525
1526 /* apply relevant nodes from the diff datatree */
1527 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, diff));
1528 }
1529
1530 return LY_SUCCESS;
1531}
1532
1533API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001534lyd_diff_merge_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001535{
1536 return lyd_diff_merge_module(src_diff, NULL, NULL, NULL, diff);
1537}
Michal Vasko4231fb62020-07-13 13:54:47 +02001538
1539static LY_ERR
1540lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
1541{
1542 LY_ERR ret = LY_SUCCESS;
1543 struct lyd_meta *meta;
1544 const char *val1 = NULL, *val2 = NULL;
1545 int dyn1 = 0, dyn2 = 0, flags;
1546
1547 meta = lyd_find_meta(leaf->meta, mod, "orig-value");
1548 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(leaf)), LY_EINT);
1549
1550 /* orig-value */
1551 val1 = lyd_meta2str(meta, &dyn1);
1552
1553 /* current value */
1554 val2 = lyd_value2str((struct lyd_node_term *)leaf, &dyn2);
1555
1556 /* switch values, keep default flag */
1557 flags = leaf->flags;
1558 LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
1559 leaf->flags = flags;
1560 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1561
1562cleanup:
1563 if (dyn1) {
1564 free((char *)val1);
1565 }
1566 if (dyn2) {
1567 free((char *)val2);
1568 }
1569 return ret;
1570}
1571
1572static LY_ERR
1573lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1574{
1575 struct lyd_meta *meta;
1576 const char *val;
1577 int dyn, flag1, flag2;
1578
1579 meta = lyd_find_meta(node->meta, mod, "orig-default");
1580 if (!meta) {
1581 /* default flag did not change */
1582 return LY_SUCCESS;
1583 }
1584
1585 /* orig-default */
1586 val = lyd_meta2str(meta, &dyn);
1587 assert(!dyn);
1588 if (!strcmp(val, "true")) {
1589 flag1 = LYD_DEFAULT;
1590 } else {
1591 flag1 = 0;
1592 }
1593
1594 /* current default */
1595 flag2 = node->flags & LYD_DEFAULT;
1596
1597 /* switch defaults */
1598 node->flags &= ~LYD_DEFAULT;
1599 node->flags |= flag1;
1600 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1601
1602 return LY_SUCCESS;
1603}
1604
1605static LY_ERR
1606lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1607{
1608 LY_ERR ret = LY_SUCCESS;
1609 struct lyd_meta *meta1, *meta2;
1610 const char *val1 = NULL, *val2 = NULL;
1611 int dyn1 = 0, dyn2 = 0;
1612
1613 meta1 = lyd_find_meta(node->meta, mod, name1);
1614 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1615
1616 meta2 = lyd_find_meta(node->meta, mod, name2);
1617 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1618
1619 /* value1 */
1620 val1 = lyd_meta2str(meta1, &dyn1);
1621
1622 /* value2 */
1623 val2 = lyd_meta2str(meta2, &dyn2);
1624
1625 /* switch values */
1626 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1627 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1628
1629cleanup:
1630 if (dyn1) {
1631 free((char *)val1);
1632 }
1633 if (dyn2) {
1634 free((char *)val2);
1635 }
1636 return ret;
1637}
1638
1639API LY_ERR
1640lyd_diff_reverse(const struct lyd_node *src_diff, struct lyd_node **diff)
1641{
1642 LY_ERR ret = LY_SUCCESS;
1643 const struct lys_module *mod;
1644 struct lyd_node *root, *next, *elem;
1645 enum lyd_diff_op op;
1646
1647 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1648
1649 if (!src_diff) {
1650 *diff = NULL;
1651 return LY_SUCCESS;
1652 }
1653
1654 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001655 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001656
1657 /* find module with metadata needed for later */
1658 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(src_diff), "yang");
1659 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_NODE_CTX(src_diff)); ret = LY_EINT, cleanup);
1660
1661 LY_LIST_FOR(*diff, root) {
1662 LYD_TREE_DFS_BEGIN(root, next, elem) {
1663 /* find operation attribute, if any */
1664 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
1665
1666 switch (op) {
1667 case LYD_DIFF_OP_CREATE:
1668 /* reverse create to delete */
1669 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
1670 break;
1671 case LYD_DIFF_OP_DELETE:
1672 /* reverse delete to create */
1673 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
1674 break;
1675 case LYD_DIFF_OP_REPLACE:
1676 switch (elem->schema->nodetype) {
1677 case LYS_LEAF:
1678 /* leaf value change */
1679 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1680 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1681 break;
1682 case LYS_LEAFLIST:
1683 /* leaf-list move */
1684 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1685 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1686 break;
1687 case LYS_LIST:
1688 /* list move */
1689 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1690 break;
1691 default:
1692 LOGINT(LYD_NODE_CTX(src_diff));
1693 ret = LY_EINT;
1694 goto cleanup;
1695 }
1696 break;
1697 case LYD_DIFF_OP_NONE:
1698 switch (elem->schema->nodetype) {
1699 case LYS_LEAF:
1700 case LYS_LEAFLIST:
1701 /* default flag change */
1702 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1703 break;
1704 default:
1705 /* nothing to do */
1706 break;
1707 }
1708 break;
1709 default:
1710 /* nothing to do */
1711 break;
1712 }
1713
1714 LYD_TREE_DFS_END(root, next, elem);
1715 }
1716 }
1717
1718cleanup:
1719 if (ret) {
1720 lyd_free_siblings(*diff);
1721 *diff = NULL;
1722 }
1723 return ret;
1724}