blob: 1ddeee405127802fd82823f7b4879f4092aa5011 [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 */
Michal Vasko8ee464a2020-08-11 14:12:50 +0200101 siblings = LYD_CHILD_NO_KEYS(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200102 }
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 Vasko8ee464a2020-08-11 14:12:50 +0200605 LY_CHECK_GOTO(lyd_diff_siblings_r(LYD_CHILD_NO_KEYS(iter_first), LYD_CHILD_NO_KEYS(match_second), options,
606 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200607 }
608
609 if (nosiblings) {
610 break;
611 }
612 }
613
614 /* reset all cached positions */
615 LY_ARRAY_FOR(userord, u) {
616 userord[u].pos = 0;
617 }
618
619 /* compare second tree to the first tree - create, user-ordered move */
620 LY_LIST_FOR(second, iter_second) {
621 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200622 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200623 /* skip default nodes */
624 continue;
625 }
626
627 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
628 lyd_find_sibling_first(first, iter_second, &match_first);
629 } else {
630 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
631 }
632
Michal Vasko3a41dff2020-07-15 14:30:28 +0200633 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200634 /* ignore default nodes */
635 match_first = NULL;
636 }
637
638 if (lysc_is_userordered(iter_second->schema)) {
639 /* get all the attributes */
640 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
641 &value, &orig_value, &key, &orig_key);
642
643 /* add into diff if there are any changes */
644 if (!ret) {
645 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
646
647 free(orig_value);
648 free(key);
649 free(value);
650 free(orig_key);
651 LY_CHECK_GOTO(ret, cleanup);
652 } else if (ret == LY_ENOT) {
653 ret = LY_SUCCESS;
654 } else {
655 goto cleanup;
656 }
657 } else if (!match_first) {
658 /* get all the attributes */
659 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
660
661 /* there must be changes, it is created */
662 assert(op == LYD_DIFF_OP_CREATE);
663 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
664
665 free(orig_value);
666 LY_CHECK_GOTO(ret, cleanup);
667 } /* else was handled */
668
669 if (nosiblings) {
670 break;
671 }
672 }
673
674cleanup:
675 LY_ARRAY_FOR(userord, u) {
676 LY_ARRAY_FREE(userord[u].inst);
677 }
678 LY_ARRAY_FREE(userord);
679 return ret;
680}
681
Michal Vasko3a41dff2020-07-15 14:30:28 +0200682static LY_ERR
683lyd_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 +0200684{
685 const struct ly_ctx *ctx;
686
687 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
688
689 if (first) {
690 ctx = LYD_NODE_CTX(first);
691 } else if (second) {
692 ctx = LYD_NODE_CTX(second);
693 } else {
694 ctx = NULL;
695 }
696
697 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
698 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
699 return LY_EINVAL;
700 }
701
702 *diff = NULL;
703
Michal Vasko3a41dff2020-07-15 14:30:28 +0200704 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
705}
706
707API LY_ERR
708lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
709{
710 return lyd_diff(first, second, options, 1, diff);
711}
712
713API LY_ERR
714lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, int options, struct lyd_node **diff)
715{
716 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200717}
718
719/**
720 * @brief Find a matching node in data tree for a diff node.
721 *
722 * @param[in] first_node First sibling in the data tree.
723 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200724 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200725 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200726static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200727lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
728{
729 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
730 /* try to find the exact instance */
731 lyd_find_sibling_first(first_node, diff_node, match_p);
732 } else {
733 /* try to simply find the node, there cannot be more instances */
734 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
735 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200736}
737
738/**
739 * @brief Learn operation of a diff node.
740 *
741 * @param[in] diff_node Diff node.
742 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200743 * @return LY_ERR value.
744 */
745static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200746lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200747{
748 struct lyd_meta *meta = NULL;
749 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200750 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200751 int dynamic;
752
753 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
754 LY_LIST_FOR(diff_parent->meta, meta) {
755 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
756 str = lyd_meta2str(meta, &dynamic);
757 assert(!dynamic);
758 if ((str[0] == 'r') && (diff_parent != diff_node)) {
759 /* we do not care about this operation if it's in our parent */
760 continue;
761 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200762 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200763 break;
764 }
765 }
766 if (meta) {
767 break;
768 }
769 }
770 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
771
Michal Vaskod59035b2020-07-08 12:00:06 +0200772 return LY_SUCCESS;
773}
774
775/**
776 * @brief Insert a diff node into a data tree.
777 *
778 * @param[in,out] first_node First sibling of the data tree.
779 * @param[in] parent_node Data tree sibling parent node.
780 * @param[in] new_node Node to insert.
781 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
782 * instance will be inserted at the first position.
783 * @return err_info, NULL on success.
784 */
785static LY_ERR
786lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
787 const char *key_or_value)
788{
789 LY_ERR ret;
790 struct lyd_node *anchor;
791
792 assert(new_node);
793
794 if (!*first_node) {
795 if (!parent_node) {
796 /* no parent or siblings */
797 *first_node = new_node;
798 return LY_SUCCESS;
799 }
800
801 /* simply insert into parent, no other children */
802 if (key_or_value) {
803 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
804 new_node->schema->name);
805 return LY_EINVAL;
806 }
Michal Vaskob104f112020-07-17 09:54:54 +0200807 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200808 }
809
810 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
811
Michal Vaskod59035b2020-07-08 12:00:06 +0200812 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200813 /* simple insert */
814 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200815 }
816
817 if (key_or_value) {
818 /* find the anchor sibling */
819 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
820 if (ret == LY_ENOTFOUND) {
821 LOGERR(LYD_NODE_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
822 new_node->schema->name);
823 return LY_EINVAL;
824 } else if (ret) {
825 return ret;
826 }
827
828 /* insert after */
829 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
830 assert(new_node->prev == anchor);
831 if (*first_node == new_node) {
832 *first_node = anchor;
833 }
834 } else {
835 if ((*first_node)->schema->flags & LYS_KEY) {
836 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
837
838 /* find last key */
839 anchor = *first_node;
840 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
841 anchor = anchor->next;
842 }
843 /* insert after the last key */
844 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
845 } else {
846 /* insert at the beginning */
847 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
848 *first_node = new_node;
849 }
850 }
851
852 return LY_SUCCESS;
853}
854
855/**
856 * @brief Apply diff subtree on data tree nodes, recursively.
857 *
858 * @param[in,out] first_node First sibling of the data tree.
859 * @param[in] parent_node Parent of the first sibling.
860 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200861 * @param[in] diff_cb Optional diff callback.
862 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200863 * @return LY_ERR value.
864 */
865static LY_ERR
866lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
867 lyd_diff_cb diff_cb, void *cb_data)
868{
869 LY_ERR ret;
870 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200871 const char *str_val;
872 enum lyd_diff_op op;
873 struct lyd_meta *meta;
Michal Vaskod59035b2020-07-08 12:00:06 +0200874 int dynamic;
875 const struct ly_ctx *ctx = LYD_NODE_CTX(diff_node);
876
877 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200878 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200879
Michal Vaskoe6323f62020-07-09 15:49:02 +0200880 /* handle specific user-ordered (leaf-)lists operations separately */
881 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
882 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200883 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200884 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200885 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200886 /* duplicate the node */
887 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200888 }
889
Michal Vaskoe6323f62020-07-09 15:49:02 +0200890 /* get "key" or "value" metadata string value */
891 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
892 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_node)), LY_EINT);
893 str_val = lyd_meta2str(meta, &dynamic);
894
Michal Vaskod59035b2020-07-08 12:00:06 +0200895 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200896 if (str_val[0]) {
897 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200898 } else {
899 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
900 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200901 if (dynamic) {
902 free((char *)str_val);
903 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200904 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200905 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200906 lyd_free_tree(match);
907 }
908 return ret;
909 }
910
911 goto next_iter_r;
912 }
913
914 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200915 switch (op) {
916 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200917 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200918 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200919
920 if (match->schema->nodetype & LYD_NODE_TERM) {
921 /* special case of only dflt flag change */
922 if (diff_node->flags & LYD_DEFAULT) {
923 match->flags |= LYD_DEFAULT;
924 } else {
925 match->flags &= ~LYD_DEFAULT;
926 }
927 } else {
928 /* none operation on nodes without children is redundant and hence forbidden */
Michal Vasko8ee464a2020-08-11 14:12:50 +0200929 if (!LYD_CHILD_NO_KEYS(diff_node)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200930 LOGINT_RET(ctx);
931 }
932 }
933 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200934 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200935 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200936 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200937
938 /* insert it at the end */
939 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200940 if (parent_node) {
941 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200942 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200943 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200944 }
945 if (ret) {
946 lyd_free_tree(match);
947 return ret;
948 }
949
950 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200951 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200952 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200953 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200954
955 /* remove it */
956 if ((match == *first_node) && !match->parent) {
957 assert(!parent_node);
958 /* we have removed the top-level node */
959 *first_node = (*first_node)->next;
960 }
961 lyd_free_tree(match);
962
963 /* we are not going recursively in this case, the whole subtree was already deleted */
964 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200965 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200966 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
967
968 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200969 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200970
971 /* update its value */
972 str_val = lyd_value2str((struct lyd_node_term *)diff_node, &dynamic);
973 ret = lyd_change_term(match, str_val);
974 if (dynamic) {
975 free((char *)str_val);
976 }
977 if (ret && (ret != LY_EEXIST)) {
978 LOGINT_RET(ctx);
979 }
980
981 /* with flags */
982 match->flags = diff_node->flags;
983 break;
984 default:
985 LOGINT_RET(ctx);
986 }
987
988next_iter_r:
989 if (diff_cb) {
990 /* call callback */
991 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
992 }
993
994 /* apply diff recursively */
Michal Vasko8ee464a2020-08-11 14:12:50 +0200995 LY_LIST_FOR(LYD_CHILD_NO_KEYS(diff_node), diff_child) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200996 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
997 }
998
999 return LY_SUCCESS;
1000}
1001
1002API LY_ERR
1003lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
1004 lyd_diff_cb diff_cb, void *cb_data)
1005{
1006 const struct lyd_node *root;
1007
1008 LY_LIST_FOR(diff, root) {
1009 if (mod && (lyd_owner_module(root) != mod)) {
1010 /* skip data nodes from different modules */
1011 continue;
1012 }
1013
1014 /* apply relevant nodes from the diff datatree */
1015 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1016 }
1017
1018 return LY_SUCCESS;
1019}
1020
1021API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001022lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001023{
1024 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1025}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001026
1027/**
1028 * @brief Update operations on a diff node when the new operation is NONE.
1029 *
1030 * @param[in] diff_match Node from the diff.
1031 * @param[in] cur_op Current operation of the diff node.
1032 * @param[in] src_diff Current source diff node.
1033 * @return LY_ERR value.
1034 */
1035static LY_ERR
1036lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1037{
1038 switch (cur_op) {
1039 case LYD_DIFF_OP_NONE:
1040 case LYD_DIFF_OP_CREATE:
1041 case LYD_DIFF_OP_REPLACE:
1042 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1043 /* NONE on a term means only its dflt flag was changed */
1044 diff_match->flags &= ~LYD_DEFAULT;
1045 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1046 }
1047 break;
1048 default:
1049 /* delete operation is not valid */
1050 LOGINT_RET(LYD_NODE_CTX(src_diff));
1051 }
1052
1053 return LY_SUCCESS;
1054}
1055
1056/**
1057 * @brief Remove an attribute from a node.
1058 *
1059 * @param[in] node Node with the metadata.
1060 * @param[in] name Metadata name.
1061 */
1062static void
1063lyd_diff_del_meta(struct lyd_node *node, const char *name)
1064{
1065 struct lyd_meta *meta;
1066
1067 LY_LIST_FOR(node->meta, meta) {
1068 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001069 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001070 return;
1071 }
1072 }
1073
1074 assert(0);
1075}
1076
1077/**
1078 * @brief Set a specific operation of a node. Delete the previous operation, if any.
1079 *
1080 * @param[in] node Node to change.
1081 * @param[in] op Operation to set.
1082 * @return LY_ERR value.
1083 */
1084static LY_ERR
1085lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1086{
1087 struct lyd_meta *meta;
1088
1089 LY_LIST_FOR(node->meta, meta) {
1090 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001091 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001092 break;
1093 }
1094 }
1095
Michal Vasko3a41dff2020-07-15 14:30:28 +02001096 return lyd_new_meta(node, NULL, "yang:operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001097}
1098
1099/**
1100 * @brief Update operations on a diff node when the new operation is REPLACE.
1101 *
1102 * @param[in] diff_match Node from the diff.
1103 * @param[in] cur_op Current operation of the diff node.
1104 * @param[in] src_diff Current source diff node.
1105 * @return LY_ERR value.
1106 */
1107static LY_ERR
1108lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1109{
1110 LY_ERR ret;
1111 int dynamic;
1112 const char *str_val, *meta_name;
1113 struct lyd_meta *meta;
1114 const struct lys_module *mod;
1115 const struct lyd_node_any *any;
1116
1117 /* get "yang" module for the metadata */
1118 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff_match), "yang");
1119 assert(mod);
1120
1121 switch (cur_op) {
1122 case LYD_DIFF_OP_REPLACE:
1123 case LYD_DIFF_OP_CREATE:
1124 switch (diff_match->schema->nodetype) {
1125 case LYS_LIST:
1126 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001127 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001128 * keep orig_key/orig_value (only replace oper) and replace key/value */
1129 assert(lysc_is_userordered(diff_match->schema));
1130 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1131
1132 lyd_diff_del_meta(diff_match, meta_name);
1133 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
1134 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001135 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001136 break;
1137 case LYS_LEAF:
1138 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001139 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001140 LOGINT_RET(LYD_NODE_CTX(src_diff));
1141 }
1142
1143 /* get leaf value */
1144 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1145
1146 /* modify the node value */
1147 ret = lyd_change_term(diff_match, str_val);
1148 if (dynamic) {
1149 free((char *)str_val);
1150 }
1151 if (ret) {
1152 LOGINT_RET(LYD_NODE_CTX(src_diff));
1153 }
1154
1155 /* compare values whether there is any change at all */
1156 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1157 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(diff_match)), LY_EINT);
1158 str_val = lyd_meta2str(meta, &dynamic);
Michal Vaskof937cfe2020-08-03 16:07:12 +02001159 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001160 if (dynamic) {
1161 free((char *)str_val);
1162 }
1163 if (!ret) {
1164 /* values are the same, remove orig-value meta and set oper to NONE */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001165 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001166 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1167 }
1168
1169 /* modify the default flag */
1170 diff_match->flags &= ~LYD_DEFAULT;
1171 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1172 break;
1173 case LYS_ANYXML:
1174 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001175 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001176 /* replaced with the exact same value, impossible */
1177 LOGINT_RET(LYD_NODE_CTX(src_diff));
1178 }
1179
1180 /* modify the node value */
1181 any = (struct lyd_node_any *)src_diff;
1182 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1183 break;
1184 default:
1185 LOGINT_RET(LYD_NODE_CTX(src_diff));
1186 }
1187 break;
1188 case LYD_DIFF_OP_NONE:
1189 /* it is moved now */
1190 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1191
1192 /* change the operation */
1193 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1194
1195 /* set orig-key and key metadata */
1196 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
1197 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001198 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001199
1200 meta = lyd_find_meta(src_diff->meta, mod, "key");
1201 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001202 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001203 break;
1204 default:
1205 /* delete operation is not valid */
1206 LOGINT_RET(LYD_NODE_CTX(src_diff));
1207 }
1208
1209 return LY_SUCCESS;
1210}
1211
1212/**
1213 * @brief Update operations in a diff node when the new operation is CREATE.
1214 *
1215 * @param[in] diff_match Node from the diff.
1216 * @param[in] cur_op Current operation of the diff node.
1217 * @param[in] src_diff Current source diff node.
1218 * @return LY_ERR value.
1219 */
1220static LY_ERR
1221lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1222{
1223 struct lyd_node *child;
Michal Vasko5632e0d2020-07-31 14:13:37 +02001224 const struct lysc_node_leaf *sleaf;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001225 const char *str_val;
1226 int dynamic;
1227 LY_ERR ret;
1228
1229 switch (cur_op) {
1230 case LYD_DIFF_OP_DELETE:
Michal Vasko5632e0d2020-07-31 14:13:37 +02001231 if (diff_match->schema->nodetype == LYS_LEAF) {
1232 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1233 } else {
1234 sleaf = NULL;
1235 }
1236
1237 if (sleaf && sleaf->dflt
1238 && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, &((struct lyd_node_term *)src_diff)->value)) {
1239 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1240 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1241
1242 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1243 /* add orig-dflt metadata */
1244 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1245 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
1246 }
1247 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001248 /* deleted + created -> operation NONE */
1249 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1250
1251 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1252 /* add orig-dflt metadata */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001253 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1254 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001255 }
1256 } else {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001257 assert(sleaf);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001258 /* we deleted it, but it was created with a different value -> operation REPLACE */
1259 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001260 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001261
Michal Vasko5632e0d2020-07-31 14:13:37 +02001262 if (lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001263 /* current value is the previous one (meta) */
1264 str_val = lyd_value2str((struct lyd_node_term *)diff_match, &dynamic);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001265 ret = lyd_new_meta(diff_match, NULL, "yang:orig-value", str_val, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001266 if (dynamic) {
1267 free((char *)str_val);
1268 }
Michal Vasko3a41dff2020-07-15 14:30:28 +02001269 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001270
1271 /* update the value itself */
1272 str_val = lyd_value2str((struct lyd_node_term *)src_diff, &dynamic);
1273 ret = lyd_change_term(diff_match, str_val);
1274 if (dynamic) {
1275 free((char *)str_val);
1276 }
1277 LY_CHECK_RET(ret);
1278 }
1279
1280 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1281 /* update dflt flag itself */
1282 diff_match->flags &= ~LYD_DEFAULT;
1283 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1284 } else {
1285 /* but the operation of its children should remain DELETE */
Michal Vasko8ee464a2020-08-11 14:12:50 +02001286 LY_LIST_FOR(LYD_CHILD_NO_KEYS(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001287 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1288 }
1289 }
1290 break;
1291 default:
1292 /* create and replace operations are not valid */
1293 LOGINT_RET(LYD_NODE_CTX(src_diff));
1294 }
1295
1296 return LY_SUCCESS;
1297}
1298
1299/**
1300 * @brief Update operations on a diff node when the new operation is DELETE.
1301 *
1302 * @param[in] diff_match Node from the diff.
1303 * @param[in] cur_op Current operation of the diff node.
1304 * @param[in] src_diff Current source diff node.
1305 * @return LY_ERR value.
1306 */
1307static LY_ERR
1308lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1309{
1310 struct lyd_node *next, *child;
1311
1312 /* we can delete only exact existing nodes */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001313 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 +02001314
1315 switch (cur_op) {
1316 case LYD_DIFF_OP_CREATE:
1317 /* it was created, but then deleted -> set NONE operation */
1318 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1319
1320 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1321 /* add orig-default meta because it is expected */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001322 LY_CHECK_RET(lyd_new_meta(diff_match, NULL, "yang:orig-default",
1323 diff_match->flags & LYD_DEFAULT ? "true" : "false", NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001324 } else {
1325 /* keep operation for all descendants (for now) */
Michal Vasko8ee464a2020-08-11 14:12:50 +02001326 LY_LIST_FOR(LYD_CHILD_NO_KEYS(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001327 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1328 }
1329 }
1330 break;
1331 case LYD_DIFF_OP_REPLACE:
1332 /* similar to none operation but also remove the redundant attribute */
1333 lyd_diff_del_meta(diff_match, "orig-value");
1334 /* fallthrough */
1335 case LYD_DIFF_OP_NONE:
1336 /* it was not modified, but should be deleted -> set DELETE operation */
1337 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1338
Michal Vasko5632e0d2020-07-31 14:13:37 +02001339 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Michal Vasko8ee464a2020-08-11 14:12:50 +02001340 LY_LIST_FOR_SAFE(LYD_CHILD_NO_KEYS(diff_match), next, child) {
1341 if (lyd_find_sibling_first(LYD_CHILD(src_diff), child, NULL) == LY_ENOTFOUND) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001342 lyd_free_tree(child);
1343 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001344 }
1345 break;
1346 default:
1347 /* delete operation is not valid */
1348 LOGINT_RET(LYD_NODE_CTX(src_diff));
1349 }
1350
1351 return LY_SUCCESS;
1352}
1353
1354/**
1355 * @brief Check whether this diff node is redundant (does not change data).
1356 *
1357 * @param[in] diff Diff node.
1358 * @return 0 if not, non-zero if it is.
1359 */
1360static int
1361lyd_diff_is_redundant(struct lyd_node *diff)
1362{
1363 enum lyd_diff_op op;
1364 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1365 struct lyd_node *child;
1366 const struct lys_module *mod;
1367 const char *str;
1368 int dynamic;
1369
1370 assert(diff);
1371
Michal Vasko8ee464a2020-08-11 14:12:50 +02001372 child = LYD_CHILD_NO_KEYS(diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001373 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(diff), "yang");
1374 assert(mod);
1375
1376 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001377 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001378
1379 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1380 /* check for redundant move */
1381 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1382 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1383 assert(orig_val_meta && val_meta);
1384
1385 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1386 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001387 lyd_free_meta_single(orig_val_meta);
1388 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001389 if (child) {
1390 /* change operation to NONE, we have siblings */
1391 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1392 return 0;
1393 }
1394
1395 /* redundant node, BUT !!
1396 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1397 * because the data that this is applied on should not change for the diff lifetime.
1398 * However, when we are merging 2 diffs, this conversion is actually lossy because
1399 * if the data change, the move operation can also change its meaning. In this specific
1400 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1401 */
1402 return 1;
1403 }
1404 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1405 /* check whether at least the default flags are different */
1406 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1407 assert(meta);
1408 str = lyd_meta2str(meta, &dynamic);
1409 assert(!dynamic);
1410
1411 /* if previous and current dflt flags are the same, this node is redundant */
1412 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1413 return 1;
1414 }
1415 return 0;
1416 }
1417
1418 if (!child && (op == LYD_DIFF_OP_NONE)) {
1419 return 1;
1420 }
1421
1422 return 0;
1423}
1424
1425/**
1426 * @brief Merge sysrepo diff with another diff, recursively.
1427 *
1428 * @param[in] src_diff Source diff node.
1429 * @param[in] diff_parent Current sysrepo diff parent.
1430 * @param[in] diff_cb Optional diff callback.
1431 * @param[in] cb_data User data for @p diff_cb.
1432 * @param[in,out] diff Diff root node.
1433 * @return LY_ERR value.
1434 */
1435static LY_ERR
1436lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
1437 struct lyd_node **diff)
1438{
1439 LY_ERR ret = LY_SUCCESS;
1440 struct lyd_node *child, *diff_node = NULL;
1441 enum lyd_diff_op src_op, cur_op;
1442
1443 /* get source node operation */
1444 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1445
1446 /* find an equal node in the current diff */
Michal Vasko8ee464a2020-08-11 14:12:50 +02001447 lyd_diff_find_node(diff_parent ? LYD_CHILD_NO_KEYS(diff_parent) : *diff, src_diff, &diff_node);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001448
1449 if (diff_node) {
1450 /* get target (current) operation */
1451 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1452
1453 /* merge operations */
1454 switch (src_op) {
1455 case LYD_DIFF_OP_REPLACE:
1456 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1457 break;
1458 case LYD_DIFF_OP_CREATE:
1459 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff);
1460 break;
1461 case LYD_DIFF_OP_DELETE:
1462 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1463 break;
1464 case LYD_DIFF_OP_NONE:
1465 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1466 break;
1467 default:
1468 LOGINT_RET(LYD_NODE_CTX(src_diff));
1469 }
1470 if (ret) {
1471 LOGERR(LYD_NODE_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
1472 return ret;
1473 }
1474
1475 if (diff_cb) {
1476 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001477 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001478 }
1479
1480 /* update diff parent */
1481 diff_parent = diff_node;
1482
1483 /* merge src_diff recursively */
Michal Vasko8ee464a2020-08-11 14:12:50 +02001484 LY_LIST_FOR(LYD_CHILD_NO_KEYS(src_diff), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001485 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, diff));
1486 }
1487 } else {
1488 /* add new diff node with all descendants */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001489 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 +02001490
1491 /* insert node into diff if not already */
1492 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001493 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001494 }
1495
1496 /* update operation */
1497 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1498
1499 if (diff_cb) {
1500 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001501 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001502 }
1503
1504 /* update diff parent */
1505 diff_parent = diff_node;
1506 }
1507
1508 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001509 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001510 if (diff_parent == *diff) {
1511 *diff = (*diff)->next;
1512 }
1513 lyd_free_tree(diff_parent);
1514 }
1515
1516 return LY_SUCCESS;
1517}
1518
1519API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001520lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod,
1521 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001522{
1523 const struct lyd_node *src_root;
1524
1525 LY_LIST_FOR(src_diff, src_root) {
1526 if (mod && (lyd_owner_module(src_root) != mod)) {
1527 /* skip data nodes from different modules */
1528 continue;
1529 }
1530
1531 /* apply relevant nodes from the diff datatree */
1532 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, diff));
1533 }
1534
1535 return LY_SUCCESS;
1536}
1537
1538API LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001539lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent, const struct lyd_node *src_sibling,
1540 lyd_diff_cb diff_cb, void *cb_data)
1541{
1542 if (!src_sibling) {
1543 return LY_SUCCESS;
1544 }
1545
1546 return lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, diff_first);
1547}
1548
1549API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001550lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001551{
Michal Vaskofb737aa2020-08-06 13:53:53 +02001552 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001553}
Michal Vasko4231fb62020-07-13 13:54:47 +02001554
1555static LY_ERR
1556lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
1557{
1558 LY_ERR ret = LY_SUCCESS;
1559 struct lyd_meta *meta;
1560 const char *val1 = NULL, *val2 = NULL;
1561 int dyn1 = 0, dyn2 = 0, flags;
1562
1563 meta = lyd_find_meta(leaf->meta, mod, "orig-value");
1564 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(leaf)), LY_EINT);
1565
1566 /* orig-value */
1567 val1 = lyd_meta2str(meta, &dyn1);
1568
1569 /* current value */
1570 val2 = lyd_value2str((struct lyd_node_term *)leaf, &dyn2);
1571
1572 /* switch values, keep default flag */
1573 flags = leaf->flags;
1574 LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
1575 leaf->flags = flags;
1576 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1577
1578cleanup:
1579 if (dyn1) {
1580 free((char *)val1);
1581 }
1582 if (dyn2) {
1583 free((char *)val2);
1584 }
1585 return ret;
1586}
1587
1588static LY_ERR
1589lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1590{
1591 struct lyd_meta *meta;
1592 const char *val;
1593 int dyn, flag1, flag2;
1594
1595 meta = lyd_find_meta(node->meta, mod, "orig-default");
1596 if (!meta) {
1597 /* default flag did not change */
1598 return LY_SUCCESS;
1599 }
1600
1601 /* orig-default */
1602 val = lyd_meta2str(meta, &dyn);
1603 assert(!dyn);
1604 if (!strcmp(val, "true")) {
1605 flag1 = LYD_DEFAULT;
1606 } else {
1607 flag1 = 0;
1608 }
1609
1610 /* current default */
1611 flag2 = node->flags & LYD_DEFAULT;
1612
1613 /* switch defaults */
1614 node->flags &= ~LYD_DEFAULT;
1615 node->flags |= flag1;
1616 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1617
1618 return LY_SUCCESS;
1619}
1620
1621static LY_ERR
1622lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1623{
1624 LY_ERR ret = LY_SUCCESS;
1625 struct lyd_meta *meta1, *meta2;
1626 const char *val1 = NULL, *val2 = NULL;
1627 int dyn1 = 0, dyn2 = 0;
1628
1629 meta1 = lyd_find_meta(node->meta, mod, name1);
1630 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1631
1632 meta2 = lyd_find_meta(node->meta, mod, name2);
1633 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
1634
1635 /* value1 */
1636 val1 = lyd_meta2str(meta1, &dyn1);
1637
1638 /* value2 */
1639 val2 = lyd_meta2str(meta2, &dyn2);
1640
1641 /* switch values */
1642 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1643 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1644
1645cleanup:
1646 if (dyn1) {
1647 free((char *)val1);
1648 }
1649 if (dyn2) {
1650 free((char *)val2);
1651 }
1652 return ret;
1653}
1654
1655API LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001656lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001657{
1658 LY_ERR ret = LY_SUCCESS;
1659 const struct lys_module *mod;
Michal Vasko56daf732020-08-10 10:57:18 +02001660 struct lyd_node *root, *elem;
Michal Vasko4231fb62020-07-13 13:54:47 +02001661 enum lyd_diff_op op;
1662
1663 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1664
1665 if (!src_diff) {
1666 *diff = NULL;
1667 return LY_SUCCESS;
1668 }
1669
1670 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001671 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001672
1673 /* find module with metadata needed for later */
1674 mod = ly_ctx_get_module_latest(LYD_NODE_CTX(src_diff), "yang");
1675 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_NODE_CTX(src_diff)); ret = LY_EINT, cleanup);
1676
1677 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001678 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001679 /* find operation attribute, if any */
1680 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
1681
1682 switch (op) {
1683 case LYD_DIFF_OP_CREATE:
1684 /* reverse create to delete */
1685 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
1686 break;
1687 case LYD_DIFF_OP_DELETE:
1688 /* reverse delete to create */
1689 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
1690 break;
1691 case LYD_DIFF_OP_REPLACE:
1692 switch (elem->schema->nodetype) {
1693 case LYS_LEAF:
1694 /* leaf value change */
1695 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1696 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1697 break;
1698 case LYS_LEAFLIST:
1699 /* leaf-list move */
1700 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1701 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1702 break;
1703 case LYS_LIST:
1704 /* list move */
1705 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1706 break;
1707 default:
1708 LOGINT(LYD_NODE_CTX(src_diff));
1709 ret = LY_EINT;
1710 goto cleanup;
1711 }
1712 break;
1713 case LYD_DIFF_OP_NONE:
1714 switch (elem->schema->nodetype) {
1715 case LYS_LEAF:
1716 case LYS_LEAFLIST:
1717 /* default flag change */
1718 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1719 break;
1720 default:
1721 /* nothing to do */
1722 break;
1723 }
1724 break;
1725 default:
1726 /* nothing to do */
1727 break;
1728 }
1729
Michal Vasko56daf732020-08-10 10:57:18 +02001730 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02001731 }
1732 }
1733
1734cleanup:
1735 if (ret) {
1736 lyd_free_siblings(*diff);
1737 *diff = NULL;
1738 }
1739 return ret;
1740}