blob: bdbad694dbcbdb2c4236bd00fcdc7b06e9342b6f [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 */
Radek Krejcif8dc59a2020-11-25 13:47:44 +010014#define _POSIX_C_SOURCE 200809L /* strdup */
Michal Vaskod59035b2020-07-08 12:00:06 +020015
16#include "diff.h"
17
18#include <assert.h>
19#include <stddef.h>
Radek Krejci47fab892020-11-05 17:02:41 +010020#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020021#include <string.h>
22
23#include "common.h"
Radek Krejci47fab892020-11-05 17:02:41 +010024#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020025#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010026#include "plugins_types.h"
27#include "set.h"
28#include "tree.h"
29#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020030#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010031#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020032#include "tree_schema.h"
33#include "tree_schema_internal.h"
34
35static const char *
36lyd_diff_op2str(enum lyd_diff_op op)
37{
38 switch (op) {
39 case LYD_DIFF_OP_CREATE:
40 return "create";
41 case LYD_DIFF_OP_DELETE:
42 return "delete";
43 case LYD_DIFF_OP_REPLACE:
44 return "replace";
45 case LYD_DIFF_OP_NONE:
46 return "none";
47 }
48
49 LOGINT(NULL);
50 return NULL;
51}
52
Michal Vaskoe6323f62020-07-09 15:49:02 +020053static enum lyd_diff_op
54lyd_diff_str2op(const char *str)
55{
56 switch (str[0]) {
57 case 'c':
58 assert(!strcmp(str, "create"));
59 return LYD_DIFF_OP_CREATE;
60 case 'd':
61 assert(!strcmp(str, "delete"));
62 return LYD_DIFF_OP_DELETE;
63 case 'r':
64 assert(!strcmp(str, "replace"));
65 return LYD_DIFF_OP_REPLACE;
66 case 'n':
67 assert(!strcmp(str, "none"));
68 return LYD_DIFF_OP_NONE;
69 }
70
71 LOGINT(NULL);
72 return 0;
73}
74
Michal Vaskod59035b2020-07-08 12:00:06 +020075LY_ERR
76lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
Radek Krejci0f969882020-08-21 16:56:47 +020077 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +020078{
79 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
80 const struct lyd_node *parent = NULL;
81 const struct lys_module *yang_mod;
82
83 assert(diff);
84
Michal Vasko53d48422020-11-13 18:02:29 +010085 /* replace leaf always needs orig-default and orig-value */
86 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
87
88 /* create on userord needs key/value */
89 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
90 key);
91 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
92 (op != LYD_DIFF_OP_CREATE) || value);
93
94 /* move on userord needs both key and orig-key/value and orig-value */
95 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
96 (key && orig_key));
97 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
98 (op != LYD_DIFF_OP_REPLACE) || (value && orig_value));
99
Michal Vaskod59035b2020-07-08 12:00:06 +0200100 /* find the first existing parent */
101 siblings = *diff;
102 while (1) {
103 /* find next node parent */
104 parent = node;
105 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100106 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200107 }
108 if (parent == node) {
109 /* no more parents to find */
110 break;
111 }
112
113 /* check whether it exists in the diff */
114 if (lyd_find_sibling_first(siblings, parent, &match)) {
115 break;
116 }
117
118 /* another parent found */
119 diff_parent = match;
120
121 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200122 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200123 }
124
125 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200126 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100127 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200128
129 /* find the first duplicated parent */
130 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100131 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200132 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100133 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200134 }
135 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100136 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200137 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100138 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200139 }
140 }
141
142 /* no parent existed, must be manually connected */
143 if (!diff_parent) {
144 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200145 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200146 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200147 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200148 }
149
150 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200151 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200152 assert(!strcmp(yang_mod->name, "yang"));
153
154 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200155 if (diff_parent && (diff_parent != dup)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100156 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200157 }
158
159 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100160 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200161
162 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200163 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100164 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200165 }
166
167 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200168 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100169 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200170 }
171
172 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200173 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100174 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200175 }
176
177 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200178 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100179 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200180 }
181
182 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200183 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100184 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200185 }
186
187 return LY_SUCCESS;
188}
189
190/**
191 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
192 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100193 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200194 * @param[in] schema Schema node of the list/leaf-list.
195 * @param[in,out] userord Sized array of userord items.
196 * @return Userord item for all the user-ordered list/leaf-list instances.
197 */
198static struct lyd_diff_userord *
199lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
200{
201 struct lyd_diff_userord *item;
202 const struct lyd_node *iter, **node;
203 LY_ARRAY_COUNT_TYPE u;
204
205 LY_ARRAY_FOR(*userord, u) {
206 if ((*userord)[u].schema == schema) {
207 return &(*userord)[u];
208 }
209 }
210
211 /* it was not added yet, add it now */
212 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
213
214 item->schema = schema;
215 item->pos = 0;
216 item->inst = NULL;
217
218 /* store all the instance pointers in the current order */
219 if (first) {
220 if (first->parent) {
221 iter = first->parent->child;
222 } else {
Radek Krejci1e008d22020-08-17 11:37:37 +0200223 for (iter = first; iter->prev->next; iter = iter->prev) {}
Michal Vaskod59035b2020-07-08 12:00:06 +0200224 }
Michal Vaskod989ba02020-08-24 10:59:24 +0200225 for ( ; iter; iter = iter->next) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200226 if (iter->schema == first->schema) {
227 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
228 *node = iter;
229 }
230 }
231 }
232
233 return item;
234}
235
236/**
237 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
238 * lists/leaf-lists.
239 *
240 * @param[in] first Node from the first tree, can be NULL (on create).
241 * @param[in] second Node from the second tree, can be NULL (on delete).
242 * @param[in] options Diff options.
243 * @param[in,out] userord Sized array of userord items for keeping the current node order.
244 * @param[out] op Operation.
245 * @param[out] orig_default Original default metadata.
246 * @param[out] value Value metadata.
247 * @param[out] orig_value Original value metadata
248 * @param[out] key Key metadata.
249 * @param[out] orig_key Original key metadata.
250 * @return LY_SUCCESS on success,
251 * @return LY_ENOT if there is no change to be added into diff,
252 * @return LY_ERR value on other errors.
253 */
254static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200255lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Radek Krejci0f969882020-08-21 16:56:47 +0200256 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
257 char **orig_value, char **key, char **orig_key)
Michal Vaskod59035b2020-07-08 12:00:06 +0200258{
259 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200260 size_t buflen, bufused, first_pos, second_pos;
261 struct lyd_diff_userord *userord_item;
262
263 assert(first || second);
264
265 *orig_default = NULL;
266 *value = NULL;
267 *orig_value = NULL;
268 *key = NULL;
269 *orig_key = NULL;
270
271 schema = first ? first->schema : second->schema;
272 assert(lysc_is_userordered(schema));
273
274 /* get userord entry */
275 userord_item = lyd_diff_userord_get(first, schema, userord);
276 LY_CHECK_RET(!userord_item, LY_EMEM);
277
278 /* prepare position of the next instance */
279 second_pos = userord_item->pos++;
280
281 /* find user-ordered first position */
282 if (first) {
283 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
284 if (userord_item->inst[first_pos] == first) {
285 break;
286 }
287 }
288 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
289 } else {
290 first_pos = 0;
291 }
292
293 /* learn operation first */
294 if (!second) {
295 *op = LYD_DIFF_OP_DELETE;
296 } else if (!first) {
297 *op = LYD_DIFF_OP_CREATE;
298 } else {
299 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vasko8f359bf2020-07-28 10:41:15 +0200300 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200301 /* in first, there is a different instance on the second position, we are going to move 'first' node */
302 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200303 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200304 /* default flag change */
305 *op = LYD_DIFF_OP_NONE;
306 } else {
307 /* no changes */
308 return LY_ENOT;
309 }
310 }
311
312 /*
313 * set each attribute correctly based on the operation and node type
314 */
315
316 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100317 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200318 if (first->flags & LYD_DEFAULT) {
319 *orig_default = "true";
320 } else {
321 *orig_default = "false";
322 }
323 }
324
325 /* value */
326 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
327 if (second_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200328 *value = strdup(LYD_CANON_VALUE(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200329 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200330 } else {
331 *value = strdup("");
332 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
333 }
334 }
335
336 /* orig-value */
337 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
338 if (first_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200339 *orig_value = strdup(LYD_CANON_VALUE(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200340 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200341 } else {
342 *orig_value = strdup("");
343 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
344 }
345 }
346
347 /* key */
Michal Vasko44f3d2c2020-08-24 09:49:38 +0200348 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200349 if (second_pos) {
350 buflen = bufused = 0;
351 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
352 } else {
353 *key = strdup("");
354 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
355 }
356 }
357
358 /* orig-key */
359 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
360 if (first_pos) {
361 buflen = bufused = 0;
362 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
363 } else {
364 *orig_key = strdup("");
365 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
366 }
367 }
368
369 /*
370 * update our instances - apply the change
371 */
372 if (*op == LYD_DIFF_OP_CREATE) {
373 /* insert the instance */
Michal Vasko5cde11b2020-12-08 10:04:48 +0100374 LY_ARRAY_CREATE_RET(schema->module->ctx, userord_item->inst, 1, LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200375 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
376 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
377 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
378 }
379 LY_ARRAY_INCREMENT(userord_item->inst);
380 userord_item->inst[second_pos] = second;
381
382 } else if (*op == LYD_DIFF_OP_DELETE) {
383 /* remove the instance */
384 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
385 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
386 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
387 }
388 LY_ARRAY_DECREMENT(userord_item->inst);
389
390 } else if (*op == LYD_DIFF_OP_REPLACE) {
391 /* move the instances */
392 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
393 (first_pos - second_pos) * sizeof *userord_item->inst);
394 userord_item->inst[second_pos] = first;
395 }
396
397 return LY_SUCCESS;
398}
399
400/**
401 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
402 * lists/leaf-lists.
403 *
404 * @param[in] first Node from the first tree, can be NULL (on create).
405 * @param[in] second Node from the second tree, can be NULL (on delete).
406 * @param[in] options Diff options.
407 * @param[out] op Operation.
408 * @param[out] orig_default Original default metadata.
409 * @param[out] orig_value Original value metadata.
410 * @return LY_SUCCESS on success,
411 * @return LY_ENOT if there is no change to be added into diff,
412 * @return LY_ERR value on other errors.
413 */
414static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200415lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, enum lyd_diff_op *op,
Radek Krejci0f969882020-08-21 16:56:47 +0200416 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200417{
418 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200419
420 assert(first || second);
421
422 *orig_default = NULL;
423 *orig_value = NULL;
424
425 schema = first ? first->schema : second->schema;
426 assert(!lysc_is_userordered(schema));
427
428 /* learn operation first */
429 if (!second) {
430 *op = LYD_DIFF_OP_DELETE;
431 } else if (!first) {
432 *op = LYD_DIFF_OP_CREATE;
433 } else {
434 switch (schema->nodetype) {
435 case LYS_CONTAINER:
436 case LYS_RPC:
437 case LYS_ACTION:
438 case LYS_NOTIF:
439 /* no changes */
440 return LY_ENOT;
441 case LYS_LIST:
442 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200443 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200444 /* default flag change */
445 *op = LYD_DIFF_OP_NONE;
446 } else {
447 /* no changes */
448 return LY_ENOT;
449 }
450 break;
451 case LYS_LEAF:
452 case LYS_ANYXML:
453 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200454 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200455 /* different values */
456 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200457 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200458 /* default flag change */
459 *op = LYD_DIFF_OP_NONE;
460 } else {
461 /* no changes */
462 return LY_ENOT;
463 }
464 break;
465 default:
466 LOGINT_RET(schema->module->ctx);
467 }
468 }
469
470 /*
471 * set each attribute correctly based on the operation and node type
472 */
473
474 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100475 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200476 if (first->flags & LYD_DEFAULT) {
477 *orig_default = "true";
478 } else {
479 *orig_default = "false";
480 }
481 }
482
483 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100484 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
485 if (schema->nodetype == LYS_LEAF) {
486 *orig_value = strdup(LYD_CANON_VALUE(first));
487 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
488 } else {
489 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
490 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200491 }
492
493 return LY_SUCCESS;
494}
495
496/**
497 * @brief Perform diff for all siblings at certain depth, recursively.
498 *
499 * For user-ordered lists/leaf-lists a specific structure is used for storing
500 * the current order. The idea is to apply all the generated diff changes
501 * virtually on the first tree so that we can continue to generate correct
502 * changes after some were already generated.
503 *
504 * The algorithm then uses second tree position-based changes with a before
505 * (preceding) item anchor.
506 *
507 * Example:
508 *
509 * Virtual first tree leaf-list order:
510 * 1 2 [3] 4 5
511 *
512 * Second tree leaf-list order:
513 * 1 2 [5] 3 4
514 *
515 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
516 * match - they do not - move nodes so that the 3rd position node is final ->
517 * -> move node 5 to the 3rd position -> move node 5 after node 2.
518 *
519 * Required properties:
520 * Stored operations (move) should not be affected by later operations -
521 * - would cause a redundantly long list of operations, possibly inifinite.
522 *
523 * Implemenation justification:
524 * First, all delete operations and only then move/create operations are stored.
525 * Also, preceding anchor is used and after each iteration another node is
526 * at its final position. That results in the invariant that all preceding
527 * nodes are final and will not be changed by the later operations, meaning
528 * they can safely be used as anchors for the later operations.
529 *
530 * @param[in] first First tree first sibling.
531 * @param[in] second Second tree first sibling.
532 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200533 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200534 * @param[in,out] diff Diff to append to.
535 * @return LY_ERR value.
536 */
537static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200538lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
Radek Krejci0f969882020-08-21 16:56:47 +0200539 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200540{
541 LY_ERR ret = LY_SUCCESS;
542 const struct lyd_node *iter_first, *iter_second;
543 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200544 struct lyd_diff_userord *userord = NULL;
545 LY_ARRAY_COUNT_TYPE u;
546 enum lyd_diff_op op;
547 const char *orig_default;
548 char *orig_value, *key, *value, *orig_key;
549
Michal Vaskod59035b2020-07-08 12:00:06 +0200550 /* compare first tree to the second tree - delete, replace, none */
551 LY_LIST_FOR(first, iter_first) {
552 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200553 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200554 /* skip default nodes */
555 continue;
556 }
557
558 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
559 /* try to find the exact instance */
560 lyd_find_sibling_first(second, iter_first, &match_second);
561 } else {
562 /* try to simply find the node, there cannot be more instances */
563 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
564 }
565
Michal Vasko3a41dff2020-07-15 14:30:28 +0200566 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200567 /* ignore default nodes */
568 match_second = NULL;
569 }
570
571 if (lysc_is_userordered(iter_first->schema)) {
572 if (match_second) {
573 /* we are handling only user-ordered node delete now */
574 continue;
575 }
576
577 /* get all the attributes */
578 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200579 &value, &orig_value, &key, &orig_key), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200580
581 /* there must be changes, it is deleted */
582 assert(op == LYD_DIFF_OP_DELETE);
583 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
584
585 free(orig_value);
586 free(key);
587 free(value);
588 free(orig_key);
589 LY_CHECK_GOTO(ret, cleanup);
590 } else {
591 /* get all the attributes */
592 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
593
594 /* add into diff if there are any changes */
595 if (!ret) {
596 if (op == LYD_DIFF_OP_DELETE) {
597 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
598 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100599 assert(match_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200600 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
601 }
602
603 free(orig_value);
604 LY_CHECK_GOTO(ret, cleanup);
605 } else if (ret == LY_ENOT) {
606 ret = LY_SUCCESS;
607 } else {
608 goto cleanup;
609 }
610 }
611
612 /* check descendants, if any, recursively */
613 if (match_second) {
Radek Krejcia1c1e542020-09-29 16:06:52 +0200614 LY_CHECK_GOTO(lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second), options,
Michal Vasko69730152020-10-09 16:30:07 +0200615 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200616 }
617
618 if (nosiblings) {
619 break;
620 }
621 }
622
623 /* reset all cached positions */
624 LY_ARRAY_FOR(userord, u) {
625 userord[u].pos = 0;
626 }
627
628 /* compare second tree to the first tree - create, user-ordered move */
629 LY_LIST_FOR(second, iter_second) {
630 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200631 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200632 /* skip default nodes */
633 continue;
634 }
635
636 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
637 lyd_find_sibling_first(first, iter_second, &match_first);
638 } else {
639 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
640 }
641
Michal Vasko3a41dff2020-07-15 14:30:28 +0200642 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200643 /* ignore default nodes */
644 match_first = NULL;
645 }
646
647 if (lysc_is_userordered(iter_second->schema)) {
648 /* get all the attributes */
649 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200650 &value, &orig_value, &key, &orig_key);
Michal Vaskod59035b2020-07-08 12:00:06 +0200651
652 /* add into diff if there are any changes */
653 if (!ret) {
654 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
655
656 free(orig_value);
657 free(key);
658 free(value);
659 free(orig_key);
660 LY_CHECK_GOTO(ret, cleanup);
661 } else if (ret == LY_ENOT) {
662 ret = LY_SUCCESS;
663 } else {
664 goto cleanup;
665 }
666 } else if (!match_first) {
667 /* get all the attributes */
668 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
669
670 /* there must be changes, it is created */
671 assert(op == LYD_DIFF_OP_CREATE);
672 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
673
674 free(orig_value);
675 LY_CHECK_GOTO(ret, cleanup);
676 } /* else was handled */
677
678 if (nosiblings) {
679 break;
680 }
681 }
682
683cleanup:
684 LY_ARRAY_FOR(userord, u) {
685 LY_ARRAY_FREE(userord[u].inst);
686 }
687 LY_ARRAY_FREE(userord);
688 return ret;
689}
690
Michal Vasko3a41dff2020-07-15 14:30:28 +0200691static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200692lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200693{
694 const struct ly_ctx *ctx;
695
696 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
697
698 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200699 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200700 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200701 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200702 } else {
703 ctx = NULL;
704 }
705
706 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
707 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
708 return LY_EINVAL;
709 }
710
711 *diff = NULL;
712
Michal Vasko3a41dff2020-07-15 14:30:28 +0200713 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
714}
715
716API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200717lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff)
Michal Vasko3a41dff2020-07-15 14:30:28 +0200718{
719 return lyd_diff(first, second, options, 1, diff);
720}
721
722API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200723lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff)
Michal Vasko3a41dff2020-07-15 14:30:28 +0200724{
725 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200726}
727
728/**
729 * @brief Find a matching node in data tree for a diff node.
730 *
731 * @param[in] first_node First sibling in the data tree.
732 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200733 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200734 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200735static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200736lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
737{
738 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
739 /* try to find the exact instance */
740 lyd_find_sibling_first(first_node, diff_node, match_p);
741 } else {
742 /* try to simply find the node, there cannot be more instances */
743 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
744 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200745}
746
747/**
748 * @brief Learn operation of a diff node.
749 *
750 * @param[in] diff_node Diff node.
751 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200752 * @return LY_ERR value.
753 */
754static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200755lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200756{
757 struct lyd_meta *meta = NULL;
758 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200759 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200760
Michal Vasko9e685082021-01-29 14:49:09 +0100761 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200762 LY_LIST_FOR(diff_parent->meta, meta) {
763 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200764 str = meta->value.canonical;
Michal Vaskod59035b2020-07-08 12:00:06 +0200765 if ((str[0] == 'r') && (diff_parent != diff_node)) {
766 /* we do not care about this operation if it's in our parent */
767 continue;
768 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200769 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200770 break;
771 }
772 }
773 if (meta) {
774 break;
775 }
776 }
Michal Vaskob7be7a82020-08-20 09:09:04 +0200777 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200778
Michal Vaskod59035b2020-07-08 12:00:06 +0200779 return LY_SUCCESS;
780}
781
782/**
783 * @brief Insert a diff node into a data tree.
784 *
785 * @param[in,out] first_node First sibling of the data tree.
786 * @param[in] parent_node Data tree sibling parent node.
787 * @param[in] new_node Node to insert.
788 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
789 * instance will be inserted at the first position.
790 * @return err_info, NULL on success.
791 */
792static LY_ERR
793lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200794 const char *key_or_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200795{
796 LY_ERR ret;
797 struct lyd_node *anchor;
798
799 assert(new_node);
800
801 if (!*first_node) {
802 if (!parent_node) {
803 /* no parent or siblings */
804 *first_node = new_node;
805 return LY_SUCCESS;
806 }
807
808 /* simply insert into parent, no other children */
809 if (key_or_value) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200810 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200811 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200812 return LY_EINVAL;
813 }
Michal Vaskob104f112020-07-17 09:54:54 +0200814 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200815 }
816
Michal Vasko9e685082021-01-29 14:49:09 +0100817 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200818
Michal Vaskod59035b2020-07-08 12:00:06 +0200819 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200820 /* simple insert */
821 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200822 }
823
824 if (key_or_value) {
825 /* find the anchor sibling */
826 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
827 if (ret == LY_ENOTFOUND) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200828 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200829 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200830 return LY_EINVAL;
831 } else if (ret) {
832 return ret;
833 }
834
835 /* insert after */
836 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
837 assert(new_node->prev == anchor);
838 if (*first_node == new_node) {
839 *first_node = anchor;
840 }
841 } else {
842 if ((*first_node)->schema->flags & LYS_KEY) {
843 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
844
845 /* find last key */
846 anchor = *first_node;
847 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
848 anchor = anchor->next;
849 }
850 /* insert after the last key */
851 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
852 } else {
853 /* insert at the beginning */
854 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
855 *first_node = new_node;
856 }
857 }
858
859 return LY_SUCCESS;
860}
861
862/**
863 * @brief Apply diff subtree on data tree nodes, recursively.
864 *
865 * @param[in,out] first_node First sibling of the data tree.
866 * @param[in] parent_node Parent of the first sibling.
867 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200868 * @param[in] diff_cb Optional diff callback.
869 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200870 * @return LY_ERR value.
871 */
872static LY_ERR
873lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200874 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +0200875{
876 LY_ERR ret;
877 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200878 const char *str_val;
879 enum lyd_diff_op op;
880 struct lyd_meta *meta;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200881 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200882
883 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200884 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200885
Michal Vaskoe6323f62020-07-09 15:49:02 +0200886 /* handle specific user-ordered (leaf-)lists operations separately */
887 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
888 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200889 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200890 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100891 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200892 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200893 /* duplicate the node */
894 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200895 }
896
Michal Vaskoe6323f62020-07-09 15:49:02 +0200897 /* get "key" or "value" metadata string value */
898 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
Michal Vaskob7be7a82020-08-20 09:09:04 +0200899 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200900 str_val = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200901
Michal Vaskod59035b2020-07-08 12:00:06 +0200902 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200903 if (str_val[0]) {
904 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200905 } else {
906 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
907 }
908 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200909 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200910 lyd_free_tree(match);
911 }
912 return ret;
913 }
914
915 goto next_iter_r;
916 }
917
918 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200919 switch (op) {
920 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200921 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200922 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100923 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200924
925 if (match->schema->nodetype & LYD_NODE_TERM) {
926 /* special case of only dflt flag change */
927 if (diff_node->flags & LYD_DEFAULT) {
928 match->flags |= LYD_DEFAULT;
929 } else {
930 match->flags &= ~LYD_DEFAULT;
931 }
932 } else {
933 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200934 if (!lyd_child_no_keys(diff_node)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200935 LOGINT_RET(ctx);
936 }
937 }
938 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200939 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200940 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200941 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200942
943 /* insert it at the end */
944 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200945 if (parent_node) {
946 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200947 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200948 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200949 }
950 if (ret) {
951 lyd_free_tree(match);
952 return ret;
953 }
954
955 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200956 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200957 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200958 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100959 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200960
961 /* remove it */
962 if ((match == *first_node) && !match->parent) {
963 assert(!parent_node);
964 /* we have removed the top-level node */
965 *first_node = (*first_node)->next;
966 }
967 lyd_free_tree(match);
968
969 /* we are not going recursively in this case, the whole subtree was already deleted */
970 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200971 case LYD_DIFF_OP_REPLACE:
Michal Vaskobaba84e2021-02-05 16:33:30 +0100972 LY_CHECK_ERR_RET(!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA)), LOGINT(ctx), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200973
974 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200975 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskoe8022002020-12-03 14:10:14 +0100976 LY_CHECK_ERR_RET(!match, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200977
Michal Vaskobaba84e2021-02-05 16:33:30 +0100978 /* update the value */
979 if (diff_node->schema->nodetype == LYS_LEAF) {
980 ret = lyd_change_term(match, LYD_CANON_VALUE(diff_node));
981 if (ret && (ret != LY_EEXIST)) {
982 LOGINT_RET(ctx);
983 }
984 } else {
985 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
986 ret = lyd_any_copy_value(match, &any->value, any->value_type);
987 LY_CHECK_RET(ret);
Michal Vaskod59035b2020-07-08 12:00:06 +0200988 }
989
990 /* with flags */
991 match->flags = diff_node->flags;
992 break;
993 default:
994 LOGINT_RET(ctx);
995 }
996
997next_iter_r:
998 if (diff_cb) {
999 /* call callback */
1000 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1001 }
1002
1003 /* apply diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001004 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe0665742021-02-11 11:08:44 +01001005 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data));
Michal Vaskod59035b2020-07-08 12:00:06 +02001006 }
1007
1008 return LY_SUCCESS;
1009}
1010
1011API LY_ERR
1012lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001013 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001014{
1015 const struct lyd_node *root;
1016
1017 LY_LIST_FOR(diff, root) {
1018 if (mod && (lyd_owner_module(root) != mod)) {
1019 /* skip data nodes from different modules */
1020 continue;
1021 }
1022
1023 /* apply relevant nodes from the diff datatree */
1024 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1025 }
1026
1027 return LY_SUCCESS;
1028}
1029
1030API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001031lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001032{
1033 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1034}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001035
1036/**
1037 * @brief Update operations on a diff node when the new operation is NONE.
1038 *
1039 * @param[in] diff_match Node from the diff.
1040 * @param[in] cur_op Current operation of the diff node.
1041 * @param[in] src_diff Current source diff node.
1042 * @return LY_ERR value.
1043 */
1044static LY_ERR
1045lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1046{
1047 switch (cur_op) {
1048 case LYD_DIFF_OP_NONE:
1049 case LYD_DIFF_OP_CREATE:
1050 case LYD_DIFF_OP_REPLACE:
1051 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1052 /* NONE on a term means only its dflt flag was changed */
1053 diff_match->flags &= ~LYD_DEFAULT;
1054 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1055 }
1056 break;
1057 default:
1058 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001059 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001060 }
1061
1062 return LY_SUCCESS;
1063}
1064
1065/**
1066 * @brief Remove an attribute from a node.
1067 *
1068 * @param[in] node Node with the metadata.
1069 * @param[in] name Metadata name.
1070 */
1071static void
1072lyd_diff_del_meta(struct lyd_node *node, const char *name)
1073{
1074 struct lyd_meta *meta;
1075
1076 LY_LIST_FOR(node->meta, meta) {
1077 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001078 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001079 return;
1080 }
1081 }
1082
1083 assert(0);
1084}
1085
1086/**
1087 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001088 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001089 *
1090 * @param[in] node Node to change.
1091 * @param[in] op Operation to set.
1092 * @return LY_ERR value.
1093 */
1094static LY_ERR
1095lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1096{
1097 struct lyd_meta *meta;
1098
1099 LY_LIST_FOR(node->meta, meta) {
1100 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001101 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001102 break;
1103 }
1104 }
1105
Michal Vasko871a0252020-11-11 18:35:24 +01001106 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001107}
1108
1109/**
1110 * @brief Update operations on a diff node when the new operation is REPLACE.
1111 *
1112 * @param[in] diff_match Node from the diff.
1113 * @param[in] cur_op Current operation of the diff node.
1114 * @param[in] src_diff Current source diff node.
1115 * @return LY_ERR value.
1116 */
1117static LY_ERR
1118lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1119{
1120 LY_ERR ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001121 const char *str_val, *meta_name;
1122 struct lyd_meta *meta;
1123 const struct lys_module *mod;
1124 const struct lyd_node_any *any;
1125
1126 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001127 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001128 assert(mod);
1129
1130 switch (cur_op) {
1131 case LYD_DIFF_OP_REPLACE:
1132 case LYD_DIFF_OP_CREATE:
1133 switch (diff_match->schema->nodetype) {
1134 case LYS_LIST:
1135 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001136 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001137 * keep orig_key/orig_value (only replace oper) and replace key/value */
1138 assert(lysc_is_userordered(diff_match->schema));
1139 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1140
1141 lyd_diff_del_meta(diff_match, meta_name);
1142 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001143 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001144 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001145 break;
1146 case LYS_LEAF:
1147 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001148 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001149 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001150 }
1151
Michal Vaskoe6323f62020-07-09 15:49:02 +02001152 /* modify the node value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001153 if (lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff))) {
1154 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001155 }
1156
Michal Vasko8caadab2020-11-05 17:38:15 +01001157 if (cur_op == LYD_DIFF_OP_REPLACE) {
1158 /* compare values whether there is any change at all */
1159 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1160 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT);
1161 str_val = meta->value.canonical;
1162 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1163 if (!ret) {
1164 /* values are the same, remove orig-value meta and set oper to NONE */
1165 lyd_free_meta_single(meta);
1166 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1167 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001168 }
1169
1170 /* modify the default flag */
1171 diff_match->flags &= ~LYD_DEFAULT;
1172 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1173 break;
1174 case LYS_ANYXML:
1175 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001176 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001177 /* replaced with the exact same value, impossible */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001178 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001179 }
1180
1181 /* modify the node value */
1182 any = (struct lyd_node_any *)src_diff;
1183 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1184 break;
1185 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001186 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001187 }
1188 break;
1189 case LYD_DIFF_OP_NONE:
1190 /* it is moved now */
1191 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1192
1193 /* change the operation */
1194 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1195
1196 /* set orig-key and key metadata */
1197 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001198 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001199 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001200
1201 meta = lyd_find_meta(src_diff->meta, mod, "key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001202 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001203 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001204 break;
1205 default:
1206 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001207 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001208 }
1209
1210 return LY_SUCCESS;
1211}
1212
1213/**
1214 * @brief Update operations in a diff node when the new operation is CREATE.
1215 *
1216 * @param[in] diff_match Node from the diff.
1217 * @param[in] cur_op Current operation of the diff node.
1218 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001219 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001220 * @return LY_ERR value.
1221 */
1222static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001223lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001224{
1225 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001226 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001227 uint32_t trg_flags;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001228
1229 switch (cur_op) {
1230 case LYD_DIFF_OP_DELETE:
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001231 if ((options & LYD_DIFF_MERGE_DEFAULTS) && (diff_match->schema->nodetype == LYS_LEAF)) {
1232 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
Michal Vasko5632e0d2020-07-31 14:13:37 +02001233 sleaf = (struct lysc_node_leaf *)diff_match->schema;
Michal Vasko5632e0d2020-07-31 14:13:37 +02001234 }
1235
Michal Vasko871a0252020-11-11 18:35:24 +01001236 /* remember current flags */
1237 trg_flags = diff_match->flags;
1238
Michal Vasko69730152020-10-09 16:30:07 +02001239 if (sleaf && sleaf->dflt &&
1240 !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, &((struct lyd_node_term *)src_diff)->value)) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001241 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1242 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001243 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001244 /* deleted + created -> operation NONE */
1245 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001246 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001247 /* we deleted it, but it was created with a different value -> operation REPLACE */
1248 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1249
1250 /* current value is the previous one (meta) */
Michal Vasko871a0252020-11-11 18:35:24 +01001251 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
1252 LYD_CANON_VALUE(diff_match), 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001253
1254 /* update the value itself */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001255 LY_CHECK_RET(lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff)));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001256 }
1257
1258 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001259 /* add orig-dflt metadata */
1260 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1261 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1262
Michal Vaskoe6323f62020-07-09 15:49:02 +02001263 /* update dflt flag itself */
1264 diff_match->flags &= ~LYD_DEFAULT;
1265 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1266 } else {
1267 /* but the operation of its children should remain DELETE */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001268 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001269 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1270 }
1271 }
1272 break;
1273 default:
1274 /* create and replace operations are not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001275 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001276 }
1277
1278 return LY_SUCCESS;
1279}
1280
1281/**
1282 * @brief Update operations on a diff node when the new operation is DELETE.
1283 *
1284 * @param[in] diff_match Node from the diff.
1285 * @param[in] cur_op Current operation of the diff node.
1286 * @param[in] src_diff Current source diff node.
1287 * @return LY_ERR value.
1288 */
1289static LY_ERR
1290lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1291{
1292 struct lyd_node *next, *child;
1293
1294 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001295 LY_CHECK_ERR_RET(lyd_compare_single(diff_match, src_diff, 0), LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001296
1297 switch (cur_op) {
1298 case LYD_DIFF_OP_CREATE:
1299 /* it was created, but then deleted -> set NONE operation */
1300 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1301
1302 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1303 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001304 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1305 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001306 } else {
1307 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001308 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001309 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1310 }
1311 }
1312 break;
1313 case LYD_DIFF_OP_REPLACE:
1314 /* similar to none operation but also remove the redundant attribute */
1315 lyd_diff_del_meta(diff_match, "orig-value");
Radek Krejcif13b87b2020-12-01 22:02:17 +01001316 /* fall through */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001317 case LYD_DIFF_OP_NONE:
1318 /* it was not modified, but should be deleted -> set DELETE operation */
1319 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1320
Michal Vasko5632e0d2020-07-31 14:13:37 +02001321 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001322 LY_LIST_FOR_SAFE(lyd_child_no_keys(diff_match), next, child) {
1323 if (lyd_find_sibling_first(lyd_child(src_diff), child, NULL) == LY_ENOTFOUND) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001324 lyd_free_tree(child);
1325 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001326 }
1327 break;
1328 default:
1329 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001330 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001331 }
1332
1333 return LY_SUCCESS;
1334}
1335
1336/**
1337 * @brief Check whether this diff node is redundant (does not change data).
1338 *
1339 * @param[in] diff Diff node.
1340 * @return 0 if not, non-zero if it is.
1341 */
1342static int
1343lyd_diff_is_redundant(struct lyd_node *diff)
1344{
1345 enum lyd_diff_op op;
1346 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1347 struct lyd_node *child;
1348 const struct lys_module *mod;
1349 const char *str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001350
1351 assert(diff);
1352
Radek Krejcia1c1e542020-09-29 16:06:52 +02001353 child = lyd_child_no_keys(diff);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001354 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001355 assert(mod);
1356
1357 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001358 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001359
1360 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1361 /* check for redundant move */
1362 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1363 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1364 assert(orig_val_meta && val_meta);
1365
1366 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1367 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001368 lyd_free_meta_single(orig_val_meta);
1369 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001370 if (child) {
1371 /* change operation to NONE, we have siblings */
1372 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1373 return 0;
1374 }
1375
1376 /* redundant node, BUT !!
1377 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1378 * because the data that this is applied on should not change for the diff lifetime.
1379 * However, when we are merging 2 diffs, this conversion is actually lossy because
1380 * if the data change, the move operation can also change its meaning. In this specific
1381 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1382 */
1383 return 1;
1384 }
1385 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1386 /* check whether at least the default flags are different */
1387 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1388 assert(meta);
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001389 str = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001390
1391 /* if previous and current dflt flags are the same, this node is redundant */
1392 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1393 return 1;
1394 }
1395 return 0;
1396 }
1397
1398 if (!child && (op == LYD_DIFF_OP_NONE)) {
1399 return 1;
1400 }
1401
1402 return 0;
1403}
1404
1405/**
1406 * @brief Merge sysrepo diff with another diff, recursively.
1407 *
1408 * @param[in] src_diff Source diff node.
1409 * @param[in] diff_parent Current sysrepo diff parent.
1410 * @param[in] diff_cb Optional diff callback.
1411 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001412 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001413 * @param[in,out] diff Diff root node.
1414 * @return LY_ERR value.
1415 */
1416static LY_ERR
1417lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001418 uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001419{
1420 LY_ERR ret = LY_SUCCESS;
1421 struct lyd_node *child, *diff_node = NULL;
1422 enum lyd_diff_op src_op, cur_op;
1423
1424 /* get source node operation */
1425 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1426
1427 /* find an equal node in the current diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001428 lyd_diff_find_node(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, &diff_node);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001429
1430 if (diff_node) {
1431 /* get target (current) operation */
1432 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1433
1434 /* merge operations */
1435 switch (src_op) {
1436 case LYD_DIFF_OP_REPLACE:
1437 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1438 break;
1439 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001440 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
1441 /* special case of creating duplicate state (leaf-)list instances */
1442 goto add_diff;
1443 }
1444
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001445 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001446 break;
1447 case LYD_DIFF_OP_DELETE:
1448 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1449 break;
1450 case LYD_DIFF_OP_NONE:
1451 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1452 break;
1453 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001454 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001455 }
1456 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001457 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001458 return ret;
1459 }
1460
1461 if (diff_cb) {
1462 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001463 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001464 }
1465
1466 /* update diff parent */
1467 diff_parent = diff_node;
1468
1469 /* merge src_diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001470 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001471 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001472 }
1473 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001474add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001475 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001476 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1477 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001478
1479 /* insert node into diff if not already */
1480 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001481 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001482 }
1483
1484 /* update operation */
1485 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1486
1487 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001488 /* call callback with no source diff node since it was duplicated and just added */
1489 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001490 }
1491
1492 /* update diff parent */
1493 diff_parent = diff_node;
1494 }
1495
1496 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001497 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001498 if (diff_parent == *diff) {
1499 *diff = (*diff)->next;
1500 }
1501 lyd_free_tree(diff_parent);
1502 }
1503
1504 return LY_SUCCESS;
1505}
1506
1507API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001508lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod,
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001509 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001510{
1511 const struct lyd_node *src_root;
1512
1513 LY_LIST_FOR(src_diff, src_root) {
1514 if (mod && (lyd_owner_module(src_root) != mod)) {
1515 /* skip data nodes from different modules */
1516 continue;
1517 }
1518
1519 /* apply relevant nodes from the diff datatree */
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001520 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001521 }
1522
1523 return LY_SUCCESS;
1524}
1525
1526API LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001527lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent, const struct lyd_node *src_sibling,
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001528 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001529{
1530 if (!src_sibling) {
1531 return LY_SUCCESS;
1532 }
1533
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001534 return lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, options, diff_first);
Michal Vasko04f85912020-08-07 12:14:58 +02001535}
1536
1537API LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001538lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001539{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001540 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001541}
Michal Vasko4231fb62020-07-13 13:54:47 +02001542
1543static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001544lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001545{
1546 LY_ERR ret = LY_SUCCESS;
1547 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001548 const char *val1 = NULL;
1549 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001550 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001551
Michal Vaskobaba84e2021-02-05 16:33:30 +01001552 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1553
1554 meta = lyd_find_meta(node->meta, mod, "orig-value");
1555 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001556
1557 /* orig-value */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001558 val1 = meta->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001559
1560 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001561 if (node->schema->nodetype == LYS_LEAF) {
1562 val2 = strdup(LYD_CANON_VALUE(node));
1563 } else {
1564 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1565 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001566
1567 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001568 flags = node->flags;
1569 if (node->schema->nodetype == LYS_LEAF) {
1570 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1571 } else {
1572 union lyd_any_value anyval = {.str = val1};
1573 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1574 }
1575 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001576 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1577
1578cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001579 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001580 return ret;
1581}
1582
1583static LY_ERR
1584lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1585{
1586 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001587 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001588
1589 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001590 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001591
1592 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001593 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001594 flag1 = LYD_DEFAULT;
1595 } else {
1596 flag1 = 0;
1597 }
1598
1599 /* current default */
1600 flag2 = node->flags & LYD_DEFAULT;
1601
Michal Vasko610e93b2020-11-09 20:58:32 +01001602 if (flag1 == flag2) {
1603 /* no default state change so nothing to reverse */
1604 return LY_SUCCESS;
1605 }
1606
Michal Vasko4231fb62020-07-13 13:54:47 +02001607 /* switch defaults */
1608 node->flags &= ~LYD_DEFAULT;
1609 node->flags |= flag1;
1610 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1611
1612 return LY_SUCCESS;
1613}
1614
1615static LY_ERR
1616lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1617{
1618 LY_ERR ret = LY_SUCCESS;
1619 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001620 const char *val1 = NULL;
1621 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001622
1623 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001624 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001625
1626 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001627 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001628
1629 /* value1 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001630 val1 = meta1->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001631
1632 /* value2 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001633 val2 = strdup(meta2->value.canonical);
Michal Vasko4231fb62020-07-13 13:54:47 +02001634
1635 /* switch values */
1636 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1637 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1638
1639cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001640 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001641 return ret;
1642}
1643
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001644/**
1645 * @brief Remove specific operation from all the nodes in a subtree.
1646 *
1647 * @param[in] diff Diff subtree to process.
1648 * @param[in] op Only expected operation.
1649 * @return LY_ERR value.
1650 */
1651static LY_ERR
1652lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
1653{
1654 struct lyd_node *elem;
1655 struct lyd_meta *meta;
1656
1657 LYD_TREE_DFS_BEGIN(diff, elem) {
1658 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
1659 if (meta) {
1660 LY_CHECK_ERR_RET(lyd_diff_str2op(meta->value.canonical) != op, LOGINT(LYD_CTX(diff)), LY_EINT);
1661 lyd_free_meta_single(meta);
1662 }
1663
1664 LYD_TREE_DFS_END(diff, elem);
1665 }
1666
1667 return LY_SUCCESS;
1668}
1669
Michal Vasko4231fb62020-07-13 13:54:47 +02001670API LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001671lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001672{
1673 LY_ERR ret = LY_SUCCESS;
1674 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001675 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02001676 enum lyd_diff_op op;
1677
1678 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1679
1680 if (!src_diff) {
1681 *diff = NULL;
1682 return LY_SUCCESS;
1683 }
1684
1685 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001686 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001687
1688 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001689 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1690 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001691
1692 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001693 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001694 /* skip all keys */
1695 if (!lysc_is_key(elem->schema)) {
1696 /* find operation attribute, if any */
1697 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001698
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001699 switch (op) {
1700 case LYD_DIFF_OP_CREATE:
1701 /* reverse create to delete */
1702 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001703
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001704 /* check all the children for the same operation, nothing else is expected */
1705 LY_LIST_FOR(lyd_child(elem), iter) {
1706 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
1707 }
1708
Michal Vasko9e070522021-03-05 14:00:14 +01001709 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001710 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001711 case LYD_DIFF_OP_DELETE:
1712 /* reverse delete to create */
1713 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001714
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001715 /* check all the children for the same operation, nothing else is expected */
1716 LY_LIST_FOR(lyd_child(elem), iter) {
1717 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
1718 }
1719
Michal Vasko9e070522021-03-05 14:00:14 +01001720 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001721 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001722 case LYD_DIFF_OP_REPLACE:
1723 switch (elem->schema->nodetype) {
1724 case LYS_LEAF:
1725 /* leaf value change */
1726 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1727 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1728 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01001729 case LYS_ANYXML:
1730 case LYS_ANYDATA:
1731 /* any value change */
1732 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1733 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001734 case LYS_LEAFLIST:
1735 /* leaf-list move */
1736 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1737 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1738 break;
1739 case LYS_LIST:
1740 /* list move */
1741 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1742 break;
1743 default:
1744 LOGINT(LYD_CTX(src_diff));
1745 ret = LY_EINT;
1746 goto cleanup;
1747 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001748 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001749 case LYD_DIFF_OP_NONE:
1750 switch (elem->schema->nodetype) {
1751 case LYS_LEAF:
1752 case LYS_LEAFLIST:
1753 /* default flag change */
1754 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1755 break;
1756 default:
1757 /* nothing to do */
1758 break;
1759 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001760 break;
1761 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001762 }
1763
Michal Vasko56daf732020-08-10 10:57:18 +02001764 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02001765 }
1766 }
1767
1768cleanup:
1769 if (ret) {
1770 lyd_free_siblings(*diff);
1771 *diff = NULL;
1772 }
1773 return ret;
1774}