blob: a3ad4110682ae9f60c8863ea2a41b8778abb3664 [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"
Michal Vasko19175b62022-04-01 09:17:07 +020029#include "plugins_exts.h"
Radek Krejci47fab892020-11-05 17:02:41 +010030#include "plugins_types.h"
31#include "set.h"
32#include "tree.h"
33#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020034#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010035#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020036#include "tree_schema.h"
37#include "tree_schema_internal.h"
38
Michal Vasko52afd7d2022-01-18 14:08:34 +010039#define LOGERR_META(ctx, meta_name, node) \
40 { \
41 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
42 LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \
43 free(__path); \
44 }
45
46#define LOGERR_NOINST(ctx, node) \
47 { \
48 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
49 LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \
50 free(__path); \
51 }
52
53#define LOGERR_UNEXPVAL(ctx, node, data_source) \
54 { \
55 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
56 LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \
57 free(__path); \
58 }
59
60#define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \
61 { \
62 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
63 LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \
64 lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \
65 free(__path); \
66 }
67
Michal Vaskod59035b2020-07-08 12:00:06 +020068static const char *
69lyd_diff_op2str(enum lyd_diff_op op)
70{
71 switch (op) {
72 case LYD_DIFF_OP_CREATE:
73 return "create";
74 case LYD_DIFF_OP_DELETE:
75 return "delete";
76 case LYD_DIFF_OP_REPLACE:
77 return "replace";
78 case LYD_DIFF_OP_NONE:
79 return "none";
80 }
81
82 LOGINT(NULL);
83 return NULL;
84}
85
Michal Vaskoe6323f62020-07-09 15:49:02 +020086static enum lyd_diff_op
87lyd_diff_str2op(const char *str)
88{
89 switch (str[0]) {
90 case 'c':
91 assert(!strcmp(str, "create"));
92 return LYD_DIFF_OP_CREATE;
93 case 'd':
94 assert(!strcmp(str, "delete"));
95 return LYD_DIFF_OP_DELETE;
96 case 'r':
97 assert(!strcmp(str, "replace"));
98 return LYD_DIFF_OP_REPLACE;
99 case 'n':
100 assert(!strcmp(str, "none"));
101 return LYD_DIFF_OP_NONE;
102 }
103
104 LOGINT(NULL);
105 return 0;
106}
107
Michal Vaskod59035b2020-07-08 12:00:06 +0200108LY_ERR
109lyd_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 +0200110 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
111 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200112{
113 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
114 const struct lyd_node *parent = NULL;
115 const struct lys_module *yang_mod;
116
117 assert(diff);
118
Michal Vasko53d48422020-11-13 18:02:29 +0100119 /* replace leaf always needs orig-default and orig-value */
120 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
121
122 /* create on userord needs key/value */
123 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200124 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100125 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200126 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100127
128 /* move on userord needs both key and orig-key/value and orig-value */
129 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200130 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100131 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200132 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
133 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100134
Michal Vaskod59035b2020-07-08 12:00:06 +0200135 /* find the first existing parent */
136 siblings = *diff;
137 while (1) {
138 /* find next node parent */
139 parent = node;
140 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100141 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200142 }
143 if (parent == node) {
144 /* no more parents to find */
145 break;
146 }
147
148 /* check whether it exists in the diff */
149 if (lyd_find_sibling_first(siblings, parent, &match)) {
150 break;
151 }
152
153 /* another parent found */
154 diff_parent = match;
155
156 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200157 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200158 }
159
160 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200161 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100162 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200163
164 /* find the first duplicated parent */
165 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100166 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200167 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100168 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200169 }
170 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100171 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200172 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100173 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200174 }
175 }
176
177 /* no parent existed, must be manually connected */
178 if (!diff_parent) {
179 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200180 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200181 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200182 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200183 }
184
185 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200186 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200187 assert(!strcmp(yang_mod->name, "yang"));
188
189 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200190 if (diff_parent && (diff_parent != dup)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100191 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200192 }
193
194 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100195 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 +0200196
197 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200198 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100199 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 +0200200 }
201
202 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200203 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100204 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 +0200205 }
206
207 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200208 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100209 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200210 }
211
212 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200213 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100214 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200215 }
216
Michal Vaskoe78faec2021-04-08 17:24:43 +0200217 /* position */
218 if (position) {
219 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "position", position, 0, NULL));
220 }
221
Michal Vaskod59035b2020-07-08 12:00:06 +0200222 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200223 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100224 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 +0200225 }
226
Michal Vaskoe78faec2021-04-08 17:24:43 +0200227 /* orig-position */
228 if (orig_position) {
229 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-position", orig_position, 0, NULL));
230 }
231
Michal Vaskod59035b2020-07-08 12:00:06 +0200232 return LY_SUCCESS;
233}
234
235/**
236 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
237 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100238 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200239 * @param[in] schema Schema node of the list/leaf-list.
240 * @param[in,out] userord Sized array of userord items.
241 * @return Userord item for all the user-ordered list/leaf-list instances.
242 */
243static struct lyd_diff_userord *
244lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
245{
246 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200247 struct lyd_node *iter;
248 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200249 LY_ARRAY_COUNT_TYPE u;
250
251 LY_ARRAY_FOR(*userord, u) {
252 if ((*userord)[u].schema == schema) {
253 return &(*userord)[u];
254 }
255 }
256
257 /* it was not added yet, add it now */
258 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
259
260 item->schema = schema;
261 item->pos = 0;
262 item->inst = NULL;
263
264 /* store all the instance pointers in the current order */
265 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200266 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
267 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
268 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200269 }
270 }
271
272 return item;
273}
274
275/**
276 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
277 * lists/leaf-lists.
278 *
279 * @param[in] first Node from the first tree, can be NULL (on create).
280 * @param[in] second Node from the second tree, can be NULL (on delete).
281 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100282 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200283 * @param[out] op Operation.
284 * @param[out] orig_default Original default metadata.
285 * @param[out] value Value metadata.
286 * @param[out] orig_value Original value metadata
287 * @param[out] key Key metadata.
288 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200289 * @param[out] position Position metadata.
290 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200291 * @return LY_SUCCESS on success,
292 * @return LY_ENOT if there is no change to be added into diff,
293 * @return LY_ERR value on other errors.
294 */
295static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200296lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100297 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200298 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200299{
300 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200301 size_t buflen, bufused;
302 uint32_t first_pos, second_pos;
Michal Vaskod59035b2020-07-08 12:00:06 +0200303
304 assert(first || second);
305
306 *orig_default = NULL;
307 *value = NULL;
308 *orig_value = NULL;
309 *key = NULL;
310 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200311 *position = NULL;
312 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200313
314 schema = first ? first->schema : second->schema;
315 assert(lysc_is_userordered(schema));
316
Michal Vaskod59035b2020-07-08 12:00:06 +0200317 /* find user-ordered first position */
318 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200319 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200320 if (userord_item->inst[first_pos] == first) {
321 break;
322 }
323 }
324 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
325 } else {
326 first_pos = 0;
327 }
328
Michal Vaskoe78faec2021-04-08 17:24:43 +0200329 /* prepare position of the next instance */
330 second_pos = userord_item->pos++;
331
Michal Vaskod59035b2020-07-08 12:00:06 +0200332 /* learn operation first */
333 if (!second) {
334 *op = LYD_DIFF_OP_DELETE;
335 } else if (!first) {
336 *op = LYD_DIFF_OP_CREATE;
337 } else {
Michal Vasko8f359bf2020-07-28 10:41:15 +0200338 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200339 /* in first, there is a different instance on the second position, we are going to move 'first' node */
340 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200341 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200342 /* default flag change */
343 *op = LYD_DIFF_OP_NONE;
344 } else {
345 /* no changes */
346 return LY_ENOT;
347 }
348 }
349
350 /*
351 * set each attribute correctly based on the operation and node type
352 */
353
354 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100355 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200356 if (first->flags & LYD_DEFAULT) {
357 *orig_default = "true";
358 } else {
359 *orig_default = "false";
360 }
361 }
362
363 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200364 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
365 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200366 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200367 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200368 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200369 } else {
370 *value = strdup("");
371 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
372 }
373 }
374
375 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200376 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
377 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200378 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200379 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200380 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200381 } else {
382 *orig_value = strdup("");
383 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
384 }
385 }
386
387 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200388 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
389 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200390 if (second_pos) {
391 buflen = bufused = 0;
392 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
393 } else {
394 *key = strdup("");
395 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
396 }
397 }
398
399 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200400 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
401 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200402 if (first_pos) {
403 buflen = bufused = 0;
404 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
405 } else {
406 *orig_key = strdup("");
407 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
408 }
409 }
410
Michal Vaskoe78faec2021-04-08 17:24:43 +0200411 /* position */
412 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
413 if (second_pos) {
414 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
415 LOGMEM(schema->module->ctx);
416 return LY_EMEM;
417 }
418 } else {
419 *position = strdup("");
420 LY_CHECK_ERR_RET(!*position, LOGMEM(schema->module->ctx), LY_EMEM);
421 }
422 }
423
424 /* orig-position */
425 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
426 if (first_pos) {
427 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
428 LOGMEM(schema->module->ctx);
429 return LY_EMEM;
430 }
431 } else {
432 *orig_position = strdup("");
433 LY_CHECK_ERR_RET(!*orig_position, LOGMEM(schema->module->ctx), LY_EMEM);
434 }
435 }
436
Michal Vaskod59035b2020-07-08 12:00:06 +0200437 /*
438 * update our instances - apply the change
439 */
440 if (*op == LYD_DIFF_OP_CREATE) {
441 /* insert the instance */
Michal Vasko5cde11b2020-12-08 10:04:48 +0100442 LY_ARRAY_CREATE_RET(schema->module->ctx, userord_item->inst, 1, LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200443 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
444 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
445 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
446 }
447 LY_ARRAY_INCREMENT(userord_item->inst);
448 userord_item->inst[second_pos] = second;
449
450 } else if (*op == LYD_DIFF_OP_DELETE) {
451 /* remove the instance */
452 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
453 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
454 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
455 }
456 LY_ARRAY_DECREMENT(userord_item->inst);
457
458 } else if (*op == LYD_DIFF_OP_REPLACE) {
459 /* move the instances */
460 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
461 (first_pos - second_pos) * sizeof *userord_item->inst);
462 userord_item->inst[second_pos] = first;
463 }
464
465 return LY_SUCCESS;
466}
467
468/**
469 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
470 * lists/leaf-lists.
471 *
472 * @param[in] first Node from the first tree, can be NULL (on create).
473 * @param[in] second Node from the second tree, can be NULL (on delete).
474 * @param[in] options Diff options.
475 * @param[out] op Operation.
476 * @param[out] orig_default Original default metadata.
477 * @param[out] orig_value Original value metadata.
478 * @return LY_SUCCESS on success,
479 * @return LY_ENOT if there is no change to be added into diff,
480 * @return LY_ERR value on other errors.
481 */
482static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200483lyd_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 +0200484 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200485{
486 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200487 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200488
489 assert(first || second);
490
491 *orig_default = NULL;
492 *orig_value = NULL;
493
494 schema = first ? first->schema : second->schema;
495 assert(!lysc_is_userordered(schema));
496
497 /* learn operation first */
498 if (!second) {
499 *op = LYD_DIFF_OP_DELETE;
500 } else if (!first) {
501 *op = LYD_DIFF_OP_CREATE;
502 } else {
503 switch (schema->nodetype) {
504 case LYS_CONTAINER:
505 case LYS_RPC:
506 case LYS_ACTION:
507 case LYS_NOTIF:
508 /* no changes */
509 return LY_ENOT;
510 case LYS_LIST:
511 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200512 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200513 /* default flag change */
514 *op = LYD_DIFF_OP_NONE;
515 } else {
516 /* no changes */
517 return LY_ENOT;
518 }
519 break;
520 case LYS_LEAF:
521 case LYS_ANYXML:
522 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200523 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200524 /* different values */
525 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200526 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200527 /* default flag change */
528 *op = LYD_DIFF_OP_NONE;
529 } else {
530 /* no changes */
531 return LY_ENOT;
532 }
533 break;
534 default:
535 LOGINT_RET(schema->module->ctx);
536 }
537 }
538
539 /*
540 * set each attribute correctly based on the operation and node type
541 */
542
543 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100544 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200545 if (first->flags & LYD_DEFAULT) {
546 *orig_default = "true";
547 } else {
548 *orig_default = "false";
549 }
550 }
551
552 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100553 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
554 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200555 str_val = lyd_get_value(first);
556 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100557 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
558 } else {
559 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
560 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200561 }
562
563 return LY_SUCCESS;
564}
565
566/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200567 * @brief Find a matching instance of a node in a data tree.
568 *
569 * @param[in] siblings Siblings to search in.
570 * @param[in] target Target node to search for.
571 * @param[in] defaults Whether to consider (or ignore) default values.
572 * @param[in,out] dup_inst_cache Duplicate instance cache.
573 * @param[out] match Found match, NULL if no matching node found.
574 * @return LY_ERR value.
575 */
576static LY_ERR
577lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200578 struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200579{
Michal Vaskoe78faec2021-04-08 17:24:43 +0200580 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
581 /* try to find the exact instance */
582 lyd_find_sibling_first(siblings, target, match);
583 } else {
584 /* try to simply find the node, there cannot be more instances */
585 lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
586 }
587
Michal Vaskod7c048c2021-05-18 16:12:55 +0200588 /* update match as needed */
589 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200590
591 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
592 /* ignore default nodes */
593 *match = NULL;
594 }
595 return LY_SUCCESS;
596}
597
598/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200599 * @brief Perform diff for all siblings at certain depth, recursively.
600 *
601 * For user-ordered lists/leaf-lists a specific structure is used for storing
602 * the current order. The idea is to apply all the generated diff changes
603 * virtually on the first tree so that we can continue to generate correct
604 * changes after some were already generated.
605 *
606 * The algorithm then uses second tree position-based changes with a before
607 * (preceding) item anchor.
608 *
609 * Example:
610 *
611 * Virtual first tree leaf-list order:
612 * 1 2 [3] 4 5
613 *
614 * Second tree leaf-list order:
615 * 1 2 [5] 3 4
616 *
617 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
618 * match - they do not - move nodes so that the 3rd position node is final ->
619 * -> move node 5 to the 3rd position -> move node 5 after node 2.
620 *
621 * Required properties:
622 * Stored operations (move) should not be affected by later operations -
623 * - would cause a redundantly long list of operations, possibly inifinite.
624 *
625 * Implemenation justification:
626 * First, all delete operations and only then move/create operations are stored.
627 * Also, preceding anchor is used and after each iteration another node is
628 * at its final position. That results in the invariant that all preceding
629 * nodes are final and will not be changed by the later operations, meaning
630 * they can safely be used as anchors for the later operations.
631 *
632 * @param[in] first First tree first sibling.
633 * @param[in] second Second tree first sibling.
634 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200635 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200636 * @param[in,out] diff Diff to append to.
637 * @return LY_ERR value.
638 */
639static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200640lyd_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 +0200641 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200642{
643 LY_ERR ret = LY_SUCCESS;
644 const struct lyd_node *iter_first, *iter_second;
645 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100646 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200647 struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200648 LY_ARRAY_COUNT_TYPE u;
649 enum lyd_diff_op op;
650 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200651 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200652
Michal Vaskod59035b2020-07-08 12:00:06 +0200653 /* compare first tree to the second tree - delete, replace, none */
654 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200655 if (!iter_first->schema) {
656 continue;
657 }
658
Michal Vaskod59035b2020-07-08 12:00:06 +0200659 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200660 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200661 /* skip default nodes */
662 continue;
663 }
664
Michal Vaskoe78faec2021-04-08 17:24:43 +0200665 /* find a match in the second tree */
666 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
667 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200668
669 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100670 /* get (create) userord entry */
671 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
672 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
673
Michal Vaskoe78faec2021-04-08 17:24:43 +0200674 /* we are handling only user-ordered node delete now */
675 if (!match_second) {
676 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100677 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
678 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200679
680 /* there must be changes, it is deleted */
681 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100682 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
683 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200684
685 free(orig_value);
686 free(key);
687 free(value);
688 free(position);
689 free(orig_key);
690 free(orig_position);
691 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200692 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200693 } else {
694 /* get all the attributes */
695 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
696
697 /* add into diff if there are any changes */
698 if (!ret) {
699 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200700 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200701 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100702 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200703 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200704 }
705
706 free(orig_value);
707 LY_CHECK_GOTO(ret, cleanup);
708 } else if (ret == LY_ENOT) {
709 ret = LY_SUCCESS;
710 } else {
711 goto cleanup;
712 }
713 }
714
715 /* check descendants, if any, recursively */
716 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200717 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
718 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200719 }
720
721 if (nosiblings) {
722 break;
723 }
724 }
725
726 /* reset all cached positions */
727 LY_ARRAY_FOR(userord, u) {
728 userord[u].pos = 0;
729 }
730
731 /* compare second tree to the first tree - create, user-ordered move */
732 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200733 if (!iter_second->schema) {
734 continue;
735 }
736
Michal Vaskod59035b2020-07-08 12:00:06 +0200737 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200738 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200739 /* skip default nodes */
740 continue;
741 }
742
Michal Vaskoe78faec2021-04-08 17:24:43 +0200743 /* find a match in the first tree */
744 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
745 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200746
747 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100748 /* get userord entry */
749 userord_item = lyd_diff_userord_get(NULL, iter_second->schema, &userord);
750 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
751
Michal Vaskod59035b2020-07-08 12:00:06 +0200752 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100753 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200754 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200755
756 /* add into diff if there are any changes */
757 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200758 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
759 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200760
761 free(orig_value);
762 free(key);
763 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200764 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200765 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200766 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200767 LY_CHECK_GOTO(ret, cleanup);
768 } else if (ret == LY_ENOT) {
769 ret = LY_SUCCESS;
770 } else {
771 goto cleanup;
772 }
773 } else if (!match_first) {
774 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200775 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 +0200776
777 /* there must be changes, it is created */
778 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200779 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200780
781 free(orig_value);
782 LY_CHECK_GOTO(ret, cleanup);
783 } /* else was handled */
784
785 if (nosiblings) {
786 break;
787 }
788 }
789
790cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200791 lyd_dup_inst_free(dup_inst_first);
792 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200793 LY_ARRAY_FOR(userord, u) {
794 LY_ARRAY_FREE(userord[u].inst);
795 }
796 LY_ARRAY_FREE(userord);
797 return ret;
798}
799
Michal Vasko3a41dff2020-07-15 14:30:28 +0200800static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100801lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
802 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200803{
804 const struct ly_ctx *ctx;
805
806 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
807
808 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200809 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200810 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200811 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200812 } else {
813 ctx = NULL;
814 }
815
816 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
817 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
818 return LY_EINVAL;
819 }
820
821 *diff = NULL;
822
Michal Vasko3a41dff2020-07-15 14:30:28 +0200823 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
824}
825
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100826LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200827lyd_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 +0200828{
829 return lyd_diff(first, second, options, 1, diff);
830}
831
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100832LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200833lyd_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 +0200834{
835 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200836}
837
838/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200839 * @brief Learn operation of a diff node.
840 *
841 * @param[in] diff_node Diff node.
842 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200843 * @return LY_ERR value.
844 */
845static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200846lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200847{
848 struct lyd_meta *meta = NULL;
849 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200850 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100851 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +0200852
Michal Vasko9e685082021-01-29 14:49:09 +0100853 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200854 LY_LIST_FOR(diff_parent->meta, meta) {
855 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200856 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200857 if ((str[0] == 'r') && (diff_parent != diff_node)) {
858 /* we do not care about this operation if it's in our parent */
859 continue;
860 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200861 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200862 break;
863 }
864 }
865 if (meta) {
866 break;
867 }
868 }
Michal Vasko52afd7d2022-01-18 14:08:34 +0100869
870 if (!meta) {
871 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
872 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
873 free(path);
874 return LY_EINT;
875 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200876
Michal Vaskod59035b2020-07-08 12:00:06 +0200877 return LY_SUCCESS;
878}
879
880/**
881 * @brief Insert a diff node into a data tree.
882 *
883 * @param[in,out] first_node First sibling of the data tree.
884 * @param[in] parent_node Data tree sibling parent node.
885 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200886 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
887 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200888 * @return err_info, NULL on success.
889 */
890static LY_ERR
891lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200892 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200893{
894 LY_ERR ret;
895 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200896 uint32_t pos, anchor_pos;
897 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200898
899 assert(new_node);
900
901 if (!*first_node) {
902 if (!parent_node) {
903 /* no parent or siblings */
904 *first_node = new_node;
905 return LY_SUCCESS;
906 }
907
908 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200909 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200910 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200911 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200912 return LY_EINVAL;
913 }
Michal Vaskob104f112020-07-17 09:54:54 +0200914 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200915 }
916
Michal Vasko9e685082021-01-29 14:49:09 +0100917 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200918
Michal Vaskod59035b2020-07-08 12:00:06 +0200919 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200920 /* simple insert */
921 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200922 }
923
Michal Vaskoe78faec2021-04-08 17:24:43 +0200924 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200925 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200926 if (lysc_is_dup_inst_list(new_node->schema)) {
927 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +0100928 if (!anchor_pos) {
929 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
930 return LY_EINVAL;
931 }
Michal Vaskoe78faec2021-04-08 17:24:43 +0200932
933 found = 0;
934 pos = 1;
935 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
936 if (pos == anchor_pos) {
937 found = 1;
938 break;
939 }
940 ++pos;
941 }
942 if (!found) {
943 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
944 new_node->schema->name);
945 return LY_EINVAL;
946 }
947 } else {
948 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
949 if (ret == LY_ENOTFOUND) {
950 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
951 new_node->schema->name);
952 return LY_EINVAL;
953 } else if (ret) {
954 return ret;
955 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200956 }
957
958 /* insert after */
959 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
960 assert(new_node->prev == anchor);
961 if (*first_node == new_node) {
962 *first_node = anchor;
963 }
964 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +0200965 /* find the first instance */
966 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
967 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +0200968
Michal Vaskoea7d3232022-04-19 12:01:36 +0200969 if (anchor) {
970 /* insert before the first instance */
971 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
972 if ((*first_node)->prev->next) {
973 assert(!new_node->prev->next);
974 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200975 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200976 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +0200977 /* insert anywhere */
978 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200979 }
980 }
981
982 return LY_SUCCESS;
983}
984
985/**
986 * @brief Apply diff subtree on data tree nodes, recursively.
987 *
988 * @param[in,out] first_node First sibling of the data tree.
989 * @param[in] parent_node Parent of the first sibling.
990 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200991 * @param[in] diff_cb Optional diff callback.
992 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200993 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200994 * @return LY_ERR value.
995 */
996static LY_ERR
997lyd_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 +0200998 lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +0200999{
1000 LY_ERR ret;
1001 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001002 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001003 enum lyd_diff_op op;
1004 struct lyd_meta *meta;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001005 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001006 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001007
1008 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001009 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001010
Michal Vaskoe6323f62020-07-09 15:49:02 +02001011 /* handle specific user-ordered (leaf-)lists operations separately */
1012 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1013 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001014 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001015 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001016 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001017 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001018 /* duplicate the node */
1019 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001020 }
1021
Michal Vaskoe78faec2021-04-08 17:24:43 +02001022 /* get "key", "value", or "position" metadata string value */
1023 if (lysc_is_dup_inst_list(diff_node->schema)) {
1024 meta_str = "yang:position";
1025 } else if (diff_node->schema->nodetype == LYS_LIST) {
1026 meta_str = "yang:key";
1027 } else {
1028 meta_str = "yang:value";
1029 }
1030 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001031 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001032 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001033
Michal Vaskod59035b2020-07-08 12:00:06 +02001034 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001035 if (str_val[0]) {
1036 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001037 } else {
1038 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1039 }
1040 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001041 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001042 lyd_free_tree(match);
1043 }
1044 return ret;
1045 }
1046
1047 goto next_iter_r;
1048 }
1049
1050 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001051 switch (op) {
1052 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001053 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001054 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001055 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001056
1057 if (match->schema->nodetype & LYD_NODE_TERM) {
1058 /* special case of only dflt flag change */
1059 if (diff_node->flags & LYD_DEFAULT) {
1060 match->flags |= LYD_DEFAULT;
1061 } else {
1062 match->flags &= ~LYD_DEFAULT;
1063 }
1064 } else {
1065 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001066 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001067 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1068 LYD_NAME(diff_node));
1069 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001070 }
1071 }
1072 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001073 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001074 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001075 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001076
1077 /* insert it at the end */
1078 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001079 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001080 if (match->flags & LYD_EXT) {
1081 ret = lyd_insert_ext(parent_node, match);
1082 } else {
1083 ret = lyd_insert_child(parent_node, match);
1084 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001085 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001086 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001087 }
1088 if (ret) {
1089 lyd_free_tree(match);
1090 return ret;
1091 }
1092
1093 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001094 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001095 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001096 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001097 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001098
1099 /* remove it */
1100 if ((match == *first_node) && !match->parent) {
1101 assert(!parent_node);
1102 /* we have removed the top-level node */
1103 *first_node = (*first_node)->next;
1104 }
1105 lyd_free_tree(match);
1106
1107 /* we are not going recursively in this case, the whole subtree was already deleted */
1108 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001109 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001110 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1111 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1112 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1113 return LY_EINVAL;
1114 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001115
1116 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001117 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001118 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001119
Michal Vaskobaba84e2021-02-05 16:33:30 +01001120 /* update the value */
1121 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001122 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001123 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001124 } else {
1125 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001126 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001127 }
1128
1129 /* with flags */
1130 match->flags = diff_node->flags;
1131 break;
1132 default:
1133 LOGINT_RET(ctx);
1134 }
1135
1136next_iter_r:
1137 if (diff_cb) {
1138 /* call callback */
1139 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1140 }
1141
1142 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001143 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001144 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001145 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1146 if (ret) {
1147 break;
1148 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001149 }
1150
Michal Vaskod7c048c2021-05-18 16:12:55 +02001151 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001152 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001153}
1154
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001155LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001156lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001157 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001158{
1159 const struct lyd_node *root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001160 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001161 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001162
1163 LY_LIST_FOR(diff, root) {
1164 if (mod && (lyd_owner_module(root) != mod)) {
1165 /* skip data nodes from different modules */
1166 continue;
1167 }
1168
1169 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001170 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1171 if (ret) {
1172 break;
1173 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001174 }
1175
Michal Vaskod7c048c2021-05-18 16:12:55 +02001176 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001177 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001178}
1179
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001180LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001181lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001182{
1183 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1184}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001185
1186/**
1187 * @brief Update operations on a diff node when the new operation is NONE.
1188 *
1189 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001190 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001191 * @param[in] src_diff Current source diff node.
1192 * @return LY_ERR value.
1193 */
1194static LY_ERR
1195lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1196{
1197 switch (cur_op) {
1198 case LYD_DIFF_OP_NONE:
1199 case LYD_DIFF_OP_CREATE:
1200 case LYD_DIFF_OP_REPLACE:
1201 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1202 /* NONE on a term means only its dflt flag was changed */
1203 diff_match->flags &= ~LYD_DEFAULT;
1204 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1205 }
1206 break;
1207 default:
1208 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001209 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1210 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001211 }
1212
1213 return LY_SUCCESS;
1214}
1215
1216/**
1217 * @brief Remove an attribute from a node.
1218 *
1219 * @param[in] node Node with the metadata.
1220 * @param[in] name Metadata name.
1221 */
1222static void
1223lyd_diff_del_meta(struct lyd_node *node, const char *name)
1224{
1225 struct lyd_meta *meta;
1226
1227 LY_LIST_FOR(node->meta, meta) {
1228 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001229 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001230 return;
1231 }
1232 }
1233
1234 assert(0);
1235}
1236
1237/**
1238 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001239 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001240 *
1241 * @param[in] node Node to change.
1242 * @param[in] op Operation to set.
1243 * @return LY_ERR value.
1244 */
1245static LY_ERR
1246lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1247{
1248 struct lyd_meta *meta;
1249
1250 LY_LIST_FOR(node->meta, meta) {
1251 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001252 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001253 break;
1254 }
1255 }
1256
Michal Vasko871a0252020-11-11 18:35:24 +01001257 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001258}
1259
1260/**
1261 * @brief Update operations on a diff node when the new operation is REPLACE.
1262 *
1263 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001264 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001265 * @param[in] src_diff Current source diff node.
1266 * @return LY_ERR value.
1267 */
1268static LY_ERR
1269lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1270{
1271 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001272 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001273 struct lyd_meta *meta;
1274 const struct lys_module *mod;
1275 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001276 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001277
1278 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001279 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001280 assert(mod);
1281
1282 switch (cur_op) {
1283 case LYD_DIFF_OP_REPLACE:
1284 case LYD_DIFF_OP_CREATE:
1285 switch (diff_match->schema->nodetype) {
1286 case LYS_LIST:
1287 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001288 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001289 * keep orig_key/orig_value (only replace oper) and replace key/value */
1290 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001291 if (lysc_is_dup_inst_list(diff_match->schema)) {
1292 meta_name = "position";
1293 } else if (diff_match->schema->nodetype == LYS_LIST) {
1294 meta_name = "key";
1295 } else {
1296 meta_name = "value";
1297 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001298
1299 lyd_diff_del_meta(diff_match, meta_name);
1300 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001301 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001302 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001303 break;
1304 case LYS_LEAF:
1305 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001306 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001307 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1308 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001309 }
1310
Michal Vaskoe6323f62020-07-09 15:49:02 +02001311 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001312 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001313 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001314 }
1315
Michal Vasko8caadab2020-11-05 17:38:15 +01001316 if (cur_op == LYD_DIFF_OP_REPLACE) {
1317 /* compare values whether there is any change at all */
1318 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001319 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001320 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001321 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1322 if (!ret) {
1323 /* values are the same, remove orig-value meta and set oper to NONE */
1324 lyd_free_meta_single(meta);
1325 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1326 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001327 }
1328
1329 /* modify the default flag */
1330 diff_match->flags &= ~LYD_DEFAULT;
1331 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1332 break;
1333 case LYS_ANYXML:
1334 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001335 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001336 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1337 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001338 }
1339
1340 /* modify the node value */
1341 any = (struct lyd_node_any *)src_diff;
1342 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1343 break;
1344 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001345 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001346 }
1347 break;
1348 case LYD_DIFF_OP_NONE:
1349 /* it is moved now */
1350 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1351
1352 /* change the operation */
1353 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1354
Michal Vaskoe78faec2021-04-08 17:24:43 +02001355 /* set orig-meta and meta */
1356 if (lysc_is_dup_inst_list(diff_match->schema)) {
1357 meta_name = "position";
1358 orig_meta_name = "orig-position";
1359 } else {
1360 meta_name = "key";
1361 orig_meta_name = "orig-key";
1362 }
1363
1364 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001365 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001366 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001367
Michal Vaskoe78faec2021-04-08 17:24:43 +02001368 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001369 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001370 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001371 break;
1372 default:
1373 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001374 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1375 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001376 }
1377
1378 return LY_SUCCESS;
1379}
1380
1381/**
1382 * @brief Update operations in a diff node when the new operation is CREATE.
1383 *
1384 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001385 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001386 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001387 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001388 * @return LY_ERR value.
1389 */
1390static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001391lyd_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 +02001392{
1393 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001394 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001395 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001396 const char *meta_name, *orig_meta_name;
1397 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001398 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001399
1400 switch (cur_op) {
1401 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001402 /* remember current flags */
1403 trg_flags = diff_match->flags;
1404
Michal Vaskoe78faec2021-04-08 17:24:43 +02001405 if (lysc_is_userordered(diff_match->schema)) {
1406 /* get anchor metadata */
1407 if (lysc_is_dup_inst_list(diff_match->schema)) {
1408 meta_name = "yang:position";
1409 orig_meta_name = "yang:orig-position";
1410 } else if (diff_match->schema->nodetype == LYS_LIST) {
1411 meta_name = "yang:key";
1412 orig_meta_name = "yang:orig-key";
1413 } else {
1414 meta_name = "yang:value";
1415 orig_meta_name = "yang:orig-value";
1416 }
1417 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001418 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001419 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001420 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001421
1422 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1423 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001424 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001425 /* deleted + created at another position -> operation REPLACE */
1426 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1427
1428 /* add anchor metadata */
1429 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1430 } else {
1431 /* deleted + created at the same position -> operation NONE */
1432 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1433
1434 /* delete anchor metadata */
1435 lyd_free_meta_single(orig_meta);
1436 }
1437 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1438 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1439 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1440 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1441 }
1442
Radek Krejci55c4bd22021-04-26 08:09:04 +02001443 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1444 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001445 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1446 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1447 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1448 /* deleted + created -> operation NONE */
1449 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1450 } else {
1451 /* we deleted it, but it was created with a different value -> operation REPLACE */
1452 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1453
1454 /* current value is the previous one (meta) */
1455 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001456 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001457
1458 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001459 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001460 }
1461 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001462 /* deleted + created -> operation NONE */
1463 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001464 }
1465
1466 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001467 /* add orig-dflt metadata */
1468 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1469 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1470
Michal Vaskoe6323f62020-07-09 15:49:02 +02001471 /* update dflt flag itself */
1472 diff_match->flags &= ~LYD_DEFAULT;
1473 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001474 }
1475
1476 /* but the operation of its children should remain DELETE */
1477 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1478 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001479 }
1480 break;
1481 default:
1482 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001483 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1484 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001485 }
1486
1487 return LY_SUCCESS;
1488}
1489
1490/**
1491 * @brief Update operations on a diff node when the new operation is DELETE.
1492 *
1493 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001494 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001495 * @param[in] src_diff Current source diff node.
1496 * @return LY_ERR value.
1497 */
1498static LY_ERR
1499lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1500{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001501 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001502 struct lyd_meta *meta;
1503 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001504 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001505
1506 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001507 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 +02001508
1509 switch (cur_op) {
1510 case LYD_DIFF_OP_CREATE:
1511 /* it was created, but then deleted -> set NONE operation */
1512 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1513
1514 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1515 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001516 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1517 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001518 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001519 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001520 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001521 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1522 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001523 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001524 break;
1525 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001526 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001527 if (lysc_is_userordered(diff_match->schema)) {
1528 if (lysc_is_dup_inst_list(diff_match->schema)) {
1529 meta_name = "position";
1530 } else if (diff_match->schema->nodetype == LYS_LIST) {
1531 meta_name = "key";
1532 } else {
1533 meta_name = "value";
1534 }
1535 } else {
1536 assert(diff_match->schema->nodetype == LYS_LEAF);
1537
1538 /* switch value for the original one */
1539 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001540 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001541 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001542 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1543 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001544 }
1545
1546 /* switch default for the original one, then remove the meta */
1547 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001548 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001549 diff_match->flags &= ~LYD_DEFAULT;
1550 if (meta->value.boolean) {
1551 diff_match->flags |= LYD_DEFAULT;
1552 }
1553 lyd_free_meta_single(meta);
1554
1555 meta_name = "orig-value";
1556 }
1557 lyd_diff_del_meta(diff_match, meta_name);
1558
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001559 /* it was being changed, but should be deleted instead -> set DELETE operation */
1560 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1561 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001562 case LYD_DIFF_OP_NONE:
1563 /* it was not modified, but should be deleted -> set DELETE operation */
1564 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001565 break;
1566 default:
1567 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001568 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1569 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001570 }
1571
1572 return LY_SUCCESS;
1573}
1574
1575/**
1576 * @brief Check whether this diff node is redundant (does not change data).
1577 *
1578 * @param[in] diff Diff node.
1579 * @return 0 if not, non-zero if it is.
1580 */
1581static int
1582lyd_diff_is_redundant(struct lyd_node *diff)
1583{
1584 enum lyd_diff_op op;
1585 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1586 struct lyd_node *child;
1587 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001588 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001589
1590 assert(diff);
1591
Michal Vaskoe78faec2021-04-08 17:24:43 +02001592 if (lysc_is_dup_inst_list(diff->schema)) {
1593 /* all descendants are keys */
1594 child = NULL;
1595 } else {
1596 child = lyd_child_no_keys(diff);
1597 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001598 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001599 assert(mod);
1600
1601 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001602 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001603
1604 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001605 /* get metadata names */
1606 if (lysc_is_dup_inst_list(diff->schema)) {
1607 meta_name = "position";
1608 orig_meta_name = "orig-position";
1609 } else if (diff->schema->nodetype == LYS_LIST) {
1610 meta_name = "key";
1611 orig_meta_name = "orig-key";
1612 } else {
1613 meta_name = "value";
1614 orig_meta_name = "orig-value";
1615 }
1616
Michal Vaskoe6323f62020-07-09 15:49:02 +02001617 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001618 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1619 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001620 assert(orig_val_meta && val_meta);
1621
1622 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1623 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001624 lyd_free_meta_single(orig_val_meta);
1625 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001626 if (child) {
1627 /* change operation to NONE, we have siblings */
1628 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1629 return 0;
1630 }
1631
1632 /* redundant node, BUT !!
1633 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1634 * because the data that this is applied on should not change for the diff lifetime.
1635 * However, when we are merging 2 diffs, this conversion is actually lossy because
1636 * if the data change, the move operation can also change its meaning. In this specific
1637 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1638 */
1639 return 1;
1640 }
1641 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1642 /* check whether at least the default flags are different */
1643 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1644 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001645 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001646
1647 /* if previous and current dflt flags are the same, this node is redundant */
1648 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1649 return 1;
1650 }
1651 return 0;
1652 }
1653
1654 if (!child && (op == LYD_DIFF_OP_NONE)) {
1655 return 1;
1656 }
1657
1658 return 0;
1659}
1660
1661/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001662 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001663 *
1664 * @param[in] src_diff Source diff node.
1665 * @param[in] diff_parent Current sysrepo diff parent.
1666 * @param[in] diff_cb Optional diff callback.
1667 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001668 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001669 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001670 * @param[in,out] diff Diff root node.
1671 * @return LY_ERR value.
1672 */
1673static LY_ERR
1674lyd_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 +02001675 struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001676{
1677 LY_ERR ret = LY_SUCCESS;
1678 struct lyd_node *child, *diff_node = NULL;
1679 enum lyd_diff_op src_op, cur_op;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001680 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001681
1682 /* get source node operation */
1683 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1684
1685 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001686 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 +02001687
1688 if (diff_node) {
1689 /* get target (current) operation */
1690 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1691
1692 /* merge operations */
1693 switch (src_op) {
1694 case LYD_DIFF_OP_REPLACE:
1695 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1696 break;
1697 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001698 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001699 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001700 goto add_diff;
1701 }
1702
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001703 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001704 break;
1705 case LYD_DIFF_OP_DELETE:
1706 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1707 break;
1708 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001709 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1710 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001711 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1712 break;
1713 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001714 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001715 }
1716 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001717 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001718 return ret;
1719 }
1720
1721 if (diff_cb) {
1722 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001723 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001724 }
1725
1726 /* update diff parent */
1727 diff_parent = diff_node;
1728
Michal Vaskoe78faec2021-04-08 17:24:43 +02001729 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1730 * so there is nothing to merge for these "keys" */
1731 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1732 /* merge src_diff recursively */
1733 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1734 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1735 if (ret) {
1736 break;
1737 }
1738 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001739 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001740 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001741 }
1742 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001743add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001744 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001745 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1746 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001747
1748 /* insert node into diff if not already */
1749 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001750 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001751 }
1752
1753 /* update operation */
1754 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1755
1756 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001757 /* call callback with no source diff node since it was duplicated and just added */
1758 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001759 }
1760
1761 /* update diff parent */
1762 diff_parent = diff_node;
1763 }
1764
1765 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001766 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001767 if (diff_parent == *diff) {
1768 *diff = (*diff)->next;
1769 }
1770 lyd_free_tree(diff_parent);
1771 }
1772
1773 return LY_SUCCESS;
1774}
1775
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001776LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001777lyd_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 +01001778 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001779{
1780 const struct lyd_node *src_root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001781 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001782 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001783
1784 LY_LIST_FOR(src_diff, src_root) {
1785 if (mod && (lyd_owner_module(src_root) != mod)) {
1786 /* skip data nodes from different modules */
1787 continue;
1788 }
1789
1790 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001791 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 +02001792 }
1793
Michal Vaskoe78faec2021-04-08 17:24:43 +02001794cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001795 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001796 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001797}
1798
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001799LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001800lyd_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 +01001801 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001802{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001803 LY_ERR ret;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001804 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001805
Michal Vasko04f85912020-08-07 12:14:58 +02001806 if (!src_sibling) {
1807 return LY_SUCCESS;
1808 }
1809
Michal Vaskoe78faec2021-04-08 17:24:43 +02001810 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 +02001811 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001812 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001813}
1814
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001815LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001816lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001817{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001818 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001819}
Michal Vasko4231fb62020-07-13 13:54:47 +02001820
1821static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001822lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001823{
1824 LY_ERR ret = LY_SUCCESS;
1825 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001826 const char *val1 = NULL;
1827 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001828 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001829
Michal Vaskobaba84e2021-02-05 16:33:30 +01001830 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1831
1832 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001833 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001834
1835 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001836 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001837
1838 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001839 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001840 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001841 } else {
1842 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1843 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001844
1845 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001846 flags = node->flags;
1847 if (node->schema->nodetype == LYS_LEAF) {
1848 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1849 } else {
1850 union lyd_any_value anyval = {.str = val1};
1851 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1852 }
1853 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001854 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1855
1856cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001857 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001858 return ret;
1859}
1860
1861static LY_ERR
1862lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1863{
1864 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001865 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001866
1867 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001868 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001869
1870 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001871 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001872 flag1 = LYD_DEFAULT;
1873 } else {
1874 flag1 = 0;
1875 }
1876
1877 /* current default */
1878 flag2 = node->flags & LYD_DEFAULT;
1879
Michal Vasko610e93b2020-11-09 20:58:32 +01001880 if (flag1 == flag2) {
1881 /* no default state change so nothing to reverse */
1882 return LY_SUCCESS;
1883 }
1884
Michal Vasko4231fb62020-07-13 13:54:47 +02001885 /* switch defaults */
1886 node->flags &= ~LYD_DEFAULT;
1887 node->flags |= flag1;
1888 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1889
1890 return LY_SUCCESS;
1891}
1892
1893static LY_ERR
1894lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1895{
1896 LY_ERR ret = LY_SUCCESS;
1897 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001898 const char *val1 = NULL;
1899 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001900
1901 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001902 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001903
1904 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001905 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001906
1907 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001908 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02001909
1910 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001911 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02001912
1913 /* switch values */
1914 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1915 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1916
1917cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001918 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001919 return ret;
1920}
1921
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001922/**
1923 * @brief Remove specific operation from all the nodes in a subtree.
1924 *
1925 * @param[in] diff Diff subtree to process.
1926 * @param[in] op Only expected operation.
1927 * @return LY_ERR value.
1928 */
1929static LY_ERR
1930lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
1931{
1932 struct lyd_node *elem;
1933 struct lyd_meta *meta;
1934
1935 LYD_TREE_DFS_BEGIN(diff, elem) {
1936 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
1937 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001938 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 +01001939 lyd_free_meta_single(meta);
1940 }
1941
1942 LYD_TREE_DFS_END(diff, elem);
1943 }
1944
1945 return LY_SUCCESS;
1946}
1947
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001948LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001949lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001950{
1951 LY_ERR ret = LY_SUCCESS;
1952 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001953 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02001954 enum lyd_diff_op op;
1955
1956 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1957
1958 if (!src_diff) {
1959 *diff = NULL;
1960 return LY_SUCCESS;
1961 }
1962
1963 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001964 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001965
1966 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001967 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1968 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001969
1970 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001971 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001972 /* skip all keys */
1973 if (!lysc_is_key(elem->schema)) {
1974 /* find operation attribute, if any */
1975 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001976
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001977 switch (op) {
1978 case LYD_DIFF_OP_CREATE:
1979 /* reverse create to delete */
1980 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001981
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001982 /* check all the children for the same operation, nothing else is expected */
1983 LY_LIST_FOR(lyd_child(elem), iter) {
1984 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
1985 }
1986
Michal Vasko9e070522021-03-05 14:00:14 +01001987 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001988 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001989 case LYD_DIFF_OP_DELETE:
1990 /* reverse delete to create */
1991 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001992
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001993 /* check all the children for the same operation, nothing else is expected */
1994 LY_LIST_FOR(lyd_child(elem), iter) {
1995 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
1996 }
1997
Michal Vasko9e070522021-03-05 14:00:14 +01001998 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001999 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002000 case LYD_DIFF_OP_REPLACE:
2001 switch (elem->schema->nodetype) {
2002 case LYS_LEAF:
2003 /* leaf value change */
2004 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2005 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2006 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002007 case LYS_ANYXML:
2008 case LYS_ANYDATA:
2009 /* any value change */
2010 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2011 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002012 case LYS_LEAFLIST:
2013 /* leaf-list move */
2014 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002015 if (lysc_is_dup_inst_list(elem->schema)) {
2016 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2017 } else {
2018 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2019 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002020 break;
2021 case LYS_LIST:
2022 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002023 if (lysc_is_dup_inst_list(elem->schema)) {
2024 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2025 } else {
2026 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2027 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002028 break;
2029 default:
2030 LOGINT(LYD_CTX(src_diff));
2031 ret = LY_EINT;
2032 goto cleanup;
2033 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002034 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002035 case LYD_DIFF_OP_NONE:
2036 switch (elem->schema->nodetype) {
2037 case LYS_LEAF:
2038 case LYS_LEAFLIST:
2039 /* default flag change */
2040 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2041 break;
2042 default:
2043 /* nothing to do */
2044 break;
2045 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002046 break;
2047 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002048 }
2049
Michal Vasko56daf732020-08-10 10:57:18 +02002050 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002051 }
2052 }
2053
2054cleanup:
2055 if (ret) {
2056 lyd_free_siblings(*diff);
2057 *diff = NULL;
2058 }
2059 return ret;
2060}