blob: a283e2a4c54029510211269f7fd8b6f766dc6912 [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 *
Michal Vaskoe78faec2021-04-08 17:24:43 +02006 * Copyright (c) 2020 - 2021 CESNET, z.s.p.o.
Michal Vaskod59035b2020-07-08 12:00:06 +02007 *
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 */
Christian Hopps32874e12021-05-01 09:43:54 -040014#define _GNU_SOURCE /* asprintf, strdup */
Michal Vaskod59035b2020-07-08 12:00:06 +020015
16#include "diff.h"
17
18#include <assert.h>
19#include <stddef.h>
Michal Vaskoe78faec2021-04-08 17:24:43 +020020#include <stdint.h>
21#include <stdio.h>
Radek Krejci47fab892020-11-05 17:02:41 +010022#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020023#include <string.h>
24
25#include "common.h"
Michal Vaskoe78faec2021-04-08 17:24:43 +020026#include "compat.h"
Radek Krejci47fab892020-11-05 17:02:41 +010027#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020028#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010029#include "plugins_types.h"
30#include "set.h"
31#include "tree.h"
32#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020033#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010034#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020035#include "tree_schema.h"
36#include "tree_schema_internal.h"
37
Michal Vasko52afd7d2022-01-18 14:08:34 +010038#define LOGERR_META(ctx, meta_name, node) \
39 { \
40 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
41 LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \
42 free(__path); \
43 }
44
45#define LOGERR_NOINST(ctx, node) \
46 { \
47 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
48 LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \
49 free(__path); \
50 }
51
52#define LOGERR_UNEXPVAL(ctx, node, data_source) \
53 { \
54 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
55 LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \
56 free(__path); \
57 }
58
59#define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \
60 { \
61 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
62 LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \
63 lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \
64 free(__path); \
65 }
66
Michal Vaskod59035b2020-07-08 12:00:06 +020067static const char *
68lyd_diff_op2str(enum lyd_diff_op op)
69{
70 switch (op) {
71 case LYD_DIFF_OP_CREATE:
72 return "create";
73 case LYD_DIFF_OP_DELETE:
74 return "delete";
75 case LYD_DIFF_OP_REPLACE:
76 return "replace";
77 case LYD_DIFF_OP_NONE:
78 return "none";
79 }
80
81 LOGINT(NULL);
82 return NULL;
83}
84
Michal Vaskoe6323f62020-07-09 15:49:02 +020085static enum lyd_diff_op
86lyd_diff_str2op(const char *str)
87{
88 switch (str[0]) {
89 case 'c':
90 assert(!strcmp(str, "create"));
91 return LYD_DIFF_OP_CREATE;
92 case 'd':
93 assert(!strcmp(str, "delete"));
94 return LYD_DIFF_OP_DELETE;
95 case 'r':
96 assert(!strcmp(str, "replace"));
97 return LYD_DIFF_OP_REPLACE;
98 case 'n':
99 assert(!strcmp(str, "none"));
100 return LYD_DIFF_OP_NONE;
101 }
102
103 LOGINT(NULL);
104 return 0;
105}
106
Michal Vaskod59035b2020-07-08 12:00:06 +0200107LY_ERR
108lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200109 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
110 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200111{
112 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
113 const struct lyd_node *parent = NULL;
114 const struct lys_module *yang_mod;
115
116 assert(diff);
117
Michal Vasko53d48422020-11-13 18:02:29 +0100118 /* replace leaf always needs orig-default and orig-value */
119 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
120
121 /* create on userord needs key/value */
122 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200123 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100124 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200125 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100126
127 /* move on userord needs both key and orig-key/value and orig-value */
128 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200129 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100130 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200131 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
132 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100133
Michal Vaskod59035b2020-07-08 12:00:06 +0200134 /* find the first existing parent */
135 siblings = *diff;
136 while (1) {
137 /* find next node parent */
138 parent = node;
139 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100140 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200141 }
142 if (parent == node) {
143 /* no more parents to find */
144 break;
145 }
146
147 /* check whether it exists in the diff */
148 if (lyd_find_sibling_first(siblings, parent, &match)) {
149 break;
150 }
151
152 /* another parent found */
153 diff_parent = match;
154
155 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200156 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200157 }
158
159 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200160 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100161 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200162
163 /* find the first duplicated parent */
164 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100165 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200166 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100167 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200168 }
169 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100170 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200171 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100172 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200173 }
174 }
175
176 /* no parent existed, must be manually connected */
177 if (!diff_parent) {
178 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200179 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200180 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200181 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200182 }
183
184 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200185 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200186 assert(!strcmp(yang_mod->name, "yang"));
187
188 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200189 if (diff_parent && (diff_parent != dup)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100190 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200191 }
192
193 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100194 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 +0200195
196 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200197 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100198 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 +0200199 }
200
201 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200202 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100203 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 +0200204 }
205
206 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200207 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100208 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200209 }
210
211 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200212 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100213 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200214 }
215
Michal Vaskoe78faec2021-04-08 17:24:43 +0200216 /* position */
217 if (position) {
218 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "position", position, 0, NULL));
219 }
220
Michal Vaskod59035b2020-07-08 12:00:06 +0200221 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200222 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100223 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 +0200224 }
225
Michal Vaskoe78faec2021-04-08 17:24:43 +0200226 /* orig-position */
227 if (orig_position) {
228 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-position", orig_position, 0, NULL));
229 }
230
Michal Vaskod59035b2020-07-08 12:00:06 +0200231 return LY_SUCCESS;
232}
233
234/**
235 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
236 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100237 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200238 * @param[in] schema Schema node of the list/leaf-list.
239 * @param[in,out] userord Sized array of userord items.
240 * @return Userord item for all the user-ordered list/leaf-list instances.
241 */
242static struct lyd_diff_userord *
243lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
244{
245 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200246 struct lyd_node *iter;
247 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200248 LY_ARRAY_COUNT_TYPE u;
249
250 LY_ARRAY_FOR(*userord, u) {
251 if ((*userord)[u].schema == schema) {
252 return &(*userord)[u];
253 }
254 }
255
256 /* it was not added yet, add it now */
257 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
258
259 item->schema = schema;
260 item->pos = 0;
261 item->inst = NULL;
262
263 /* store all the instance pointers in the current order */
264 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200265 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
266 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
267 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200268 }
269 }
270
271 return item;
272}
273
274/**
275 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
276 * lists/leaf-lists.
277 *
278 * @param[in] first Node from the first tree, can be NULL (on create).
279 * @param[in] second Node from the second tree, can be NULL (on delete).
280 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100281 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200282 * @param[out] op Operation.
283 * @param[out] orig_default Original default metadata.
284 * @param[out] value Value metadata.
285 * @param[out] orig_value Original value metadata
286 * @param[out] key Key metadata.
287 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200288 * @param[out] position Position metadata.
289 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200290 * @return LY_SUCCESS on success,
291 * @return LY_ENOT if there is no change to be added into diff,
292 * @return LY_ERR value on other errors.
293 */
294static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200295lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100296 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200297 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200298{
299 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200300 size_t buflen, bufused;
301 uint32_t first_pos, second_pos;
Michal Vaskod59035b2020-07-08 12:00:06 +0200302
303 assert(first || second);
304
305 *orig_default = NULL;
306 *value = NULL;
307 *orig_value = NULL;
308 *key = NULL;
309 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200310 *position = NULL;
311 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200312
313 schema = first ? first->schema : second->schema;
314 assert(lysc_is_userordered(schema));
315
Michal Vaskod59035b2020-07-08 12:00:06 +0200316 /* find user-ordered first position */
317 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200318 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200319 if (userord_item->inst[first_pos] == first) {
320 break;
321 }
322 }
323 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
324 } else {
325 first_pos = 0;
326 }
327
Michal Vaskoe78faec2021-04-08 17:24:43 +0200328 /* prepare position of the next instance */
329 second_pos = userord_item->pos++;
330
Michal Vaskod59035b2020-07-08 12:00:06 +0200331 /* learn operation first */
332 if (!second) {
333 *op = LYD_DIFF_OP_DELETE;
334 } else if (!first) {
335 *op = LYD_DIFF_OP_CREATE;
336 } else {
Michal Vasko8f359bf2020-07-28 10:41:15 +0200337 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200338 /* in first, there is a different instance on the second position, we are going to move 'first' node */
339 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200340 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200341 /* default flag change */
342 *op = LYD_DIFF_OP_NONE;
343 } else {
344 /* no changes */
345 return LY_ENOT;
346 }
347 }
348
349 /*
350 * set each attribute correctly based on the operation and node type
351 */
352
353 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100354 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200355 if (first->flags & LYD_DEFAULT) {
356 *orig_default = "true";
357 } else {
358 *orig_default = "false";
359 }
360 }
361
362 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200363 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
364 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200365 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200366 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200367 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200368 } else {
369 *value = strdup("");
370 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
371 }
372 }
373
374 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200375 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
376 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200377 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200378 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200379 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200380 } else {
381 *orig_value = strdup("");
382 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
383 }
384 }
385
386 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200387 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
388 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200389 if (second_pos) {
390 buflen = bufused = 0;
391 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
392 } else {
393 *key = strdup("");
394 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
395 }
396 }
397
398 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200399 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
400 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200401 if (first_pos) {
402 buflen = bufused = 0;
403 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
404 } else {
405 *orig_key = strdup("");
406 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
407 }
408 }
409
Michal Vaskoe78faec2021-04-08 17:24:43 +0200410 /* position */
411 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
412 if (second_pos) {
413 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
414 LOGMEM(schema->module->ctx);
415 return LY_EMEM;
416 }
417 } else {
418 *position = strdup("");
419 LY_CHECK_ERR_RET(!*position, LOGMEM(schema->module->ctx), LY_EMEM);
420 }
421 }
422
423 /* orig-position */
424 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
425 if (first_pos) {
426 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
427 LOGMEM(schema->module->ctx);
428 return LY_EMEM;
429 }
430 } else {
431 *orig_position = strdup("");
432 LY_CHECK_ERR_RET(!*orig_position, LOGMEM(schema->module->ctx), LY_EMEM);
433 }
434 }
435
Michal Vaskod59035b2020-07-08 12:00:06 +0200436 /*
437 * update our instances - apply the change
438 */
439 if (*op == LYD_DIFF_OP_CREATE) {
440 /* insert the instance */
Michal Vasko5cde11b2020-12-08 10:04:48 +0100441 LY_ARRAY_CREATE_RET(schema->module->ctx, userord_item->inst, 1, LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200442 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
443 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
444 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
445 }
446 LY_ARRAY_INCREMENT(userord_item->inst);
447 userord_item->inst[second_pos] = second;
448
449 } else if (*op == LYD_DIFF_OP_DELETE) {
450 /* remove the instance */
451 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
452 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
453 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
454 }
455 LY_ARRAY_DECREMENT(userord_item->inst);
456
457 } else if (*op == LYD_DIFF_OP_REPLACE) {
458 /* move the instances */
459 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
460 (first_pos - second_pos) * sizeof *userord_item->inst);
461 userord_item->inst[second_pos] = first;
462 }
463
464 return LY_SUCCESS;
465}
466
467/**
468 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
469 * lists/leaf-lists.
470 *
471 * @param[in] first Node from the first tree, can be NULL (on create).
472 * @param[in] second Node from the second tree, can be NULL (on delete).
473 * @param[in] options Diff options.
474 * @param[out] op Operation.
475 * @param[out] orig_default Original default metadata.
476 * @param[out] orig_value Original value metadata.
477 * @return LY_SUCCESS on success,
478 * @return LY_ENOT if there is no change to be added into diff,
479 * @return LY_ERR value on other errors.
480 */
481static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200482lyd_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 +0200483 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200484{
485 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200486 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200487
488 assert(first || second);
489
490 *orig_default = NULL;
491 *orig_value = NULL;
492
493 schema = first ? first->schema : second->schema;
494 assert(!lysc_is_userordered(schema));
495
496 /* learn operation first */
497 if (!second) {
498 *op = LYD_DIFF_OP_DELETE;
499 } else if (!first) {
500 *op = LYD_DIFF_OP_CREATE;
501 } else {
502 switch (schema->nodetype) {
503 case LYS_CONTAINER:
504 case LYS_RPC:
505 case LYS_ACTION:
506 case LYS_NOTIF:
507 /* no changes */
508 return LY_ENOT;
509 case LYS_LIST:
510 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200511 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200512 /* default flag change */
513 *op = LYD_DIFF_OP_NONE;
514 } else {
515 /* no changes */
516 return LY_ENOT;
517 }
518 break;
519 case LYS_LEAF:
520 case LYS_ANYXML:
521 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200522 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200523 /* different values */
524 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200525 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200526 /* default flag change */
527 *op = LYD_DIFF_OP_NONE;
528 } else {
529 /* no changes */
530 return LY_ENOT;
531 }
532 break;
533 default:
534 LOGINT_RET(schema->module->ctx);
535 }
536 }
537
538 /*
539 * set each attribute correctly based on the operation and node type
540 */
541
542 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100543 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200544 if (first->flags & LYD_DEFAULT) {
545 *orig_default = "true";
546 } else {
547 *orig_default = "false";
548 }
549 }
550
551 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100552 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
553 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200554 str_val = lyd_get_value(first);
555 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100556 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
557 } else {
558 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
559 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200560 }
561
562 return LY_SUCCESS;
563}
564
565/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200566 * @brief Find a matching instance of a node in a data tree.
567 *
568 * @param[in] siblings Siblings to search in.
569 * @param[in] target Target node to search for.
570 * @param[in] defaults Whether to consider (or ignore) default values.
571 * @param[in,out] dup_inst_cache Duplicate instance cache.
572 * @param[out] match Found match, NULL if no matching node found.
573 * @return LY_ERR value.
574 */
575static LY_ERR
576lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200577 struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200578{
Michal Vaskoe78faec2021-04-08 17:24:43 +0200579 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
580 /* try to find the exact instance */
581 lyd_find_sibling_first(siblings, target, match);
582 } else {
583 /* try to simply find the node, there cannot be more instances */
584 lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
585 }
586
Michal Vaskod7c048c2021-05-18 16:12:55 +0200587 /* update match as needed */
588 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200589
590 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
591 /* ignore default nodes */
592 *match = NULL;
593 }
594 return LY_SUCCESS;
595}
596
597/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200598 * @brief Perform diff for all siblings at certain depth, recursively.
599 *
600 * For user-ordered lists/leaf-lists a specific structure is used for storing
601 * the current order. The idea is to apply all the generated diff changes
602 * virtually on the first tree so that we can continue to generate correct
603 * changes after some were already generated.
604 *
605 * The algorithm then uses second tree position-based changes with a before
606 * (preceding) item anchor.
607 *
608 * Example:
609 *
610 * Virtual first tree leaf-list order:
611 * 1 2 [3] 4 5
612 *
613 * Second tree leaf-list order:
614 * 1 2 [5] 3 4
615 *
616 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
617 * match - they do not - move nodes so that the 3rd position node is final ->
618 * -> move node 5 to the 3rd position -> move node 5 after node 2.
619 *
620 * Required properties:
621 * Stored operations (move) should not be affected by later operations -
622 * - would cause a redundantly long list of operations, possibly inifinite.
623 *
624 * Implemenation justification:
625 * First, all delete operations and only then move/create operations are stored.
626 * Also, preceding anchor is used and after each iteration another node is
627 * at its final position. That results in the invariant that all preceding
628 * nodes are final and will not be changed by the later operations, meaning
629 * they can safely be used as anchors for the later operations.
630 *
631 * @param[in] first First tree first sibling.
632 * @param[in] second Second tree first sibling.
633 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200634 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200635 * @param[in,out] diff Diff to append to.
636 * @return LY_ERR value.
637 */
638static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200639lyd_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 +0200640 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200641{
642 LY_ERR ret = LY_SUCCESS;
643 const struct lyd_node *iter_first, *iter_second;
644 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100645 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200646 struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200647 LY_ARRAY_COUNT_TYPE u;
648 enum lyd_diff_op op;
649 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200650 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200651
Michal Vaskod59035b2020-07-08 12:00:06 +0200652 /* compare first tree to the second tree - delete, replace, none */
653 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200654 if (!iter_first->schema) {
655 continue;
656 }
657
Michal Vaskod59035b2020-07-08 12:00:06 +0200658 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200659 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200660 /* skip default nodes */
661 continue;
662 }
663
Michal Vaskoe78faec2021-04-08 17:24:43 +0200664 /* find a match in the second tree */
665 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
666 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200667
668 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100669 /* get (create) userord entry */
670 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
671 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
672
Michal Vaskoe78faec2021-04-08 17:24:43 +0200673 /* we are handling only user-ordered node delete now */
674 if (!match_second) {
675 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100676 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
677 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200678
679 /* there must be changes, it is deleted */
680 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100681 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
682 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200683
684 free(orig_value);
685 free(key);
686 free(value);
687 free(position);
688 free(orig_key);
689 free(orig_position);
690 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200691 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200692 } else {
693 /* get all the attributes */
694 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
695
696 /* add into diff if there are any changes */
697 if (!ret) {
698 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200699 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200700 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100701 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200702 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200703 }
704
705 free(orig_value);
706 LY_CHECK_GOTO(ret, cleanup);
707 } else if (ret == LY_ENOT) {
708 ret = LY_SUCCESS;
709 } else {
710 goto cleanup;
711 }
712 }
713
714 /* check descendants, if any, recursively */
715 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200716 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
717 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200718 }
719
720 if (nosiblings) {
721 break;
722 }
723 }
724
725 /* reset all cached positions */
726 LY_ARRAY_FOR(userord, u) {
727 userord[u].pos = 0;
728 }
729
730 /* compare second tree to the first tree - create, user-ordered move */
731 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200732 if (!iter_second->schema) {
733 continue;
734 }
735
Michal Vaskod59035b2020-07-08 12:00:06 +0200736 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200737 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200738 /* skip default nodes */
739 continue;
740 }
741
Michal Vaskoe78faec2021-04-08 17:24:43 +0200742 /* find a match in the first tree */
743 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
744 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200745
746 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100747 /* get userord entry */
748 userord_item = lyd_diff_userord_get(NULL, iter_second->schema, &userord);
749 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
750
Michal Vaskod59035b2020-07-08 12:00:06 +0200751 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100752 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200753 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200754
755 /* add into diff if there are any changes */
756 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200757 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
758 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200759
760 free(orig_value);
761 free(key);
762 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200763 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200764 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200765 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200766 LY_CHECK_GOTO(ret, cleanup);
767 } else if (ret == LY_ENOT) {
768 ret = LY_SUCCESS;
769 } else {
770 goto cleanup;
771 }
772 } else if (!match_first) {
773 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200774 LY_CHECK_GOTO(ret = lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200775
776 /* there must be changes, it is created */
777 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200778 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200779
780 free(orig_value);
781 LY_CHECK_GOTO(ret, cleanup);
782 } /* else was handled */
783
784 if (nosiblings) {
785 break;
786 }
787 }
788
789cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200790 lyd_dup_inst_free(dup_inst_first);
791 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200792 LY_ARRAY_FOR(userord, u) {
793 LY_ARRAY_FREE(userord[u].inst);
794 }
795 LY_ARRAY_FREE(userord);
796 return ret;
797}
798
Michal Vasko3a41dff2020-07-15 14:30:28 +0200799static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100800lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
801 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200802{
803 const struct ly_ctx *ctx;
804
805 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
806
807 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200808 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200809 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200810 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200811 } else {
812 ctx = NULL;
813 }
814
815 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
816 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
817 return LY_EINVAL;
818 }
819
820 *diff = NULL;
821
Michal Vasko3a41dff2020-07-15 14:30:28 +0200822 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
823}
824
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100825LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200826lyd_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 +0200827{
828 return lyd_diff(first, second, options, 1, diff);
829}
830
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100831LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200832lyd_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 +0200833{
834 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200835}
836
837/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200838 * @brief Learn operation of a diff node.
839 *
840 * @param[in] diff_node Diff node.
841 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200842 * @return LY_ERR value.
843 */
844static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200845lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200846{
847 struct lyd_meta *meta = NULL;
848 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200849 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100850 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +0200851
Michal Vasko9e685082021-01-29 14:49:09 +0100852 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200853 LY_LIST_FOR(diff_parent->meta, meta) {
854 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200855 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200856 if ((str[0] == 'r') && (diff_parent != diff_node)) {
857 /* we do not care about this operation if it's in our parent */
858 continue;
859 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200860 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200861 break;
862 }
863 }
864 if (meta) {
865 break;
866 }
867 }
Michal Vasko52afd7d2022-01-18 14:08:34 +0100868
869 if (!meta) {
870 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
871 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
872 free(path);
873 return LY_EINT;
874 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200875
Michal Vaskod59035b2020-07-08 12:00:06 +0200876 return LY_SUCCESS;
877}
878
879/**
880 * @brief Insert a diff node into a data tree.
881 *
882 * @param[in,out] first_node First sibling of the data tree.
883 * @param[in] parent_node Data tree sibling parent node.
884 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200885 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
886 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 * @return err_info, NULL on success.
888 */
889static LY_ERR
890lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200891 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200892{
893 LY_ERR ret;
894 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200895 uint32_t pos, anchor_pos;
896 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200897
898 assert(new_node);
899
900 if (!*first_node) {
901 if (!parent_node) {
902 /* no parent or siblings */
903 *first_node = new_node;
904 return LY_SUCCESS;
905 }
906
907 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200908 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200909 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200910 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200911 return LY_EINVAL;
912 }
Michal Vaskob104f112020-07-17 09:54:54 +0200913 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200914 }
915
Michal Vasko9e685082021-01-29 14:49:09 +0100916 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200917
Michal Vaskod59035b2020-07-08 12:00:06 +0200918 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200919 /* simple insert */
920 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200921 }
922
Michal Vaskoe78faec2021-04-08 17:24:43 +0200923 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200924 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200925 if (lysc_is_dup_inst_list(new_node->schema)) {
926 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +0100927 if (!anchor_pos) {
928 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
929 return LY_EINVAL;
930 }
Michal Vaskoe78faec2021-04-08 17:24:43 +0200931
932 found = 0;
933 pos = 1;
934 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
935 if (pos == anchor_pos) {
936 found = 1;
937 break;
938 }
939 ++pos;
940 }
941 if (!found) {
942 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
943 new_node->schema->name);
944 return LY_EINVAL;
945 }
946 } else {
947 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
948 if (ret == LY_ENOTFOUND) {
949 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
950 new_node->schema->name);
951 return LY_EINVAL;
952 } else if (ret) {
953 return ret;
954 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200955 }
956
957 /* insert after */
958 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
959 assert(new_node->prev == anchor);
960 if (*first_node == new_node) {
961 *first_node = anchor;
962 }
963 } else {
964 if ((*first_node)->schema->flags & LYS_KEY) {
965 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
966
967 /* find last key */
968 anchor = *first_node;
969 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
970 anchor = anchor->next;
971 }
972 /* insert after the last key */
973 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
974 } else {
975 /* insert at the beginning */
976 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
977 *first_node = new_node;
978 }
979 }
980
981 return LY_SUCCESS;
982}
983
984/**
985 * @brief Apply diff subtree on data tree nodes, recursively.
986 *
987 * @param[in,out] first_node First sibling of the data tree.
988 * @param[in] parent_node Parent of the first sibling.
989 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200990 * @param[in] diff_cb Optional diff callback.
991 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200992 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200993 * @return LY_ERR value.
994 */
995static LY_ERR
996lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200997 lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +0200998{
999 LY_ERR ret;
1000 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001001 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001002 enum lyd_diff_op op;
1003 struct lyd_meta *meta;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001004 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001005 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001006
1007 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001008 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001009
Michal Vaskoe6323f62020-07-09 15:49:02 +02001010 /* handle specific user-ordered (leaf-)lists operations separately */
1011 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1012 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001013 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001014 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001015 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001016 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001017 /* duplicate the node */
1018 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001019 }
1020
Michal Vaskoe78faec2021-04-08 17:24:43 +02001021 /* get "key", "value", or "position" metadata string value */
1022 if (lysc_is_dup_inst_list(diff_node->schema)) {
1023 meta_str = "yang:position";
1024 } else if (diff_node->schema->nodetype == LYS_LIST) {
1025 meta_str = "yang:key";
1026 } else {
1027 meta_str = "yang:value";
1028 }
1029 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001030 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001031 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001032
Michal Vaskod59035b2020-07-08 12:00:06 +02001033 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001034 if (str_val[0]) {
1035 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001036 } else {
1037 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1038 }
1039 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001040 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001041 lyd_free_tree(match);
1042 }
1043 return ret;
1044 }
1045
1046 goto next_iter_r;
1047 }
1048
1049 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001050 switch (op) {
1051 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001052 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001053 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001054 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001055
1056 if (match->schema->nodetype & LYD_NODE_TERM) {
1057 /* special case of only dflt flag change */
1058 if (diff_node->flags & LYD_DEFAULT) {
1059 match->flags |= LYD_DEFAULT;
1060 } else {
1061 match->flags &= ~LYD_DEFAULT;
1062 }
1063 } else {
1064 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001065 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001066 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1067 LYD_NAME(diff_node));
1068 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001069 }
1070 }
1071 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001072 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001073 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001074 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001075
1076 /* insert it at the end */
1077 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001078 if (parent_node) {
1079 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +02001080 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001081 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001082 }
1083 if (ret) {
1084 lyd_free_tree(match);
1085 return ret;
1086 }
1087
1088 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001089 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001090 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001091 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001092 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001093
1094 /* remove it */
1095 if ((match == *first_node) && !match->parent) {
1096 assert(!parent_node);
1097 /* we have removed the top-level node */
1098 *first_node = (*first_node)->next;
1099 }
1100 lyd_free_tree(match);
1101
1102 /* we are not going recursively in this case, the whole subtree was already deleted */
1103 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001104 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001105 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1106 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1107 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1108 return LY_EINVAL;
1109 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001110
1111 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001112 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001113 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001114
Michal Vaskobaba84e2021-02-05 16:33:30 +01001115 /* update the value */
1116 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001117 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001118 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001119 } else {
1120 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001121 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001122 }
1123
1124 /* with flags */
1125 match->flags = diff_node->flags;
1126 break;
1127 default:
1128 LOGINT_RET(ctx);
1129 }
1130
1131next_iter_r:
1132 if (diff_cb) {
1133 /* call callback */
1134 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1135 }
1136
1137 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001138 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001139 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001140 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1141 if (ret) {
1142 break;
1143 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001144 }
1145
Michal Vaskod7c048c2021-05-18 16:12:55 +02001146 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001147 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001148}
1149
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001150LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001151lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001152 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001153{
1154 const struct lyd_node *root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001155 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001156 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001157
1158 LY_LIST_FOR(diff, root) {
1159 if (mod && (lyd_owner_module(root) != mod)) {
1160 /* skip data nodes from different modules */
1161 continue;
1162 }
1163
1164 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001165 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1166 if (ret) {
1167 break;
1168 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001169 }
1170
Michal Vaskod7c048c2021-05-18 16:12:55 +02001171 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001172 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001173}
1174
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001175LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001176lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001177{
1178 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1179}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001180
1181/**
1182 * @brief Update operations on a diff node when the new operation is NONE.
1183 *
1184 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001185 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001186 * @param[in] src_diff Current source diff node.
1187 * @return LY_ERR value.
1188 */
1189static LY_ERR
1190lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1191{
1192 switch (cur_op) {
1193 case LYD_DIFF_OP_NONE:
1194 case LYD_DIFF_OP_CREATE:
1195 case LYD_DIFF_OP_REPLACE:
1196 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1197 /* NONE on a term means only its dflt flag was changed */
1198 diff_match->flags &= ~LYD_DEFAULT;
1199 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1200 }
1201 break;
1202 default:
1203 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001204 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1205 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001206 }
1207
1208 return LY_SUCCESS;
1209}
1210
1211/**
1212 * @brief Remove an attribute from a node.
1213 *
1214 * @param[in] node Node with the metadata.
1215 * @param[in] name Metadata name.
1216 */
1217static void
1218lyd_diff_del_meta(struct lyd_node *node, const char *name)
1219{
1220 struct lyd_meta *meta;
1221
1222 LY_LIST_FOR(node->meta, meta) {
1223 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001224 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001225 return;
1226 }
1227 }
1228
1229 assert(0);
1230}
1231
1232/**
1233 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001234 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001235 *
1236 * @param[in] node Node to change.
1237 * @param[in] op Operation to set.
1238 * @return LY_ERR value.
1239 */
1240static LY_ERR
1241lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1242{
1243 struct lyd_meta *meta;
1244
1245 LY_LIST_FOR(node->meta, meta) {
1246 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001247 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001248 break;
1249 }
1250 }
1251
Michal Vasko871a0252020-11-11 18:35:24 +01001252 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001253}
1254
1255/**
1256 * @brief Update operations on a diff node when the new operation is REPLACE.
1257 *
1258 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001259 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001260 * @param[in] src_diff Current source diff node.
1261 * @return LY_ERR value.
1262 */
1263static LY_ERR
1264lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1265{
1266 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001267 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001268 struct lyd_meta *meta;
1269 const struct lys_module *mod;
1270 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001271 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001272
1273 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001274 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001275 assert(mod);
1276
1277 switch (cur_op) {
1278 case LYD_DIFF_OP_REPLACE:
1279 case LYD_DIFF_OP_CREATE:
1280 switch (diff_match->schema->nodetype) {
1281 case LYS_LIST:
1282 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001283 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001284 * keep orig_key/orig_value (only replace oper) and replace key/value */
1285 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001286 if (lysc_is_dup_inst_list(diff_match->schema)) {
1287 meta_name = "position";
1288 } else if (diff_match->schema->nodetype == LYS_LIST) {
1289 meta_name = "key";
1290 } else {
1291 meta_name = "value";
1292 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001293
1294 lyd_diff_del_meta(diff_match, meta_name);
1295 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001296 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001297 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001298 break;
1299 case LYS_LEAF:
1300 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001301 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001302 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1303 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001304 }
1305
Michal Vaskoe6323f62020-07-09 15:49:02 +02001306 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001307 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001308 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001309 }
1310
Michal Vasko8caadab2020-11-05 17:38:15 +01001311 if (cur_op == LYD_DIFF_OP_REPLACE) {
1312 /* compare values whether there is any change at all */
1313 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001314 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001315 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001316 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1317 if (!ret) {
1318 /* values are the same, remove orig-value meta and set oper to NONE */
1319 lyd_free_meta_single(meta);
1320 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1321 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001322 }
1323
1324 /* modify the default flag */
1325 diff_match->flags &= ~LYD_DEFAULT;
1326 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1327 break;
1328 case LYS_ANYXML:
1329 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001330 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001331 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1332 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001333 }
1334
1335 /* modify the node value */
1336 any = (struct lyd_node_any *)src_diff;
1337 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1338 break;
1339 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001340 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001341 }
1342 break;
1343 case LYD_DIFF_OP_NONE:
1344 /* it is moved now */
1345 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1346
1347 /* change the operation */
1348 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1349
Michal Vaskoe78faec2021-04-08 17:24:43 +02001350 /* set orig-meta and meta */
1351 if (lysc_is_dup_inst_list(diff_match->schema)) {
1352 meta_name = "position";
1353 orig_meta_name = "orig-position";
1354 } else {
1355 meta_name = "key";
1356 orig_meta_name = "orig-key";
1357 }
1358
1359 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001360 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001361 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001362
Michal Vaskoe78faec2021-04-08 17:24:43 +02001363 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001364 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001365 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001366 break;
1367 default:
1368 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001369 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1370 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001371 }
1372
1373 return LY_SUCCESS;
1374}
1375
1376/**
1377 * @brief Update operations in a diff node when the new operation is CREATE.
1378 *
1379 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001380 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001381 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001382 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001383 * @return LY_ERR value.
1384 */
1385static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001386lyd_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 +02001387{
1388 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001389 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001390 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001391 const char *meta_name, *orig_meta_name;
1392 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001393 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001394
1395 switch (cur_op) {
1396 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001397 /* remember current flags */
1398 trg_flags = diff_match->flags;
1399
Michal Vaskoe78faec2021-04-08 17:24:43 +02001400 if (lysc_is_userordered(diff_match->schema)) {
1401 /* get anchor metadata */
1402 if (lysc_is_dup_inst_list(diff_match->schema)) {
1403 meta_name = "yang:position";
1404 orig_meta_name = "yang:orig-position";
1405 } else if (diff_match->schema->nodetype == LYS_LIST) {
1406 meta_name = "yang:key";
1407 orig_meta_name = "yang:orig-key";
1408 } else {
1409 meta_name = "yang:value";
1410 orig_meta_name = "yang:orig-value";
1411 }
1412 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001413 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001414 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001415 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001416
1417 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1418 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001419 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001420 /* deleted + created at another position -> operation REPLACE */
1421 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1422
1423 /* add anchor metadata */
1424 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1425 } else {
1426 /* deleted + created at the same position -> operation NONE */
1427 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1428
1429 /* delete anchor metadata */
1430 lyd_free_meta_single(orig_meta);
1431 }
1432 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1433 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1434 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1435 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1436 }
1437
Radek Krejci55c4bd22021-04-26 08:09:04 +02001438 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1439 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001440 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1441 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1442 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1443 /* deleted + created -> operation NONE */
1444 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1445 } else {
1446 /* we deleted it, but it was created with a different value -> operation REPLACE */
1447 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1448
1449 /* current value is the previous one (meta) */
1450 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001451 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001452
1453 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001454 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001455 }
1456 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001457 /* deleted + created -> operation NONE */
1458 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001459 }
1460
1461 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001462 /* add orig-dflt metadata */
1463 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1464 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1465
Michal Vaskoe6323f62020-07-09 15:49:02 +02001466 /* update dflt flag itself */
1467 diff_match->flags &= ~LYD_DEFAULT;
1468 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001469 }
1470
1471 /* but the operation of its children should remain DELETE */
1472 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1473 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001474 }
1475 break;
1476 default:
1477 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001478 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1479 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001480 }
1481
1482 return LY_SUCCESS;
1483}
1484
1485/**
1486 * @brief Update operations on a diff node when the new operation is DELETE.
1487 *
1488 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001489 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001490 * @param[in] src_diff Current source diff node.
1491 * @return LY_ERR value.
1492 */
1493static LY_ERR
1494lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1495{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001496 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001497 struct lyd_meta *meta;
1498 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001499 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001500
1501 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001502 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 +02001503
1504 switch (cur_op) {
1505 case LYD_DIFF_OP_CREATE:
1506 /* it was created, but then deleted -> set NONE operation */
1507 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1508
1509 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1510 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001511 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1512 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001513 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001514 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001515 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001516 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1517 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001518 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001519 break;
1520 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001521 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001522 if (lysc_is_userordered(diff_match->schema)) {
1523 if (lysc_is_dup_inst_list(diff_match->schema)) {
1524 meta_name = "position";
1525 } else if (diff_match->schema->nodetype == LYS_LIST) {
1526 meta_name = "key";
1527 } else {
1528 meta_name = "value";
1529 }
1530 } else {
1531 assert(diff_match->schema->nodetype == LYS_LEAF);
1532
1533 /* switch value for the original one */
1534 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001535 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001536 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001537 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1538 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001539 }
1540
1541 /* switch default for the original one, then remove the meta */
1542 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001543 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001544 diff_match->flags &= ~LYD_DEFAULT;
1545 if (meta->value.boolean) {
1546 diff_match->flags |= LYD_DEFAULT;
1547 }
1548 lyd_free_meta_single(meta);
1549
1550 meta_name = "orig-value";
1551 }
1552 lyd_diff_del_meta(diff_match, meta_name);
1553
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001554 /* it was being changed, but should be deleted instead -> set DELETE operation */
1555 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1556 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001557 case LYD_DIFF_OP_NONE:
1558 /* it was not modified, but should be deleted -> set DELETE operation */
1559 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001560 break;
1561 default:
1562 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001563 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1564 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001565 }
1566
1567 return LY_SUCCESS;
1568}
1569
1570/**
1571 * @brief Check whether this diff node is redundant (does not change data).
1572 *
1573 * @param[in] diff Diff node.
1574 * @return 0 if not, non-zero if it is.
1575 */
1576static int
1577lyd_diff_is_redundant(struct lyd_node *diff)
1578{
1579 enum lyd_diff_op op;
1580 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1581 struct lyd_node *child;
1582 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001583 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001584
1585 assert(diff);
1586
Michal Vaskoe78faec2021-04-08 17:24:43 +02001587 if (lysc_is_dup_inst_list(diff->schema)) {
1588 /* all descendants are keys */
1589 child = NULL;
1590 } else {
1591 child = lyd_child_no_keys(diff);
1592 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001593 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001594 assert(mod);
1595
1596 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001597 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001598
1599 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001600 /* get metadata names */
1601 if (lysc_is_dup_inst_list(diff->schema)) {
1602 meta_name = "position";
1603 orig_meta_name = "orig-position";
1604 } else if (diff->schema->nodetype == LYS_LIST) {
1605 meta_name = "key";
1606 orig_meta_name = "orig-key";
1607 } else {
1608 meta_name = "value";
1609 orig_meta_name = "orig-value";
1610 }
1611
Michal Vaskoe6323f62020-07-09 15:49:02 +02001612 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001613 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1614 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001615 assert(orig_val_meta && val_meta);
1616
1617 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1618 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001619 lyd_free_meta_single(orig_val_meta);
1620 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001621 if (child) {
1622 /* change operation to NONE, we have siblings */
1623 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1624 return 0;
1625 }
1626
1627 /* redundant node, BUT !!
1628 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1629 * because the data that this is applied on should not change for the diff lifetime.
1630 * However, when we are merging 2 diffs, this conversion is actually lossy because
1631 * if the data change, the move operation can also change its meaning. In this specific
1632 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1633 */
1634 return 1;
1635 }
1636 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1637 /* check whether at least the default flags are different */
1638 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1639 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001640 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001641
1642 /* if previous and current dflt flags are the same, this node is redundant */
1643 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1644 return 1;
1645 }
1646 return 0;
1647 }
1648
1649 if (!child && (op == LYD_DIFF_OP_NONE)) {
1650 return 1;
1651 }
1652
1653 return 0;
1654}
1655
1656/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001657 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001658 *
1659 * @param[in] src_diff Source diff node.
1660 * @param[in] diff_parent Current sysrepo diff parent.
1661 * @param[in] diff_cb Optional diff callback.
1662 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001663 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001664 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001665 * @param[in,out] diff Diff root node.
1666 * @return LY_ERR value.
1667 */
1668static LY_ERR
1669lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vaskod7c048c2021-05-18 16:12:55 +02001670 struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001671{
1672 LY_ERR ret = LY_SUCCESS;
1673 struct lyd_node *child, *diff_node = NULL;
1674 enum lyd_diff_op src_op, cur_op;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001675 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001676
1677 /* get source node operation */
1678 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1679
1680 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001681 LY_CHECK_RET(lyd_diff_find_match(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, 1, dup_inst, &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001682
1683 if (diff_node) {
1684 /* get target (current) operation */
1685 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1686
1687 /* merge operations */
1688 switch (src_op) {
1689 case LYD_DIFF_OP_REPLACE:
1690 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1691 break;
1692 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001693 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001694 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001695 goto add_diff;
1696 }
1697
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001698 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001699 break;
1700 case LYD_DIFF_OP_DELETE:
1701 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1702 break;
1703 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001704 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1705 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001706 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1707 break;
1708 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001709 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001710 }
1711 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001712 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001713 return ret;
1714 }
1715
1716 if (diff_cb) {
1717 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001718 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001719 }
1720
1721 /* update diff parent */
1722 diff_parent = diff_node;
1723
Michal Vaskoe78faec2021-04-08 17:24:43 +02001724 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1725 * so there is nothing to merge for these "keys" */
1726 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1727 /* merge src_diff recursively */
1728 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1729 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1730 if (ret) {
1731 break;
1732 }
1733 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001734 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001735 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001736 }
1737 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001738add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001739 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001740 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1741 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001742
1743 /* insert node into diff if not already */
1744 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001745 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001746 }
1747
1748 /* update operation */
1749 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1750
1751 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001752 /* call callback with no source diff node since it was duplicated and just added */
1753 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001754 }
1755
1756 /* update diff parent */
1757 diff_parent = diff_node;
1758 }
1759
1760 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001761 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001762 if (diff_parent == *diff) {
1763 *diff = (*diff)->next;
1764 }
1765 lyd_free_tree(diff_parent);
1766 }
1767
1768 return LY_SUCCESS;
1769}
1770
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001771LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001772lyd_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 +01001773 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001774{
1775 const struct lyd_node *src_root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001776 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001777 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001778
1779 LY_LIST_FOR(src_diff, src_root) {
1780 if (mod && (lyd_owner_module(src_root) != mod)) {
1781 /* skip data nodes from different modules */
1782 continue;
1783 }
1784
1785 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001786 LY_CHECK_GOTO(ret = lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, &dup_inst, options, diff), cleanup);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001787 }
1788
Michal Vaskoe78faec2021-04-08 17:24:43 +02001789cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001790 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001791 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001792}
1793
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001794LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001795lyd_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 +01001796 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001797{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001798 LY_ERR ret;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001799 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001800
Michal Vasko04f85912020-08-07 12:14:58 +02001801 if (!src_sibling) {
1802 return LY_SUCCESS;
1803 }
1804
Michal Vaskoe78faec2021-04-08 17:24:43 +02001805 ret = lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, &dup_inst, options, diff_first);
Michal Vaskod7c048c2021-05-18 16:12:55 +02001806 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001807 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001808}
1809
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001810LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001811lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001812{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001813 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001814}
Michal Vasko4231fb62020-07-13 13:54:47 +02001815
1816static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001817lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001818{
1819 LY_ERR ret = LY_SUCCESS;
1820 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001821 const char *val1 = NULL;
1822 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001823 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001824
Michal Vaskobaba84e2021-02-05 16:33:30 +01001825 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1826
1827 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001828 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001829
1830 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001831 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001832
1833 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001834 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001835 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001836 } else {
1837 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1838 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001839
1840 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001841 flags = node->flags;
1842 if (node->schema->nodetype == LYS_LEAF) {
1843 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1844 } else {
1845 union lyd_any_value anyval = {.str = val1};
1846 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1847 }
1848 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001849 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1850
1851cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001852 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001853 return ret;
1854}
1855
1856static LY_ERR
1857lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1858{
1859 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001860 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001861
1862 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001863 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001864
1865 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001866 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001867 flag1 = LYD_DEFAULT;
1868 } else {
1869 flag1 = 0;
1870 }
1871
1872 /* current default */
1873 flag2 = node->flags & LYD_DEFAULT;
1874
Michal Vasko610e93b2020-11-09 20:58:32 +01001875 if (flag1 == flag2) {
1876 /* no default state change so nothing to reverse */
1877 return LY_SUCCESS;
1878 }
1879
Michal Vasko4231fb62020-07-13 13:54:47 +02001880 /* switch defaults */
1881 node->flags &= ~LYD_DEFAULT;
1882 node->flags |= flag1;
1883 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1884
1885 return LY_SUCCESS;
1886}
1887
1888static LY_ERR
1889lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1890{
1891 LY_ERR ret = LY_SUCCESS;
1892 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001893 const char *val1 = NULL;
1894 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001895
1896 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001897 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001898
1899 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001900 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001901
1902 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001903 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02001904
1905 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001906 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02001907
1908 /* switch values */
1909 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1910 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1911
1912cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001913 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001914 return ret;
1915}
1916
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001917/**
1918 * @brief Remove specific operation from all the nodes in a subtree.
1919 *
1920 * @param[in] diff Diff subtree to process.
1921 * @param[in] op Only expected operation.
1922 * @return LY_ERR value.
1923 */
1924static LY_ERR
1925lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
1926{
1927 struct lyd_node *elem;
1928 struct lyd_meta *meta;
1929
1930 LYD_TREE_DFS_BEGIN(diff, elem) {
1931 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
1932 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001933 LY_CHECK_ERR_RET(lyd_diff_str2op(lyd_get_meta_value(meta)) != op, LOGINT(LYD_CTX(diff)), LY_EINT);
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001934 lyd_free_meta_single(meta);
1935 }
1936
1937 LYD_TREE_DFS_END(diff, elem);
1938 }
1939
1940 return LY_SUCCESS;
1941}
1942
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001943LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001944lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001945{
1946 LY_ERR ret = LY_SUCCESS;
1947 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001948 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02001949 enum lyd_diff_op op;
1950
1951 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1952
1953 if (!src_diff) {
1954 *diff = NULL;
1955 return LY_SUCCESS;
1956 }
1957
1958 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001959 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001960
1961 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001962 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1963 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001964
1965 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001966 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001967 /* skip all keys */
1968 if (!lysc_is_key(elem->schema)) {
1969 /* find operation attribute, if any */
1970 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001971
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001972 switch (op) {
1973 case LYD_DIFF_OP_CREATE:
1974 /* reverse create to delete */
1975 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001976
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001977 /* check all the children for the same operation, nothing else is expected */
1978 LY_LIST_FOR(lyd_child(elem), iter) {
1979 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
1980 }
1981
Michal Vasko9e070522021-03-05 14:00:14 +01001982 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001983 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001984 case LYD_DIFF_OP_DELETE:
1985 /* reverse delete to create */
1986 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001987
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001988 /* check all the children for the same operation, nothing else is expected */
1989 LY_LIST_FOR(lyd_child(elem), iter) {
1990 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
1991 }
1992
Michal Vasko9e070522021-03-05 14:00:14 +01001993 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001994 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001995 case LYD_DIFF_OP_REPLACE:
1996 switch (elem->schema->nodetype) {
1997 case LYS_LEAF:
1998 /* leaf value change */
1999 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2000 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2001 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002002 case LYS_ANYXML:
2003 case LYS_ANYDATA:
2004 /* any value change */
2005 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2006 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002007 case LYS_LEAFLIST:
2008 /* leaf-list move */
2009 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002010 if (lysc_is_dup_inst_list(elem->schema)) {
2011 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2012 } else {
2013 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2014 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002015 break;
2016 case LYS_LIST:
2017 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002018 if (lysc_is_dup_inst_list(elem->schema)) {
2019 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2020 } else {
2021 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2022 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002023 break;
2024 default:
2025 LOGINT(LYD_CTX(src_diff));
2026 ret = LY_EINT;
2027 goto cleanup;
2028 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002029 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002030 case LYD_DIFF_OP_NONE:
2031 switch (elem->schema->nodetype) {
2032 case LYS_LEAF:
2033 case LYS_LEAFLIST:
2034 /* default flag change */
2035 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2036 break;
2037 default:
2038 /* nothing to do */
2039 break;
2040 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002041 break;
2042 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002043 }
2044
Michal Vasko56daf732020-08-10 10:57:18 +02002045 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002046 }
2047 }
2048
2049cleanup:
2050 if (ret) {
2051 lyd_free_siblings(*diff);
2052 *diff = NULL;
2053 }
2054 return ret;
2055}