blob: a7d1c25d2f73b32af7164f094e5d65461c7a0e58 [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 */
Michal Vaskob104f112020-07-17 09:54:54 +0200124 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200125 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200126 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200127 }
128
129 /* get module with the operation metadata */
130 yang_mod = LYD_NODE_CTX(node)->list.objs[1];
131 assert(!strcmp(yang_mod->name, "yang"));
132
133 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200134 if (diff_parent && (diff_parent != dup)) {
135 LY_CHECK_RET(lyd_new_meta(diff_parent, yang_mod, "operation", "none", NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200136 }
137
138 /* add subtree operation */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200139 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "operation", lyd_diff_op2str(op), NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200140
141 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200142 if (orig_default) {
143 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-default", orig_default, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200144 }
145
146 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200147 if (orig_value) {
148 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-value", orig_value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200149 }
150
151 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200152 if (key) {
153 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "key", key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200154 }
155
156 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200157 if (value) {
158 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "value", value, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200159 }
160
161 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200162 if (orig_key) {
163 LY_CHECK_RET(lyd_new_meta(dup, yang_mod, "orig-key", orig_key, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200164 }
165
166 return LY_SUCCESS;
167}
168
169/**
170 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
171 *
172 * @param[in] first
173 * @param[in] schema Schema node of the list/leaf-list.
174 * @param[in,out] userord Sized array of userord items.
175 * @return Userord item for all the user-ordered list/leaf-list instances.
176 */
177static struct lyd_diff_userord *
178lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
179{
180 struct lyd_diff_userord *item;
181 const struct lyd_node *iter, **node;
182 LY_ARRAY_COUNT_TYPE u;
183
184 LY_ARRAY_FOR(*userord, u) {
185 if ((*userord)[u].schema == schema) {
186 return &(*userord)[u];
187 }
188 }
189
190 /* it was not added yet, add it now */
191 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
192
193 item->schema = schema;
194 item->pos = 0;
195 item->inst = NULL;
196
197 /* store all the instance pointers in the current order */
198 if (first) {
199 if (first->parent) {
200 iter = first->parent->child;
201 } else {
202 for (iter = first; iter->prev->next; iter = iter->prev);
203 }
204 for (; iter; iter = iter->next) {
205 if (iter->schema == first->schema) {
206 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
207 *node = iter;
208 }
209 }
210 }
211
212 return item;
213}
214
215/**
216 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
217 * lists/leaf-lists.
218 *
219 * @param[in] first Node from the first tree, can be NULL (on create).
220 * @param[in] second Node from the second tree, can be NULL (on delete).
221 * @param[in] options Diff options.
222 * @param[in,out] userord Sized array of userord items for keeping the current node order.
223 * @param[out] op Operation.
224 * @param[out] orig_default Original default metadata.
225 * @param[out] value Value metadata.
226 * @param[out] orig_value Original value metadata
227 * @param[out] key Key metadata.
228 * @param[out] orig_key Original key metadata.
229 * @return LY_SUCCESS on success,
230 * @return LY_ENOT if there is no change to be added into diff,
231 * @return LY_ERR value on other errors.
232 */
233static LY_ERR
234lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, int options,
235 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
236 char **orig_value, char **key, char **orig_key)
237{
238 const struct lysc_node *schema;
239 int dynamic;
240 size_t buflen, bufused, first_pos, second_pos;
241 struct lyd_diff_userord *userord_item;
242
243 assert(first || second);
244
245 *orig_default = NULL;
246 *value = NULL;
247 *orig_value = NULL;
248 *key = NULL;
249 *orig_key = NULL;
250
251 schema = first ? first->schema : second->schema;
252 assert(lysc_is_userordered(schema));
253
254 /* get userord entry */
255 userord_item = lyd_diff_userord_get(first, schema, userord);
256 LY_CHECK_RET(!userord_item, LY_EMEM);
257
258 /* prepare position of the next instance */
259 second_pos = userord_item->pos++;
260
261 /* find user-ordered first position */
262 if (first) {
263 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
264 if (userord_item->inst[first_pos] == first) {
265 break;
266 }
267 }
268 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
269 } else {
270 first_pos = 0;
271 }
272
273 /* learn operation first */
274 if (!second) {
275 *op = LYD_DIFF_OP_DELETE;
276 } else if (!first) {
277 *op = LYD_DIFF_OP_CREATE;
278 } else {
279 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vasko8f359bf2020-07-28 10:41:15 +0200280 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200281 /* in first, there is a different instance on the second position, we are going to move 'first' node */
282 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200283 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200284 /* default flag change */
285 *op = LYD_DIFF_OP_NONE;
286 } else {
287 /* no changes */
288 return LY_ENOT;
289 }
290 }
291
292 /*
293 * set each attribute correctly based on the operation and node type
294 */
295
296 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200297 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype == LYS_LEAFLIST)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200298 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
299 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200300 if (first->flags & LYD_DEFAULT) {
301 *orig_default = "true";
302 } else {
303 *orig_default = "false";
304 }
305 }
306
307 /* value */
308 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
309 if (second_pos) {
310 *value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[second_pos - 1], &dynamic);
311 if (!dynamic) {
312 *value = strdup(*value);
313 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
314 }
315 } else {
316 *value = strdup("");
317 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
318 }
319 }
320
321 /* orig-value */
322 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
323 if (first_pos) {
324 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)userord_item->inst[first_pos - 1], &dynamic);
325 if (!dynamic) {
326 *orig_value = strdup(*orig_value);
327 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
328 }
329 } else {
330 *orig_value = strdup("");
331 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
332 }
333 }
334
335 /* key */
336 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op ==LYD_DIFF_OP_CREATE))) {
337 if (second_pos) {
338 buflen = bufused = 0;
339 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
340 } else {
341 *key = strdup("");
342 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
343 }
344 }
345
346 /* orig-key */
347 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
348 if (first_pos) {
349 buflen = bufused = 0;
350 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
351 } else {
352 *orig_key = strdup("");
353 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
354 }
355 }
356
357 /*
358 * update our instances - apply the change
359 */
360 if (*op == LYD_DIFF_OP_CREATE) {
361 /* insert the instance */
362 LY_ARRAY_RESIZE_ERR_RET(schema->module->ctx, userord_item->inst, LY_ARRAY_COUNT(userord_item->inst) + 1,
363 ;, LY_EMEM);
364 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
365 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
366 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
367 }
368 LY_ARRAY_INCREMENT(userord_item->inst);
369 userord_item->inst[second_pos] = second;
370
371 } else if (*op == LYD_DIFF_OP_DELETE) {
372 /* remove the instance */
373 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
374 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
375 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
376 }
377 LY_ARRAY_DECREMENT(userord_item->inst);
378
379 } else if (*op == LYD_DIFF_OP_REPLACE) {
380 /* move the instances */
381 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
382 (first_pos - second_pos) * sizeof *userord_item->inst);
383 userord_item->inst[second_pos] = first;
384 }
385
386 return LY_SUCCESS;
387}
388
389/**
390 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
391 * lists/leaf-lists.
392 *
393 * @param[in] first Node from the first tree, can be NULL (on create).
394 * @param[in] second Node from the second tree, can be NULL (on delete).
395 * @param[in] options Diff options.
396 * @param[out] op Operation.
397 * @param[out] orig_default Original default metadata.
398 * @param[out] orig_value Original value metadata.
399 * @return LY_SUCCESS on success,
400 * @return LY_ENOT if there is no change to be added into diff,
401 * @return LY_ERR value on other errors.
402 */
403static LY_ERR
404lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, int options, enum lyd_diff_op *op,
405 const char **orig_default, char **orig_value)
406{
407 const struct lysc_node *schema;
408 int dynamic;
409
410 assert(first || second);
411
412 *orig_default = NULL;
413 *orig_value = NULL;
414
415 schema = first ? first->schema : second->schema;
416 assert(!lysc_is_userordered(schema));
417
418 /* learn operation first */
419 if (!second) {
420 *op = LYD_DIFF_OP_DELETE;
421 } else if (!first) {
422 *op = LYD_DIFF_OP_CREATE;
423 } else {
424 switch (schema->nodetype) {
425 case LYS_CONTAINER:
426 case LYS_RPC:
427 case LYS_ACTION:
428 case LYS_NOTIF:
429 /* no changes */
430 return LY_ENOT;
431 case LYS_LIST:
432 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200433 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200434 /* default flag change */
435 *op = LYD_DIFF_OP_NONE;
436 } else {
437 /* no changes */
438 return LY_ENOT;
439 }
440 break;
441 case LYS_LEAF:
442 case LYS_ANYXML:
443 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200444 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200445 /* different values */
446 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200447 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200448 /* default flag change */
449 *op = LYD_DIFF_OP_NONE;
450 } else {
451 /* no changes */
452 return LY_ENOT;
453 }
454 break;
455 default:
456 LOGINT_RET(schema->module->ctx);
457 }
458 }
459
460 /*
461 * set each attribute correctly based on the operation and node type
462 */
463
464 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200465 if ((options & LYD_DIFF_DEFAULTS) && (schema->nodetype & LYD_NODE_TERM)
Michal Vaskoe6323f62020-07-09 15:49:02 +0200466 && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))
467 && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200468 if (first->flags & LYD_DEFAULT) {
469 *orig_default = "true";
470 } else {
471 *orig_default = "false";
472 }
473 }
474
475 /* orig-value */
476 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
477 /* leaf */
478 *orig_value = (char *)lyd_value2str((struct lyd_node_term *)first, &dynamic);
479 if (!dynamic) {
480 *orig_value = strdup(*orig_value);
481 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
482 }
483 }
484
485 return LY_SUCCESS;
486}
487
488/**
489 * @brief Perform diff for all siblings at certain depth, recursively.
490 *
491 * For user-ordered lists/leaf-lists a specific structure is used for storing
492 * the current order. The idea is to apply all the generated diff changes
493 * virtually on the first tree so that we can continue to generate correct
494 * changes after some were already generated.
495 *
496 * The algorithm then uses second tree position-based changes with a before
497 * (preceding) item anchor.
498 *
499 * Example:
500 *
501 * Virtual first tree leaf-list order:
502 * 1 2 [3] 4 5
503 *
504 * Second tree leaf-list order:
505 * 1 2 [5] 3 4
506 *
507 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
508 * match - they do not - move nodes so that the 3rd position node is final ->
509 * -> move node 5 to the 3rd position -> move node 5 after node 2.
510 *
511 * Required properties:
512 * Stored operations (move) should not be affected by later operations -
513 * - would cause a redundantly long list of operations, possibly inifinite.
514 *
515 * Implemenation justification:
516 * First, all delete operations and only then move/create operations are stored.
517 * Also, preceding anchor is used and after each iteration another node is
518 * at its final position. That results in the invariant that all preceding
519 * nodes are final and will not be changed by the later operations, meaning
520 * they can safely be used as anchors for the later operations.
521 *
522 * @param[in] first First tree first sibling.
523 * @param[in] second Second tree first sibling.
524 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200525 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200526 * @param[in,out] diff Diff to append to.
527 * @return LY_ERR value.
528 */
529static LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +0200530lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, int options, int nosiblings,
531 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200532{
533 LY_ERR ret = LY_SUCCESS;
534 const struct lyd_node *iter_first, *iter_second;
535 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200536 struct lyd_diff_userord *userord = NULL;
537 LY_ARRAY_COUNT_TYPE u;
538 enum lyd_diff_op op;
539 const char *orig_default;
540 char *orig_value, *key, *value, *orig_key;
541
Michal Vaskod59035b2020-07-08 12:00:06 +0200542 /* compare first tree to the second tree - delete, replace, none */
543 LY_LIST_FOR(first, iter_first) {
544 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200545 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200546 /* skip default nodes */
547 continue;
548 }
549
550 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
551 /* try to find the exact instance */
552 lyd_find_sibling_first(second, iter_first, &match_second);
553 } else {
554 /* try to simply find the node, there cannot be more instances */
555 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
556 }
557
Michal Vasko3a41dff2020-07-15 14:30:28 +0200558 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200559 /* ignore default nodes */
560 match_second = NULL;
561 }
562
563 if (lysc_is_userordered(iter_first->schema)) {
564 if (match_second) {
565 /* we are handling only user-ordered node delete now */
566 continue;
567 }
568
569 /* get all the attributes */
570 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
571 &value, &orig_value, &key, &orig_key), cleanup);
572
573 /* there must be changes, it is deleted */
574 assert(op == LYD_DIFF_OP_DELETE);
575 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
576
577 free(orig_value);
578 free(key);
579 free(value);
580 free(orig_key);
581 LY_CHECK_GOTO(ret, cleanup);
582 } else {
583 /* get all the attributes */
584 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
585
586 /* add into diff if there are any changes */
587 if (!ret) {
588 if (op == LYD_DIFF_OP_DELETE) {
589 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
590 } else {
591 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
592 }
593
594 free(orig_value);
595 LY_CHECK_GOTO(ret, cleanup);
596 } else if (ret == LY_ENOT) {
597 ret = LY_SUCCESS;
598 } else {
599 goto cleanup;
600 }
601 }
602
603 /* check descendants, if any, recursively */
604 if (match_second) {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200605 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 +0200606 }
607
608 if (nosiblings) {
609 break;
610 }
611 }
612
613 /* reset all cached positions */
614 LY_ARRAY_FOR(userord, u) {
615 userord[u].pos = 0;
616 }
617
618 /* compare second tree to the first tree - create, user-ordered move */
619 LY_LIST_FOR(second, iter_second) {
620 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200621 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200622 /* skip default nodes */
623 continue;
624 }
625
626 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
627 lyd_find_sibling_first(first, iter_second, &match_first);
628 } else {
629 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
630 }
631
Michal Vasko3a41dff2020-07-15 14:30:28 +0200632 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200633 /* ignore default nodes */
634 match_first = NULL;
635 }
636
637 if (lysc_is_userordered(iter_second->schema)) {
638 /* get all the attributes */
639 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
640 &value, &orig_value, &key, &orig_key);
641
642 /* add into diff if there are any changes */
643 if (!ret) {
644 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
645
646 free(orig_value);
647 free(key);
648 free(value);
649 free(orig_key);
650 LY_CHECK_GOTO(ret, cleanup);
651 } else if (ret == LY_ENOT) {
652 ret = LY_SUCCESS;
653 } else {
654 goto cleanup;
655 }
656 } else if (!match_first) {
657 /* get all the attributes */
658 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
659
660 /* there must be changes, it is created */
661 assert(op == LYD_DIFF_OP_CREATE);
662 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
663
664 free(orig_value);
665 LY_CHECK_GOTO(ret, cleanup);
666 } /* else was handled */
667
668 if (nosiblings) {
669 break;
670 }
671 }
672
673cleanup:
674 LY_ARRAY_FOR(userord, u) {
675 LY_ARRAY_FREE(userord[u].inst);
676 }
677 LY_ARRAY_FREE(userord);
678 return ret;
679}
680
Michal Vasko3a41dff2020-07-15 14:30:28 +0200681static LY_ERR
682lyd_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 +0200683{
684 const struct ly_ctx *ctx;
685
686 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
687
688 if (first) {
689 ctx = LYD_NODE_CTX(first);
690 } else if (second) {
691 ctx = LYD_NODE_CTX(second);
692 } else {
693 ctx = NULL;
694 }
695
696 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
697 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
698 return LY_EINVAL;
699 }
700
701 *diff = NULL;
702
Michal Vasko3a41dff2020-07-15 14:30:28 +0200703 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
704}
705
706API LY_ERR
707lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
708{
709 return lyd_diff(first, second, options, 1, diff);
710}
711
712API LY_ERR
713lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
714{
715 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200716}
717
718/**
719 * @brief Find a matching node in data tree for a diff node.
720 *
721 * @param[in] first_node First sibling in the data tree.
722 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200723 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200724 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200725static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200726lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
727{
728 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
729 /* try to find the exact instance */
730 lyd_find_sibling_first(first_node, diff_node, match_p);
731 } else {
732 /* try to simply find the node, there cannot be more instances */
733 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
734 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200735}
736
737/**
738 * @brief Learn operation of a diff node.
739 *
740 * @param[in] diff_node Diff node.
741 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200742 * @return LY_ERR value.
743 */
744static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200745lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200746{
747 struct lyd_meta *meta = NULL;
748 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200749 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200750 int dynamic;
751
752 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
753 LY_LIST_FOR(diff_parent->meta, meta) {
754 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
755 str = lyd_meta2str(meta, &dynamic);
756 assert(!dynamic);
757 if ((str[0] == 'r') && (diff_parent != diff_node)) {
758 /* we do not care about this operation if it's in our parent */
759 continue;
760 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200761 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200762 break;
763 }
764 }
765 if (meta) {
766 break;
767 }
768 }
769 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
770
Michal Vaskod59035b2020-07-08 12:00:06 +0200771 return LY_SUCCESS;
772}
773
774/**
775 * @brief Insert a diff node into a data tree.
776 *
777 * @param[in,out] first_node First sibling of the data tree.
778 * @param[in] parent_node Data tree sibling parent node.
779 * @param[in] new_node Node to insert.
780 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
781 * instance will be inserted at the first position.
782 * @return err_info, NULL on success.
783 */
784static LY_ERR
785lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
786 const char *key_or_value)
787{
788 LY_ERR ret;
789 struct lyd_node *anchor;
790
791 assert(new_node);
792
793 if (!*first_node) {
794 if (!parent_node) {
795 /* no parent or siblings */
796 *first_node = new_node;
797 return LY_SUCCESS;
798 }
799
800 /* simply insert into parent, no other children */
801 if (key_or_value) {
802 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
803 new_node->schema->name);
804 return LY_EINVAL;
805 }
Michal Vaskob104f112020-07-17 09:54:54 +0200806 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200807 }
808
809 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
810
Michal Vaskod59035b2020-07-08 12:00:06 +0200811 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200812 /* simple insert */
813 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200814 }
815
816 if (key_or_value) {
817 /* find the anchor sibling */
818 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
819 if (ret == LY_ENOTFOUND) {
820 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
821 new_node->schema->name);
822 return LY_EINVAL;
823 } else if (ret) {
824 return ret;
825 }
826
827 /* insert after */
828 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
829 assert(new_node->prev == anchor);
830 if (*first_node == new_node) {
831 *first_node = anchor;
832 }
833 } else {
834 if ((*first_node)->schema->flags & LYS_KEY) {
835 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
836
837 /* find last key */
838 anchor = *first_node;
839 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
840 anchor = anchor->next;
841 }
842 /* insert after the last key */
843 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
844 } else {
845 /* insert at the beginning */
846 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
847 *first_node = new_node;
848 }
849 }
850
851 return LY_SUCCESS;
852}
853
854/**
855 * @brief Apply diff subtree on data tree nodes, recursively.
856 *
857 * @param[in,out] first_node First sibling of the data tree.
858 * @param[in] parent_node Parent of the first sibling.
859 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200860 * @param[in] diff_cb Optional diff callback.
861 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200862 * @return LY_ERR value.
863 */
864static LY_ERR
865lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
866 lyd_diff_cb diff_cb, void *cb_data)
867{
868 LY_ERR ret;
869 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200870 const char *str_val;
871 enum lyd_diff_op op;
872 struct lyd_meta *meta;
Michal Vaskod59035b2020-07-08 12:00:06 +0200873 int dynamic;
874 const struct ly_ctx *ctx = LYD_NODE_CTX(diff_node);
875
876 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200877 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200878
Michal Vaskoe6323f62020-07-09 15:49:02 +0200879 /* handle specific user-ordered (leaf-)lists operations separately */
880 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
881 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200882 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200883 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200884 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200885 /* duplicate the node */
886 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 }
888
Michal Vaskoe6323f62020-07-09 15:49:02 +0200889 /* get "key" or "value" metadata string value */
890 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
891 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
892 str_val = lyd_meta2str(meta, &dynamic);
893
Michal Vaskod59035b2020-07-08 12:00:06 +0200894 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200895 if (str_val[0]) {
896 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200897 } else {
898 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
899 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200900 if (dynamic) {
901 free((char *)str_val);
902 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200903 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200904 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200905 lyd_free_tree(match);
906 }
907 return ret;
908 }
909
910 goto next_iter_r;
911 }
912
913 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200914 switch (op) {
915 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200916 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200917 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200918
919 if (match->schema->nodetype & LYD_NODE_TERM) {
920 /* special case of only dflt flag change */
921 if (diff_node->flags & LYD_DEFAULT) {
922 match->flags |= LYD_DEFAULT;
923 } else {
924 match->flags &= ~LYD_DEFAULT;
925 }
926 } else {
927 /* none operation on nodes without children is redundant and hence forbidden */
928 if (!LYD_CHILD(diff_node)) {
929 LOGINT_RET(ctx);
930 }
931 }
932 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200933 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200934 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200935 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200936
937 /* insert it at the end */
938 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200939 if (parent_node) {
940 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200941 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200942 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200943 }
944 if (ret) {
945 lyd_free_tree(match);
946 return ret;
947 }
948
949 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200950 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200951 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200952 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200953
954 /* remove it */
955 if ((match == *first_node) && !match->parent) {
956 assert(!parent_node);
957 /* we have removed the top-level node */
958 *first_node = (*first_node)->next;
959 }
960 lyd_free_tree(match);
961
962 /* we are not going recursively in this case, the whole subtree was already deleted */
963 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200964 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200965 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
966
967 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200968 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200969
970 /* update its value */
971 str_val = lyd_value2str((struct lyd_node_term *)diff_node, &dynamic);
972 ret = lyd_change_term(match, str_val);
973 if (dynamic) {
974 free((char *)str_val);
975 }
976 if (ret && (ret != LY_EEXIST)) {
977 LOGINT_RET(ctx);
978 }
979
980 /* with flags */
981 match->flags = diff_node->flags;
982 break;
983 default:
984 LOGINT_RET(ctx);
985 }
986
987next_iter_r:
988 if (diff_cb) {
989 /* call callback */
990 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
991 }
992
993 /* apply diff recursively */
994 LY_LIST_FOR(LYD_CHILD(diff_node), diff_child) {
995 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
996 }
997
998 return LY_SUCCESS;
999}
1000
1001API LY_ERR
1002lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
1003 lyd_diff_cb diff_cb, void *cb_data)
1004{
1005 const struct lyd_node *root;
1006
1007 LY_LIST_FOR(diff, root) {
1008 if (mod && (lyd_owner_module(root) != mod)) {
1009 /* skip data nodes from different modules */
1010 continue;
1011 }
1012
1013 /* apply relevant nodes from the diff datatree */
1014 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1015 }
1016
1017 return LY_SUCCESS;
1018}
1019
1020API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001021lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001022{
1023 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1024}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001025
1026/**
1027 * @brief Update operations on a diff node when the new operation is NONE.
1028 *
1029 * @param[in] diff_match Node from the diff.
1030 * @param[in] cur_op Current operation of the diff node.
1031 * @param[in] src_diff Current source diff node.
1032 * @return LY_ERR value.
1033 */
1034static LY_ERR
1035lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1036{
1037 switch (cur_op) {
1038 case LYD_DIFF_OP_NONE:
1039 case LYD_DIFF_OP_CREATE:
1040 case LYD_DIFF_OP_REPLACE:
1041 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1042 /* NONE on a term means only its dflt flag was changed */
1043 diff_match->flags &= ~LYD_DEFAULT;
1044 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1045 }
1046 break;
1047 default:
1048 /* delete operation is not valid */
1049 LOGINT_RET(LYD_NODE_CTX(src_diff));
1050 }
1051
1052 return LY_SUCCESS;
1053}
1054
1055/**
1056 * @brief Remove an attribute from a node.
1057 *
1058 * @param[in] node Node with the metadata.
1059 * @param[in] name Metadata name.
1060 */
1061static void
1062lyd_diff_del_meta(struct lyd_node *node, const char *name)
1063{
1064 struct lyd_meta *meta;
1065
1066 LY_LIST_FOR(node->meta, meta) {
1067 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001068 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001069 return;
1070 }
1071 }
1072
1073 assert(0);
1074}
1075
1076/**
1077 * @brief Set a specific operation of a node. Delete the previous operation, if any.
1078 *
1079 * @param[in] node Node to change.
1080 * @param[in] op Operation to set.
1081 * @return LY_ERR value.
1082 */
1083static LY_ERR
1084lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1085{
1086 struct lyd_meta *meta;
1087
1088 LY_LIST_FOR(node->meta, meta) {
1089 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001090 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001091 break;
1092 }
1093 }
1094
Michal Vasko3a41dff2020-07-15 14:30:28 +02001095 return lyd_new_meta(node, NULL, "yang:operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001096}
1097
1098/**
1099 * @brief Update operations on a diff node when the new operation is REPLACE.
1100 *
1101 * @param[in] diff_match Node from the diff.
1102 * @param[in] cur_op Current operation of the diff node.
1103 * @param[in] src_diff Current source diff node.
1104 * @return LY_ERR value.
1105 */
1106static LY_ERR
1107lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1108{
1109 LY_ERR ret;
1110 int dynamic;
1111 const char *str_val, *meta_name;
1112 struct lyd_meta *meta;
1113 const struct lys_module *mod;
1114 const struct lyd_node_any *any;
1115
1116 /* get "yang" module for the metadata */
1117 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff_match), "yang");
1118 assert(mod);
1119
1120 switch (cur_op) {
1121 case LYD_DIFF_OP_REPLACE:
1122 case LYD_DIFF_OP_CREATE:
1123 switch (diff_match->schema->nodetype) {
1124 case LYS_LIST:
1125 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001126 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001127 * keep orig_key/orig_value (only replace oper) and replace key/value */
1128 assert(lysc_is_userordered(diff_match->schema));
1129 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1130
1131 lyd_diff_del_meta(diff_match, meta_name);
1132 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
1133 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001134 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001135 break;
1136 case LYS_LEAF:
1137 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001138 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001139 LOGINT_RET(LYD_NODE_CTX(src_diff));
1140 }
1141
1142 /* get leaf value */
1143 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1144
1145 /* modify the node value */
1146 ret = lyd_change_term(diff_match, str_val);
1147 if (dynamic) {
1148 free((char *)str_val);
1149 }
1150 if (ret) {
1151 LOGINT_RET(LYD_NODE_CTX(src_diff));
1152 }
1153
1154 /* compare values whether there is any change at all */
1155 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1156 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_match)), LY_EINT);
1157 str_val = lyd_meta2str(meta, &dynamic);
Michal Vaskof937cfe2020-08-03 16:07:12 +02001158 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001159 if (dynamic) {
1160 free((char *)str_val);
1161 }
1162 if (!ret) {
1163 /* values are the same, remove orig-value meta and set oper to NONE */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001164 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001165 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1166 }
1167
1168 /* modify the default flag */
1169 diff_match->flags &= ~LYD_DEFAULT;
1170 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1171 break;
1172 case LYS_ANYXML:
1173 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001174 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001175 /* replaced with the exact same value, impossible */
1176 LOGINT_RET(LYD_NODE_CTX(src_diff));
1177 }
1178
1179 /* modify the node value */
1180 any = (struct lyd_node_any *)src_diff;
1181 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1182 break;
1183 default:
1184 LOGINT_RET(LYD_NODE_CTX(src_diff));
1185 }
1186 break;
1187 case LYD_DIFF_OP_NONE:
1188 /* it is moved now */
1189 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1190
1191 /* change the operation */
1192 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1193
1194 /* set orig-key and key metadata */
1195 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
1196 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001197 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001198
1199 meta = lyd_find_meta(src_diff->meta, mod, "key");
1200 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001201 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001202 break;
1203 default:
1204 /* delete operation is not valid */
1205 LOGINT_RET(LYD_NODE_CTX(src_diff));
1206 }
1207
1208 return LY_SUCCESS;
1209}
1210
1211/**
1212 * @brief Update operations in a diff node when the new operation is CREATE.
1213 *
1214 * @param[in] diff_match Node from the diff.
1215 * @param[in] cur_op Current operation of the diff node.
1216 * @param[in] src_diff Current source diff node.
1217 * @return LY_ERR value.
1218 */
1219static LY_ERR
1220lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1221{
1222 struct lyd_node *child;
Michal Vasko5632e0d2020-07-31 14:13:37 +02001223 const struct lysc_node_leaf *sleaf;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001224 const char *str_val;
1225 int dynamic;
1226 LY_ERR ret;
1227
1228 switch (cur_op) {
1229 case LYD_DIFF_OP_DELETE:
Michal Vasko5632e0d2020-07-31 14:13:37 +02001230 if (diff_match->schema->nodetype == LYS_LEAF) {
1231 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1232 } else {
1233 sleaf = NULL;
1234 }
1235
1236 if (sleaf && sleaf->dflt
1237 && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, &((struct lyd_node_term *)src_diff)->value)) {
1238 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1239 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1240
1241 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1242 /* add orig-dflt metadata */
1243 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1244 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
1245 }
1246 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001247 /* deleted + created -> operation NONE */
1248 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1249
1250 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1251 /* add orig-dflt metadata */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001252 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1253 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001254 }
1255 } else {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001256 assert(sleaf);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001257 /* we deleted it, but it was created with a different value -> operation REPLACE */
1258 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001259 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001260
Michal Vasko5632e0d2020-07-31 14:13:37 +02001261 if (lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001262 /* current value is the previous one (meta) */
1263 str_val = lyd_value2str((struct lyd_node_term *)diff_match, &dynamic);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001264 ret = lyd_new_meta(diff_match, NULL, "yang:orig-value", str_val, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001265 if (dynamic) {
1266 free((char *)str_val);
1267 }
Michal Vasko3a41dff2020-07-15 14:30:28 +02001268 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001269
1270 /* update the value itself */
1271 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1272 ret = lyd_change_term(diff_match, str_val);
1273 if (dynamic) {
1274 free((char *)str_val);
1275 }
1276 LY_CHECK_RET(ret);
1277 }
1278
1279 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1280 /* update dflt flag itself */
1281 diff_match->flags &= ~LYD_DEFAULT;
1282 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1283 } else {
1284 /* but the operation of its children should remain DELETE */
1285 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1286 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1287 }
1288 }
1289 break;
1290 default:
1291 /* create and replace operations are not valid */
1292 LOGINT_RET(LYD_NODE_CTX(src_diff));
1293 }
1294
1295 return LY_SUCCESS;
1296}
1297
1298/**
1299 * @brief Update operations on a diff node when the new operation is DELETE.
1300 *
1301 * @param[in] diff_match Node from the diff.
1302 * @param[in] cur_op Current operation of the diff node.
1303 * @param[in] src_diff Current source diff node.
1304 * @return LY_ERR value.
1305 */
1306static LY_ERR
1307lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1308{
1309 struct lyd_node *next, *child;
1310
1311 /* we can delete only exact existing nodes */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001312 LY_CHECK_ERR_RET(lyd_compare_single(diff_match, src_diff, 0), LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001313
1314 switch (cur_op) {
1315 case LYD_DIFF_OP_CREATE:
1316 /* it was created, but then deleted -> set NONE operation */
1317 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1318
1319 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1320 /* add orig-default meta because it is expected */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001321 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1322 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001323 } else {
1324 /* keep operation for all descendants (for now) */
1325 LY_LIST_FOR(LYD_CHILD(diff_match), child) {
1326 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1327 }
1328 }
1329 break;
1330 case LYD_DIFF_OP_REPLACE:
1331 /* similar to none operation but also remove the redundant attribute */
1332 lyd_diff_del_meta(diff_match, "orig-value");
1333 /* fallthrough */
1334 case LYD_DIFF_OP_NONE:
1335 /* it was not modified, but should be deleted -> set DELETE operation */
1336 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1337
Michal Vasko5632e0d2020-07-31 14:13:37 +02001338 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001339 LY_LIST_FOR_SAFE(LYD_CHILD(diff_match), next, child) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001340 if (lyd_find_sibling_first(lyd_node_children(src_diff, 0), child, NULL) == LY_ENOTFOUND) {
1341 lyd_free_tree(child);
1342 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001343 }
1344 break;
1345 default:
1346 /* delete operation is not valid */
1347 LOGINT_RET(LYD_NODE_CTX(src_diff));
1348 }
1349
1350 return LY_SUCCESS;
1351}
1352
1353/**
1354 * @brief Check whether this diff node is redundant (does not change data).
1355 *
1356 * @param[in] diff Diff node.
1357 * @return 0 if not, non-zero if it is.
1358 */
1359static int
1360lyd_diff_is_redundant(struct lyd_node *diff)
1361{
1362 enum lyd_diff_op op;
1363 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1364 struct lyd_node *child;
1365 const struct lys_module *mod;
1366 const char *str;
1367 int dynamic;
1368
1369 assert(diff);
1370
1371 child = LYD_CHILD(diff);
1372 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff), "yang");
1373 assert(mod);
1374
1375 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001376 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001377
1378 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1379 /* check for redundant move */
1380 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1381 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1382 assert(orig_val_meta && val_meta);
1383
1384 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1385 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001386 lyd_free_meta_single(orig_val_meta);
1387 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001388 if (child) {
1389 /* change operation to NONE, we have siblings */
1390 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1391 return 0;
1392 }
1393
1394 /* redundant node, BUT !!
1395 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1396 * because the data that this is applied on should not change for the diff lifetime.
1397 * However, when we are merging 2 diffs, this conversion is actually lossy because
1398 * if the data change, the move operation can also change its meaning. In this specific
1399 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1400 */
1401 return 1;
1402 }
1403 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1404 /* check whether at least the default flags are different */
1405 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1406 assert(meta);
1407 str = lyd_meta2str(meta, &dynamic);
1408 assert(!dynamic);
1409
1410 /* if previous and current dflt flags are the same, this node is redundant */
1411 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1412 return 1;
1413 }
1414 return 0;
1415 }
1416
1417 if (!child && (op == LYD_DIFF_OP_NONE)) {
1418 return 1;
1419 }
1420
1421 return 0;
1422}
1423
1424/**
1425 * @brief Merge sysrepo diff with another diff, recursively.
1426 *
1427 * @param[in] src_diff Source diff node.
1428 * @param[in] diff_parent Current sysrepo diff parent.
1429 * @param[in] diff_cb Optional diff callback.
1430 * @param[in] cb_data User data for @p diff_cb.
1431 * @param[in,out] diff Diff root node.
1432 * @return LY_ERR value.
1433 */
1434static LY_ERR
1435lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
1436 struct lyd_node **diff)
1437{
1438 LY_ERR ret = LY_SUCCESS;
1439 struct lyd_node *child, *diff_node = NULL;
1440 enum lyd_diff_op src_op, cur_op;
1441
1442 /* get source node operation */
1443 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1444
1445 /* find an equal node in the current diff */
1446 lyd_diff_find_node(diff_parent ? LYD_CHILD(diff_parent) : *diff, src_diff, &diff_node);
1447
1448 if (diff_node) {
1449 /* get target (current) operation */
1450 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1451
1452 /* merge operations */
1453 switch (src_op) {
1454 case LYD_DIFF_OP_REPLACE:
1455 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1456 break;
1457 case LYD_DIFF_OP_CREATE:
1458 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff);
1459 break;
1460 case LYD_DIFF_OP_DELETE:
1461 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1462 break;
1463 case LYD_DIFF_OP_NONE:
1464 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1465 break;
1466 default:
1467 LOGINT_RET(LYD_NODE_CTX(src_diff));
1468 }
1469 if (ret) {
1470 LOGERR(LYD_NODE_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
1471 return ret;
1472 }
1473
1474 if (diff_cb) {
1475 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001476 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001477 }
1478
1479 /* update diff parent */
1480 diff_parent = diff_node;
1481
1482 /* merge src_diff recursively */
1483 LY_LIST_FOR(LYD_CHILD(src_diff), child) {
1484 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, diff));
1485 }
1486 } else {
1487 /* add new diff node with all descendants */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001488 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 +02001489
1490 /* insert node into diff if not already */
1491 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001492 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001493 }
1494
1495 /* update operation */
1496 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1497
1498 if (diff_cb) {
1499 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001500 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001501 }
1502
1503 /* update diff parent */
1504 diff_parent = diff_node;
1505 }
1506
1507 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001508 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001509 if (diff_parent == *diff) {
1510 *diff = (*diff)->next;
1511 }
1512 lyd_free_tree(diff_parent);
1513 }
1514
1515 return LY_SUCCESS;
1516}
1517
1518API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001519lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod,
1520 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001521{
1522 const struct lyd_node *src_root;
1523
1524 LY_LIST_FOR(src_diff, src_root) {
1525 if (mod && (lyd_owner_module(src_root) != mod)) {
1526 /* skip data nodes from different modules */
1527 continue;
1528 }
1529
1530 /* apply relevant nodes from the diff datatree */
1531 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, diff));
1532 }
1533
1534 return LY_SUCCESS;
1535}
1536
1537API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001538lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001539{
Michal Vaskofb737aa2020-08-06 13:53:53 +02001540 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001541}
Michal Vasko4231fb62020-07-13 13:54:47 +02001542
1543static LY_ERR
1544lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
1545{
1546 LY_ERR ret = LY_SUCCESS;
1547 struct lyd_meta *meta;
1548 const char *val1 = NULL, *val2 = NULL;
1549 int dyn1 = 0, dyn2 = 0, flags;
1550
1551 meta = lyd_find_meta(leaf->meta, mod, "orig-value");
1552 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(leaf)), LY_EINT);
1553
1554 /* orig-value */
1555 val1 = lyd_meta2str(meta, &dyn1);
1556
1557 /* current value */
1558 val2 = lyd_value2str((struct lyd_node_term *)leaf, &dyn2);
1559
1560 /* switch values, keep default flag */
1561 flags = leaf->flags;
1562 LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
1563 leaf->flags = flags;
1564 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1565
1566cleanup:
1567 if (dyn1) {
1568 free((char *)val1);
1569 }
1570 if (dyn2) {
1571 free((char *)val2);
1572 }
1573 return ret;
1574}
1575
1576static LY_ERR
1577lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1578{
1579 struct lyd_meta *meta;
1580 const char *val;
1581 int dyn, flag1, flag2;
1582
1583 meta = lyd_find_meta(node->meta, mod, "orig-default");
1584 if (!meta) {
1585 /* default flag did not change */
1586 return LY_SUCCESS;
1587 }
1588
1589 /* orig-default */
1590 val = lyd_meta2str(meta, &dyn);
1591 assert(!dyn);
1592 if (!strcmp(val, "true")) {
1593 flag1 = LYD_DEFAULT;
1594 } else {
1595 flag1 = 0;
1596 }
1597
1598 /* current default */
1599 flag2 = node->flags & LYD_DEFAULT;
1600
1601 /* switch defaults */
1602 node->flags &= ~LYD_DEFAULT;
1603 node->flags |= flag1;
1604 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1605
1606 return LY_SUCCESS;
1607}
1608
1609static LY_ERR
1610lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1611{
1612 LY_ERR ret = LY_SUCCESS;
1613 struct lyd_meta *meta1, *meta2;
1614 const char *val1 = NULL, *val2 = NULL;
1615 int dyn1 = 0, dyn2 = 0;
1616
1617 meta1 = lyd_find_meta(node->meta, mod, name1);
1618 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1619
1620 meta2 = lyd_find_meta(node->meta, mod, name2);
1621 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1622
1623 /* value1 */
1624 val1 = lyd_meta2str(meta1, &dyn1);
1625
1626 /* value2 */
1627 val2 = lyd_meta2str(meta2, &dyn2);
1628
1629 /* switch values */
1630 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1631 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1632
1633cleanup:
1634 if (dyn1) {
1635 free((char *)val1);
1636 }
1637 if (dyn2) {
1638 free((char *)val2);
1639 }
1640 return ret;
1641}
1642
1643API LY_ERR
1644lyd_diff_reverse(const struct lyd_node *src_diff, struct lyd_node **diff)
1645{
1646 LY_ERR ret = LY_SUCCESS;
1647 const struct lys_module *mod;
1648 struct lyd_node *root, *next, *elem;
1649 enum lyd_diff_op op;
1650
1651 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1652
1653 if (!src_diff) {
1654 *diff = NULL;
1655 return LY_SUCCESS;
1656 }
1657
1658 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001659 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001660
1661 /* find module with metadata needed for later */
1662 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(src_diff), "yang");
1663 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_NODE_CTX(src_diff)); ret = LY_EINT, cleanup);
1664
1665 LY_LIST_FOR(*diff, root) {
1666 LYD_TREE_DFS_BEGIN(root, next, elem) {
1667 /* find operation attribute, if any */
1668 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
1669
1670 switch (op) {
1671 case LYD_DIFF_OP_CREATE:
1672 /* reverse create to delete */
1673 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
1674 break;
1675 case LYD_DIFF_OP_DELETE:
1676 /* reverse delete to create */
1677 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
1678 break;
1679 case LYD_DIFF_OP_REPLACE:
1680 switch (elem->schema->nodetype) {
1681 case LYS_LEAF:
1682 /* leaf value change */
1683 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1684 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1685 break;
1686 case LYS_LEAFLIST:
1687 /* leaf-list move */
1688 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1689 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1690 break;
1691 case LYS_LIST:
1692 /* list move */
1693 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1694 break;
1695 default:
1696 LOGINT(LYD_NODE_CTX(src_diff));
1697 ret = LY_EINT;
1698 goto cleanup;
1699 }
1700 break;
1701 case LYD_DIFF_OP_NONE:
1702 switch (elem->schema->nodetype) {
1703 case LYS_LEAF:
1704 case LYS_LEAFLIST:
1705 /* default flag change */
1706 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1707 break;
1708 default:
1709 /* nothing to do */
1710 break;
1711 }
1712 break;
1713 default:
1714 /* nothing to do */
1715 break;
1716 }
1717
1718 LYD_TREE_DFS_END(root, next, elem);
1719 }
1720 }
1721
1722cleanup:
1723 if (ret) {
1724 lyd_free_siblings(*diff);
1725 *diff = NULL;
1726 }
1727 return ret;
1728}