blob: 34ee404fde0886a83a4abea0aa10cf70dc051de5 [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{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200300 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200301 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200302 size_t buflen, bufused;
303 uint32_t first_pos, second_pos;
Michal Vaskod59035b2020-07-08 12:00:06 +0200304
305 assert(first || second);
306
307 *orig_default = NULL;
308 *value = NULL;
309 *orig_value = NULL;
310 *key = NULL;
311 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200312 *position = NULL;
313 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200314
315 schema = first ? first->schema : second->schema;
316 assert(lysc_is_userordered(schema));
317
Michal Vaskod59035b2020-07-08 12:00:06 +0200318 /* find user-ordered first position */
319 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200320 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200321 if (userord_item->inst[first_pos] == first) {
322 break;
323 }
324 }
325 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
326 } else {
327 first_pos = 0;
328 }
329
Michal Vaskoe78faec2021-04-08 17:24:43 +0200330 /* prepare position of the next instance */
331 second_pos = userord_item->pos++;
332
Michal Vaskod59035b2020-07-08 12:00:06 +0200333 /* learn operation first */
334 if (!second) {
335 *op = LYD_DIFF_OP_DELETE;
336 } else if (!first) {
337 *op = LYD_DIFF_OP_CREATE;
338 } else {
Michal Vasko8f359bf2020-07-28 10:41:15 +0200339 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200340 /* in first, there is a different instance on the second position, we are going to move 'first' node */
341 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200342 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200343 /* default flag change */
344 *op = LYD_DIFF_OP_NONE;
345 } else {
346 /* no changes */
347 return LY_ENOT;
348 }
349 }
350
351 /*
352 * set each attribute correctly based on the operation and node type
353 */
354
355 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100356 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200357 if (first->flags & LYD_DEFAULT) {
358 *orig_default = "true";
359 } else {
360 *orig_default = "false";
361 }
362 }
363
364 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200365 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
366 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200367 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200368 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200369 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200370 } else {
371 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200372 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200373 }
374 }
375
376 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200377 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
378 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200379 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200380 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200381 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200382 } else {
383 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200384 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200385 }
386 }
387
388 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200389 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
390 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200391 if (second_pos) {
392 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200393 LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200394 } else {
395 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200396 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200397 }
398 }
399
400 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200401 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
402 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200403 if (first_pos) {
404 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200405 LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200406 } else {
407 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200408 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200409 }
410 }
411
Michal Vaskoe78faec2021-04-08 17:24:43 +0200412 /* position */
413 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
414 if (second_pos) {
415 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
416 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200417 rc = LY_EMEM;
418 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200419 }
420 } else {
421 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200422 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200423 }
424 }
425
426 /* orig-position */
427 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
428 if (first_pos) {
429 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
430 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200431 rc = LY_EMEM;
432 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200433 }
434 } else {
435 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200436 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200437 }
438 }
439
Michal Vaskod59035b2020-07-08 12:00:06 +0200440 /*
441 * update our instances - apply the change
442 */
443 if (*op == LYD_DIFF_OP_CREATE) {
444 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200445 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200446 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
447 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
448 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
449 }
450 LY_ARRAY_INCREMENT(userord_item->inst);
451 userord_item->inst[second_pos] = second;
452
453 } else if (*op == LYD_DIFF_OP_DELETE) {
454 /* remove the instance */
455 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
456 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
457 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
458 }
459 LY_ARRAY_DECREMENT(userord_item->inst);
460
461 } else if (*op == LYD_DIFF_OP_REPLACE) {
462 /* move the instances */
463 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
464 (first_pos - second_pos) * sizeof *userord_item->inst);
465 userord_item->inst[second_pos] = first;
466 }
467
Michal Vaskof9b052a2022-06-08 10:26:53 +0200468cleanup:
469 if (rc) {
470 free(*value);
471 *value = NULL;
472 free(*orig_value);
473 *orig_value = NULL;
474 free(*key);
475 *key = NULL;
476 free(*orig_key);
477 *orig_key = NULL;
478 free(*position);
479 *position = NULL;
480 free(*orig_position);
481 *orig_position = NULL;
482 }
483 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200484}
485
486/**
487 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
488 * lists/leaf-lists.
489 *
490 * @param[in] first Node from the first tree, can be NULL (on create).
491 * @param[in] second Node from the second tree, can be NULL (on delete).
492 * @param[in] options Diff options.
493 * @param[out] op Operation.
494 * @param[out] orig_default Original default metadata.
495 * @param[out] orig_value Original value metadata.
496 * @return LY_SUCCESS on success,
497 * @return LY_ENOT if there is no change to be added into diff,
498 * @return LY_ERR value on other errors.
499 */
500static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200501lyd_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 +0200502 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200503{
504 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200505 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200506
507 assert(first || second);
508
509 *orig_default = NULL;
510 *orig_value = NULL;
511
512 schema = first ? first->schema : second->schema;
513 assert(!lysc_is_userordered(schema));
514
515 /* learn operation first */
516 if (!second) {
517 *op = LYD_DIFF_OP_DELETE;
518 } else if (!first) {
519 *op = LYD_DIFF_OP_CREATE;
520 } else {
521 switch (schema->nodetype) {
522 case LYS_CONTAINER:
523 case LYS_RPC:
524 case LYS_ACTION:
525 case LYS_NOTIF:
526 /* no changes */
527 return LY_ENOT;
528 case LYS_LIST:
529 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200530 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200531 /* default flag change */
532 *op = LYD_DIFF_OP_NONE;
533 } else {
534 /* no changes */
535 return LY_ENOT;
536 }
537 break;
538 case LYS_LEAF:
539 case LYS_ANYXML:
540 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200541 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200542 /* different values */
543 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200544 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200545 /* default flag change */
546 *op = LYD_DIFF_OP_NONE;
547 } else {
548 /* no changes */
549 return LY_ENOT;
550 }
551 break;
552 default:
553 LOGINT_RET(schema->module->ctx);
554 }
555 }
556
557 /*
558 * set each attribute correctly based on the operation and node type
559 */
560
561 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100562 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200563 if (first->flags & LYD_DEFAULT) {
564 *orig_default = "true";
565 } else {
566 *orig_default = "false";
567 }
568 }
569
570 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100571 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
572 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200573 str_val = lyd_get_value(first);
574 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100575 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
576 } else {
577 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
578 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200579 }
580
581 return LY_SUCCESS;
582}
583
584/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200585 * @brief Find a matching instance of a node in a data tree.
586 *
587 * @param[in] siblings Siblings to search in.
588 * @param[in] target Target node to search for.
589 * @param[in] defaults Whether to consider (or ignore) default values.
590 * @param[in,out] dup_inst_cache Duplicate instance cache.
591 * @param[out] match Found match, NULL if no matching node found.
592 * @return LY_ERR value.
593 */
594static LY_ERR
595lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200596 struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200597{
Michal Vaskoe78faec2021-04-08 17:24:43 +0200598 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
599 /* try to find the exact instance */
600 lyd_find_sibling_first(siblings, target, match);
601 } else {
602 /* try to simply find the node, there cannot be more instances */
603 lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
604 }
605
Michal Vaskod7c048c2021-05-18 16:12:55 +0200606 /* update match as needed */
607 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200608
609 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
610 /* ignore default nodes */
611 *match = NULL;
612 }
613 return LY_SUCCESS;
614}
615
616/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200617 * @brief Perform diff for all siblings at certain depth, recursively.
618 *
619 * For user-ordered lists/leaf-lists a specific structure is used for storing
620 * the current order. The idea is to apply all the generated diff changes
621 * virtually on the first tree so that we can continue to generate correct
622 * changes after some were already generated.
623 *
624 * The algorithm then uses second tree position-based changes with a before
625 * (preceding) item anchor.
626 *
627 * Example:
628 *
629 * Virtual first tree leaf-list order:
630 * 1 2 [3] 4 5
631 *
632 * Second tree leaf-list order:
633 * 1 2 [5] 3 4
634 *
635 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
636 * match - they do not - move nodes so that the 3rd position node is final ->
637 * -> move node 5 to the 3rd position -> move node 5 after node 2.
638 *
639 * Required properties:
640 * Stored operations (move) should not be affected by later operations -
641 * - would cause a redundantly long list of operations, possibly inifinite.
642 *
643 * Implemenation justification:
644 * First, all delete operations and only then move/create operations are stored.
645 * Also, preceding anchor is used and after each iteration another node is
646 * at its final position. That results in the invariant that all preceding
647 * nodes are final and will not be changed by the later operations, meaning
648 * they can safely be used as anchors for the later operations.
649 *
650 * @param[in] first First tree first sibling.
651 * @param[in] second Second tree first sibling.
652 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200653 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200654 * @param[in,out] diff Diff to append to.
655 * @return LY_ERR value.
656 */
657static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200658lyd_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 +0200659 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200660{
661 LY_ERR ret = LY_SUCCESS;
662 const struct lyd_node *iter_first, *iter_second;
663 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100664 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200665 struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200666 LY_ARRAY_COUNT_TYPE u;
667 enum lyd_diff_op op;
668 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200669 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200670
Michal Vaskod59035b2020-07-08 12:00:06 +0200671 /* compare first tree to the second tree - delete, replace, none */
672 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200673 if (!iter_first->schema) {
674 continue;
675 }
676
Michal Vaskod59035b2020-07-08 12:00:06 +0200677 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200678 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200679 /* skip default nodes */
680 continue;
681 }
682
Michal Vaskoe78faec2021-04-08 17:24:43 +0200683 /* find a match in the second tree */
684 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
685 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200686
687 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100688 /* get (create) userord entry */
689 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
690 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
691
Michal Vaskoe78faec2021-04-08 17:24:43 +0200692 /* we are handling only user-ordered node delete now */
693 if (!match_second) {
694 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100695 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
696 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200697
698 /* there must be changes, it is deleted */
699 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100700 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
701 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200702
703 free(orig_value);
704 free(key);
705 free(value);
706 free(position);
707 free(orig_key);
708 free(orig_position);
709 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200710 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200711 } else {
712 /* get all the attributes */
713 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
714
715 /* add into diff if there are any changes */
716 if (!ret) {
717 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200718 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200719 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100720 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200721 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200722 }
723
724 free(orig_value);
725 LY_CHECK_GOTO(ret, cleanup);
726 } else if (ret == LY_ENOT) {
727 ret = LY_SUCCESS;
728 } else {
729 goto cleanup;
730 }
731 }
732
733 /* check descendants, if any, recursively */
734 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200735 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
736 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200737 }
738
739 if (nosiblings) {
740 break;
741 }
742 }
743
744 /* reset all cached positions */
745 LY_ARRAY_FOR(userord, u) {
746 userord[u].pos = 0;
747 }
748
749 /* compare second tree to the first tree - create, user-ordered move */
750 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200751 if (!iter_second->schema) {
752 continue;
753 }
754
Michal Vaskod59035b2020-07-08 12:00:06 +0200755 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200756 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200757 /* skip default nodes */
758 continue;
759 }
760
Michal Vaskoe78faec2021-04-08 17:24:43 +0200761 /* find a match in the first tree */
762 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
763 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200764
765 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100766 /* get userord entry */
767 userord_item = lyd_diff_userord_get(NULL, iter_second->schema, &userord);
768 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
769
Michal Vaskod59035b2020-07-08 12:00:06 +0200770 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100771 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200772 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200773
774 /* add into diff if there are any changes */
775 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200776 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
777 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200778
779 free(orig_value);
780 free(key);
781 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200782 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200783 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200784 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200785 LY_CHECK_GOTO(ret, cleanup);
786 } else if (ret == LY_ENOT) {
787 ret = LY_SUCCESS;
788 } else {
789 goto cleanup;
790 }
791 } else if (!match_first) {
792 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200793 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 +0200794
795 /* there must be changes, it is created */
796 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200797 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200798
799 free(orig_value);
800 LY_CHECK_GOTO(ret, cleanup);
801 } /* else was handled */
802
803 if (nosiblings) {
804 break;
805 }
806 }
807
808cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200809 lyd_dup_inst_free(dup_inst_first);
810 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200811 LY_ARRAY_FOR(userord, u) {
812 LY_ARRAY_FREE(userord[u].inst);
813 }
814 LY_ARRAY_FREE(userord);
815 return ret;
816}
817
Michal Vasko3a41dff2020-07-15 14:30:28 +0200818static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100819lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
820 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200821{
822 const struct ly_ctx *ctx;
823
824 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
825
826 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200827 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200828 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200829 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200830 } else {
831 ctx = NULL;
832 }
833
834 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
835 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
836 return LY_EINVAL;
837 }
838
839 *diff = NULL;
840
Michal Vasko3a41dff2020-07-15 14:30:28 +0200841 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
842}
843
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100844LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200845lyd_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 +0200846{
847 return lyd_diff(first, second, options, 1, diff);
848}
849
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100850LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200851lyd_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 +0200852{
853 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200854}
855
856/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200857 * @brief Learn operation of a diff node.
858 *
859 * @param[in] diff_node Diff node.
860 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200861 * @return LY_ERR value.
862 */
863static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200864lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200865{
866 struct lyd_meta *meta = NULL;
867 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200868 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100869 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +0200870
Michal Vasko9e685082021-01-29 14:49:09 +0100871 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200872 LY_LIST_FOR(diff_parent->meta, meta) {
873 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200874 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200875 if ((str[0] == 'r') && (diff_parent != diff_node)) {
876 /* we do not care about this operation if it's in our parent */
877 continue;
878 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200879 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200880 break;
881 }
882 }
883 if (meta) {
884 break;
885 }
886 }
Michal Vasko52afd7d2022-01-18 14:08:34 +0100887
888 if (!meta) {
889 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
890 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
891 free(path);
892 return LY_EINT;
893 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200894
Michal Vaskod59035b2020-07-08 12:00:06 +0200895 return LY_SUCCESS;
896}
897
898/**
899 * @brief Insert a diff node into a data tree.
900 *
901 * @param[in,out] first_node First sibling of the data tree.
902 * @param[in] parent_node Data tree sibling parent node.
903 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200904 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
905 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200906 * @return err_info, NULL on success.
907 */
908static LY_ERR
909lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200910 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200911{
912 LY_ERR ret;
913 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200914 uint32_t pos, anchor_pos;
915 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200916
917 assert(new_node);
918
919 if (!*first_node) {
920 if (!parent_node) {
921 /* no parent or siblings */
922 *first_node = new_node;
923 return LY_SUCCESS;
924 }
925
926 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200927 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200928 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200929 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200930 return LY_EINVAL;
931 }
Michal Vaskob104f112020-07-17 09:54:54 +0200932 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200933 }
934
Michal Vasko9e685082021-01-29 14:49:09 +0100935 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200936
Michal Vaskod59035b2020-07-08 12:00:06 +0200937 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200938 /* simple insert */
939 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200940 }
941
Michal Vaskoe78faec2021-04-08 17:24:43 +0200942 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200943 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200944 if (lysc_is_dup_inst_list(new_node->schema)) {
945 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +0100946 if (!anchor_pos) {
947 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
948 return LY_EINVAL;
949 }
Michal Vaskoe78faec2021-04-08 17:24:43 +0200950
951 found = 0;
952 pos = 1;
953 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
954 if (pos == anchor_pos) {
955 found = 1;
956 break;
957 }
958 ++pos;
959 }
960 if (!found) {
961 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
962 new_node->schema->name);
963 return LY_EINVAL;
964 }
965 } else {
966 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
967 if (ret == LY_ENOTFOUND) {
968 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
969 new_node->schema->name);
970 return LY_EINVAL;
971 } else if (ret) {
972 return ret;
973 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200974 }
975
976 /* insert after */
977 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
978 assert(new_node->prev == anchor);
979 if (*first_node == new_node) {
980 *first_node = anchor;
981 }
982 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +0200983 /* find the first instance */
984 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
985 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +0200986
Michal Vaskoea7d3232022-04-19 12:01:36 +0200987 if (anchor) {
988 /* insert before the first instance */
989 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
990 if ((*first_node)->prev->next) {
991 assert(!new_node->prev->next);
992 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200993 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200994 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +0200995 /* insert anywhere */
996 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200997 }
998 }
999
1000 return LY_SUCCESS;
1001}
1002
1003/**
1004 * @brief Apply diff subtree on data tree nodes, recursively.
1005 *
1006 * @param[in,out] first_node First sibling of the data tree.
1007 * @param[in] parent_node Parent of the first sibling.
1008 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001009 * @param[in] diff_cb Optional diff callback.
1010 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001011 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001012 * @return LY_ERR value.
1013 */
1014static LY_ERR
1015lyd_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 +02001016 lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001017{
1018 LY_ERR ret;
1019 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001020 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001021 enum lyd_diff_op op;
1022 struct lyd_meta *meta;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001023 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001024 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001025
1026 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001027 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001028
Michal Vaskoe6323f62020-07-09 15:49:02 +02001029 /* handle specific user-ordered (leaf-)lists operations separately */
1030 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1031 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001032 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001033 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001034 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001035 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001036 /* duplicate the node */
1037 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001038 }
1039
Michal Vaskoe78faec2021-04-08 17:24:43 +02001040 /* get "key", "value", or "position" metadata string value */
1041 if (lysc_is_dup_inst_list(diff_node->schema)) {
1042 meta_str = "yang:position";
1043 } else if (diff_node->schema->nodetype == LYS_LIST) {
1044 meta_str = "yang:key";
1045 } else {
1046 meta_str = "yang:value";
1047 }
1048 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001049 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001050 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001051
Michal Vaskod59035b2020-07-08 12:00:06 +02001052 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001053 if (str_val[0]) {
1054 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001055 } else {
1056 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1057 }
1058 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001059 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001060 lyd_free_tree(match);
1061 }
1062 return ret;
1063 }
1064
1065 goto next_iter_r;
1066 }
1067
1068 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001069 switch (op) {
1070 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001071 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001072 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001073 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001074
1075 if (match->schema->nodetype & LYD_NODE_TERM) {
1076 /* special case of only dflt flag change */
1077 if (diff_node->flags & LYD_DEFAULT) {
1078 match->flags |= LYD_DEFAULT;
1079 } else {
1080 match->flags &= ~LYD_DEFAULT;
1081 }
1082 } else {
1083 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001084 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001085 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1086 LYD_NAME(diff_node));
1087 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001088 }
1089 }
1090 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001091 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001092 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001093 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001094
1095 /* insert it at the end */
1096 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001097 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001098 if (match->flags & LYD_EXT) {
1099 ret = lyd_insert_ext(parent_node, match);
1100 } else {
1101 ret = lyd_insert_child(parent_node, match);
1102 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001103 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001104 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001105 }
1106 if (ret) {
1107 lyd_free_tree(match);
1108 return ret;
1109 }
1110
1111 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001112 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001113 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001114 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001115 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001116
1117 /* remove it */
1118 if ((match == *first_node) && !match->parent) {
1119 assert(!parent_node);
1120 /* we have removed the top-level node */
1121 *first_node = (*first_node)->next;
1122 }
1123 lyd_free_tree(match);
1124
1125 /* we are not going recursively in this case, the whole subtree was already deleted */
1126 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001127 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001128 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1129 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1130 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1131 return LY_EINVAL;
1132 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001133
1134 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001135 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001136 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001137
Michal Vaskobaba84e2021-02-05 16:33:30 +01001138 /* update the value */
1139 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001140 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001141 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001142 } else {
1143 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001144 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001145 }
1146
1147 /* with flags */
1148 match->flags = diff_node->flags;
1149 break;
1150 default:
1151 LOGINT_RET(ctx);
1152 }
1153
1154next_iter_r:
1155 if (diff_cb) {
1156 /* call callback */
1157 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1158 }
1159
1160 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001161 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001162 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001163 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1164 if (ret) {
1165 break;
1166 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001167 }
1168
Michal Vaskod7c048c2021-05-18 16:12:55 +02001169 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001170 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001171}
1172
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001173LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001174lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001175 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001176{
1177 const struct lyd_node *root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001178 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001179 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001180
1181 LY_LIST_FOR(diff, root) {
1182 if (mod && (lyd_owner_module(root) != mod)) {
1183 /* skip data nodes from different modules */
1184 continue;
1185 }
1186
1187 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001188 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1189 if (ret) {
1190 break;
1191 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001192 }
1193
Michal Vaskod7c048c2021-05-18 16:12:55 +02001194 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001195 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001196}
1197
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001198LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001199lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001200{
1201 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1202}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001203
1204/**
1205 * @brief Update operations on a diff node when the new operation is NONE.
1206 *
1207 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001208 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001209 * @param[in] src_diff Current source diff node.
1210 * @return LY_ERR value.
1211 */
1212static LY_ERR
1213lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1214{
1215 switch (cur_op) {
1216 case LYD_DIFF_OP_NONE:
1217 case LYD_DIFF_OP_CREATE:
1218 case LYD_DIFF_OP_REPLACE:
1219 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1220 /* NONE on a term means only its dflt flag was changed */
1221 diff_match->flags &= ~LYD_DEFAULT;
1222 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1223 }
1224 break;
1225 default:
1226 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001227 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1228 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001229 }
1230
1231 return LY_SUCCESS;
1232}
1233
1234/**
1235 * @brief Remove an attribute from a node.
1236 *
1237 * @param[in] node Node with the metadata.
1238 * @param[in] name Metadata name.
1239 */
1240static void
1241lyd_diff_del_meta(struct lyd_node *node, const char *name)
1242{
1243 struct lyd_meta *meta;
1244
1245 LY_LIST_FOR(node->meta, meta) {
1246 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001247 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001248 return;
1249 }
1250 }
1251
1252 assert(0);
1253}
1254
1255/**
1256 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001257 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001258 *
1259 * @param[in] node Node to change.
1260 * @param[in] op Operation to set.
1261 * @return LY_ERR value.
1262 */
1263static LY_ERR
1264lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1265{
1266 struct lyd_meta *meta;
1267
1268 LY_LIST_FOR(node->meta, meta) {
1269 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001270 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001271 break;
1272 }
1273 }
1274
Michal Vasko871a0252020-11-11 18:35:24 +01001275 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001276}
1277
1278/**
1279 * @brief Update operations on a diff node when the new operation is REPLACE.
1280 *
1281 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001282 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001283 * @param[in] src_diff Current source diff node.
1284 * @return LY_ERR value.
1285 */
1286static LY_ERR
1287lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1288{
1289 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001290 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001291 struct lyd_meta *meta;
1292 const struct lys_module *mod;
1293 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001294 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001295
1296 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001297 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001298 assert(mod);
1299
1300 switch (cur_op) {
1301 case LYD_DIFF_OP_REPLACE:
1302 case LYD_DIFF_OP_CREATE:
1303 switch (diff_match->schema->nodetype) {
1304 case LYS_LIST:
1305 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001306 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001307 * keep orig_key/orig_value (only replace oper) and replace key/value */
1308 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001309 if (lysc_is_dup_inst_list(diff_match->schema)) {
1310 meta_name = "position";
1311 } else if (diff_match->schema->nodetype == LYS_LIST) {
1312 meta_name = "key";
1313 } else {
1314 meta_name = "value";
1315 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001316
1317 lyd_diff_del_meta(diff_match, meta_name);
1318 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001319 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001320 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001321 break;
1322 case LYS_LEAF:
1323 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001324 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001325 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1326 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001327 }
1328
Michal Vaskoe6323f62020-07-09 15:49:02 +02001329 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001330 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001331 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001332 }
1333
Michal Vasko8caadab2020-11-05 17:38:15 +01001334 if (cur_op == LYD_DIFF_OP_REPLACE) {
1335 /* compare values whether there is any change at all */
1336 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001337 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001338 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001339 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1340 if (!ret) {
1341 /* values are the same, remove orig-value meta and set oper to NONE */
1342 lyd_free_meta_single(meta);
1343 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1344 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001345 }
1346
1347 /* modify the default flag */
1348 diff_match->flags &= ~LYD_DEFAULT;
1349 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1350 break;
1351 case LYS_ANYXML:
1352 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001353 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001354 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1355 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001356 }
1357
1358 /* modify the node value */
1359 any = (struct lyd_node_any *)src_diff;
1360 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1361 break;
1362 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001363 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001364 }
1365 break;
1366 case LYD_DIFF_OP_NONE:
1367 /* it is moved now */
1368 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1369
1370 /* change the operation */
1371 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1372
Michal Vaskoe78faec2021-04-08 17:24:43 +02001373 /* set orig-meta and meta */
1374 if (lysc_is_dup_inst_list(diff_match->schema)) {
1375 meta_name = "position";
1376 orig_meta_name = "orig-position";
1377 } else {
1378 meta_name = "key";
1379 orig_meta_name = "orig-key";
1380 }
1381
1382 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001383 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001384 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001385
Michal Vaskoe78faec2021-04-08 17:24:43 +02001386 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001387 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001388 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001389 break;
1390 default:
1391 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001392 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1393 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001394 }
1395
1396 return LY_SUCCESS;
1397}
1398
1399/**
1400 * @brief Update operations in a diff node when the new operation is CREATE.
1401 *
1402 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001403 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001404 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001405 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001406 * @return LY_ERR value.
1407 */
1408static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001409lyd_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 +02001410{
1411 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001412 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001413 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001414 const char *meta_name, *orig_meta_name;
1415 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001416 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001417
1418 switch (cur_op) {
1419 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001420 /* remember current flags */
1421 trg_flags = diff_match->flags;
1422
Michal Vaskoe78faec2021-04-08 17:24:43 +02001423 if (lysc_is_userordered(diff_match->schema)) {
1424 /* get anchor metadata */
1425 if (lysc_is_dup_inst_list(diff_match->schema)) {
1426 meta_name = "yang:position";
1427 orig_meta_name = "yang:orig-position";
1428 } else if (diff_match->schema->nodetype == LYS_LIST) {
1429 meta_name = "yang:key";
1430 orig_meta_name = "yang:orig-key";
1431 } else {
1432 meta_name = "yang:value";
1433 orig_meta_name = "yang:orig-value";
1434 }
1435 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001436 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001437 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001438 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001439
1440 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1441 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001442 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001443 /* deleted + created at another position -> operation REPLACE */
1444 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1445
1446 /* add anchor metadata */
1447 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1448 } else {
1449 /* deleted + created at the same position -> operation NONE */
1450 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1451
1452 /* delete anchor metadata */
1453 lyd_free_meta_single(orig_meta);
1454 }
1455 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1456 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1457 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1458 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1459 }
1460
Radek Krejci55c4bd22021-04-26 08:09:04 +02001461 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1462 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001463 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1464 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1465 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1466 /* deleted + created -> operation NONE */
1467 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1468 } else {
1469 /* we deleted it, but it was created with a different value -> operation REPLACE */
1470 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1471
1472 /* current value is the previous one (meta) */
1473 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001474 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001475
1476 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001477 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001478 }
1479 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001480 /* deleted + created -> operation NONE */
1481 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001482 }
1483
1484 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001485 /* add orig-dflt metadata */
1486 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1487 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1488
Michal Vaskoe6323f62020-07-09 15:49:02 +02001489 /* update dflt flag itself */
1490 diff_match->flags &= ~LYD_DEFAULT;
1491 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001492 }
1493
1494 /* but the operation of its children should remain DELETE */
1495 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1496 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001497 }
1498 break;
1499 default:
1500 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001501 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1502 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001503 }
1504
1505 return LY_SUCCESS;
1506}
1507
1508/**
1509 * @brief Update operations on a diff node when the new operation is DELETE.
1510 *
1511 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001512 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001513 * @param[in] src_diff Current source diff node.
1514 * @return LY_ERR value.
1515 */
1516static LY_ERR
1517lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1518{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001519 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001520 struct lyd_meta *meta;
1521 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001522 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001523
1524 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001525 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 +02001526
1527 switch (cur_op) {
1528 case LYD_DIFF_OP_CREATE:
1529 /* it was created, but then deleted -> set NONE operation */
1530 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1531
1532 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1533 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001534 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1535 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001536 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001537 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001538 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001539 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1540 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001541 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001542 break;
1543 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001544 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001545 if (lysc_is_userordered(diff_match->schema)) {
1546 if (lysc_is_dup_inst_list(diff_match->schema)) {
1547 meta_name = "position";
1548 } else if (diff_match->schema->nodetype == LYS_LIST) {
1549 meta_name = "key";
1550 } else {
1551 meta_name = "value";
1552 }
1553 } else {
1554 assert(diff_match->schema->nodetype == LYS_LEAF);
1555
1556 /* switch value for the original one */
1557 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001558 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001559 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001560 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1561 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001562 }
1563
1564 /* switch default for the original one, then remove the meta */
1565 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001566 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001567 diff_match->flags &= ~LYD_DEFAULT;
1568 if (meta->value.boolean) {
1569 diff_match->flags |= LYD_DEFAULT;
1570 }
1571 lyd_free_meta_single(meta);
1572
1573 meta_name = "orig-value";
1574 }
1575 lyd_diff_del_meta(diff_match, meta_name);
1576
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001577 /* it was being changed, but should be deleted instead -> set DELETE operation */
1578 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1579 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001580 case LYD_DIFF_OP_NONE:
1581 /* it was not modified, but should be deleted -> set DELETE operation */
1582 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001583 break;
1584 default:
1585 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001586 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1587 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001588 }
1589
1590 return LY_SUCCESS;
1591}
1592
1593/**
1594 * @brief Check whether this diff node is redundant (does not change data).
1595 *
1596 * @param[in] diff Diff node.
1597 * @return 0 if not, non-zero if it is.
1598 */
1599static int
1600lyd_diff_is_redundant(struct lyd_node *diff)
1601{
1602 enum lyd_diff_op op;
1603 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1604 struct lyd_node *child;
1605 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001606 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001607
1608 assert(diff);
1609
Michal Vaskoe78faec2021-04-08 17:24:43 +02001610 if (lysc_is_dup_inst_list(diff->schema)) {
1611 /* all descendants are keys */
1612 child = NULL;
1613 } else {
1614 child = lyd_child_no_keys(diff);
1615 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001616 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001617 assert(mod);
1618
1619 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001620 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001621
1622 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001623 /* get metadata names */
1624 if (lysc_is_dup_inst_list(diff->schema)) {
1625 meta_name = "position";
1626 orig_meta_name = "orig-position";
1627 } else if (diff->schema->nodetype == LYS_LIST) {
1628 meta_name = "key";
1629 orig_meta_name = "orig-key";
1630 } else {
1631 meta_name = "value";
1632 orig_meta_name = "orig-value";
1633 }
1634
Michal Vaskoe6323f62020-07-09 15:49:02 +02001635 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001636 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1637 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001638 assert(orig_val_meta && val_meta);
1639
1640 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1641 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001642 lyd_free_meta_single(orig_val_meta);
1643 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001644 if (child) {
1645 /* change operation to NONE, we have siblings */
1646 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1647 return 0;
1648 }
1649
1650 /* redundant node, BUT !!
1651 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1652 * because the data that this is applied on should not change for the diff lifetime.
1653 * However, when we are merging 2 diffs, this conversion is actually lossy because
1654 * if the data change, the move operation can also change its meaning. In this specific
1655 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1656 */
1657 return 1;
1658 }
1659 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1660 /* check whether at least the default flags are different */
1661 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1662 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001663 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001664
1665 /* if previous and current dflt flags are the same, this node is redundant */
1666 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1667 return 1;
1668 }
1669 return 0;
1670 }
1671
1672 if (!child && (op == LYD_DIFF_OP_NONE)) {
1673 return 1;
1674 }
1675
1676 return 0;
1677}
1678
1679/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001680 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001681 *
1682 * @param[in] src_diff Source diff node.
1683 * @param[in] diff_parent Current sysrepo diff parent.
1684 * @param[in] diff_cb Optional diff callback.
1685 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001686 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001687 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001688 * @param[in,out] diff Diff root node.
1689 * @return LY_ERR value.
1690 */
1691static LY_ERR
1692lyd_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 +02001693 struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001694{
1695 LY_ERR ret = LY_SUCCESS;
1696 struct lyd_node *child, *diff_node = NULL;
1697 enum lyd_diff_op src_op, cur_op;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001698 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001699
1700 /* get source node operation */
1701 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1702
1703 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001704 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 +02001705
1706 if (diff_node) {
1707 /* get target (current) operation */
1708 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1709
1710 /* merge operations */
1711 switch (src_op) {
1712 case LYD_DIFF_OP_REPLACE:
1713 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1714 break;
1715 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001716 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001717 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001718 goto add_diff;
1719 }
1720
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001721 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001722 break;
1723 case LYD_DIFF_OP_DELETE:
1724 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1725 break;
1726 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001727 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1728 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001729 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1730 break;
1731 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001732 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001733 }
1734 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001735 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001736 return ret;
1737 }
1738
1739 if (diff_cb) {
1740 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001741 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001742 }
1743
1744 /* update diff parent */
1745 diff_parent = diff_node;
1746
Michal Vaskoe78faec2021-04-08 17:24:43 +02001747 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1748 * so there is nothing to merge for these "keys" */
1749 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1750 /* merge src_diff recursively */
1751 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1752 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1753 if (ret) {
1754 break;
1755 }
1756 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001757 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001758 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001759 }
1760 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001761add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001762 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001763 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1764 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001765
1766 /* insert node into diff if not already */
1767 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001768 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001769 }
1770
1771 /* update operation */
1772 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1773
1774 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001775 /* call callback with no source diff node since it was duplicated and just added */
1776 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001777 }
1778
1779 /* update diff parent */
1780 diff_parent = diff_node;
1781 }
1782
1783 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001784 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001785 if (diff_parent == *diff) {
1786 *diff = (*diff)->next;
1787 }
1788 lyd_free_tree(diff_parent);
1789 }
1790
1791 return LY_SUCCESS;
1792}
1793
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001794LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001795lyd_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 +01001796 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001797{
1798 const struct lyd_node *src_root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001799 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001800 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001801
1802 LY_LIST_FOR(src_diff, src_root) {
1803 if (mod && (lyd_owner_module(src_root) != mod)) {
1804 /* skip data nodes from different modules */
1805 continue;
1806 }
1807
1808 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001809 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 +02001810 }
1811
Michal Vaskoe78faec2021-04-08 17:24:43 +02001812cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001813 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001814 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001815}
1816
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001817LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001818lyd_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 +01001819 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001820{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001821 LY_ERR ret;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001822 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001823
Michal Vasko04f85912020-08-07 12:14:58 +02001824 if (!src_sibling) {
1825 return LY_SUCCESS;
1826 }
1827
Michal Vaskoe78faec2021-04-08 17:24:43 +02001828 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 +02001829 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001830 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001831}
1832
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001833LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001834lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001835{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001836 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001837}
Michal Vasko4231fb62020-07-13 13:54:47 +02001838
1839static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001840lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001841{
1842 LY_ERR ret = LY_SUCCESS;
1843 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001844 const char *val1 = NULL;
1845 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001846 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001847
Michal Vaskobaba84e2021-02-05 16:33:30 +01001848 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1849
1850 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001851 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001852
1853 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001854 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001855
1856 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001857 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001858 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001859 } else {
1860 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1861 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001862
1863 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001864 flags = node->flags;
1865 if (node->schema->nodetype == LYS_LEAF) {
1866 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1867 } else {
1868 union lyd_any_value anyval = {.str = val1};
1869 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1870 }
1871 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001872 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1873
1874cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001875 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001876 return ret;
1877}
1878
1879static LY_ERR
1880lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1881{
1882 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001883 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001884
1885 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001886 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001887
1888 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001889 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001890 flag1 = LYD_DEFAULT;
1891 } else {
1892 flag1 = 0;
1893 }
1894
1895 /* current default */
1896 flag2 = node->flags & LYD_DEFAULT;
1897
Michal Vasko610e93b2020-11-09 20:58:32 +01001898 if (flag1 == flag2) {
1899 /* no default state change so nothing to reverse */
1900 return LY_SUCCESS;
1901 }
1902
Michal Vasko4231fb62020-07-13 13:54:47 +02001903 /* switch defaults */
1904 node->flags &= ~LYD_DEFAULT;
1905 node->flags |= flag1;
1906 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1907
1908 return LY_SUCCESS;
1909}
1910
1911static LY_ERR
1912lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1913{
1914 LY_ERR ret = LY_SUCCESS;
1915 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001916 const char *val1 = NULL;
1917 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001918
1919 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001920 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001921
1922 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001923 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001924
1925 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001926 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02001927
1928 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001929 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02001930
1931 /* switch values */
1932 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1933 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1934
1935cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001936 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001937 return ret;
1938}
1939
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001940/**
1941 * @brief Remove specific operation from all the nodes in a subtree.
1942 *
1943 * @param[in] diff Diff subtree to process.
1944 * @param[in] op Only expected operation.
1945 * @return LY_ERR value.
1946 */
1947static LY_ERR
1948lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
1949{
1950 struct lyd_node *elem;
1951 struct lyd_meta *meta;
1952
1953 LYD_TREE_DFS_BEGIN(diff, elem) {
1954 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
1955 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001956 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 +01001957 lyd_free_meta_single(meta);
1958 }
1959
1960 LYD_TREE_DFS_END(diff, elem);
1961 }
1962
1963 return LY_SUCCESS;
1964}
1965
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001966LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001967lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001968{
1969 LY_ERR ret = LY_SUCCESS;
1970 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001971 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02001972 enum lyd_diff_op op;
1973
1974 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1975
1976 if (!src_diff) {
1977 *diff = NULL;
1978 return LY_SUCCESS;
1979 }
1980
1981 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001982 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001983
1984 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001985 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1986 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001987
1988 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001989 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001990 /* skip all keys */
1991 if (!lysc_is_key(elem->schema)) {
1992 /* find operation attribute, if any */
1993 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001994
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001995 switch (op) {
1996 case LYD_DIFF_OP_CREATE:
1997 /* reverse create to delete */
1998 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001999
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002000 /* check all the children for the same operation, nothing else is expected */
2001 LY_LIST_FOR(lyd_child(elem), iter) {
2002 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2003 }
2004
Michal Vasko9e070522021-03-05 14:00:14 +01002005 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002006 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002007 case LYD_DIFF_OP_DELETE:
2008 /* reverse delete to create */
2009 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002010
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002011 /* check all the children for the same operation, nothing else is expected */
2012 LY_LIST_FOR(lyd_child(elem), iter) {
2013 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2014 }
2015
Michal Vasko9e070522021-03-05 14:00:14 +01002016 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002017 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002018 case LYD_DIFF_OP_REPLACE:
2019 switch (elem->schema->nodetype) {
2020 case LYS_LEAF:
2021 /* leaf value change */
2022 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2023 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2024 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002025 case LYS_ANYXML:
2026 case LYS_ANYDATA:
2027 /* any value change */
2028 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2029 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002030 case LYS_LEAFLIST:
2031 /* leaf-list move */
2032 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002033 if (lysc_is_dup_inst_list(elem->schema)) {
2034 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2035 } else {
2036 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2037 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002038 break;
2039 case LYS_LIST:
2040 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002041 if (lysc_is_dup_inst_list(elem->schema)) {
2042 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2043 } else {
2044 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2045 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002046 break;
2047 default:
2048 LOGINT(LYD_CTX(src_diff));
2049 ret = LY_EINT;
2050 goto cleanup;
2051 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002052 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002053 case LYD_DIFF_OP_NONE:
2054 switch (elem->schema->nodetype) {
2055 case LYS_LEAF:
2056 case LYS_LEAFLIST:
2057 /* default flag change */
2058 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2059 break;
2060 default:
2061 /* nothing to do */
2062 break;
2063 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002064 break;
2065 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002066 }
2067
Michal Vasko56daf732020-08-10 10:57:18 +02002068 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002069 }
2070 }
2071
2072cleanup:
2073 if (ret) {
2074 lyd_free_siblings(*diff);
2075 *diff = NULL;
2076 }
2077 return ret;
2078}