blob: fd5a1762d2fb9920fa46edb205b7f69b1ec48f69 [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"
Michal Vaskob4750962022-10-06 15:33:35 +020030#include "plugins_exts/metadata.h"
Radek Krejci47fab892020-11-05 17:02:41 +010031#include "plugins_types.h"
32#include "set.h"
33#include "tree.h"
34#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020035#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010036#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020037#include "tree_schema.h"
38#include "tree_schema_internal.h"
39
Michal Vasko52afd7d2022-01-18 14:08:34 +010040#define LOGERR_META(ctx, meta_name, node) \
41 { \
42 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
43 LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \
44 free(__path); \
45 }
46
47#define LOGERR_NOINST(ctx, node) \
48 { \
49 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
50 LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \
51 free(__path); \
52 }
53
54#define LOGERR_UNEXPVAL(ctx, node, data_source) \
55 { \
56 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
57 LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \
58 free(__path); \
59 }
60
61#define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \
62 { \
63 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
64 LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \
65 lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \
66 free(__path); \
67 }
68
Michal Vaskod59035b2020-07-08 12:00:06 +020069static const char *
70lyd_diff_op2str(enum lyd_diff_op op)
71{
72 switch (op) {
73 case LYD_DIFF_OP_CREATE:
74 return "create";
75 case LYD_DIFF_OP_DELETE:
76 return "delete";
77 case LYD_DIFF_OP_REPLACE:
78 return "replace";
79 case LYD_DIFF_OP_NONE:
80 return "none";
81 }
82
83 LOGINT(NULL);
84 return NULL;
85}
86
Michal Vaskoe6323f62020-07-09 15:49:02 +020087static enum lyd_diff_op
88lyd_diff_str2op(const char *str)
89{
90 switch (str[0]) {
91 case 'c':
92 assert(!strcmp(str, "create"));
93 return LYD_DIFF_OP_CREATE;
94 case 'd':
95 assert(!strcmp(str, "delete"));
96 return LYD_DIFF_OP_DELETE;
97 case 'r':
98 assert(!strcmp(str, "replace"));
99 return LYD_DIFF_OP_REPLACE;
100 case 'n':
101 assert(!strcmp(str, "none"));
102 return LYD_DIFF_OP_NONE;
103 }
104
105 LOGINT(NULL);
106 return 0;
107}
108
Michal Vaskocffc3f92022-06-15 07:57:24 +0200109/**
110 * @brief Create diff metadata for a nested user-ordered node with the effective operation "create".
111 *
112 * @param[in] node User-rodered node to update.
113 * @return LY_ERR value.
114 */
115static LY_ERR
116lyd_diff_add_create_nested_userord(struct lyd_node *node)
117{
118 LY_ERR rc = LY_SUCCESS;
119 const char *meta_name, *meta_val;
120 size_t buflen = 0, bufused = 0;
121 uint32_t pos;
122 char *dyn = NULL;
123
124 assert(lysc_is_userordered(node->schema));
125
126 /* get correct metadata name and value */
127 if (lysc_is_dup_inst_list(node->schema)) {
128 meta_name = "yang:position";
129
130 pos = lyd_list_pos(node);
131 if (asprintf(&dyn, "%" PRIu32, pos) == -1) {
132 LOGMEM(LYD_CTX(node));
133 rc = LY_EMEM;
134 goto cleanup;
135 }
136 meta_val = dyn;
137 } else if (node->schema->nodetype == LYS_LIST) {
138 meta_name = "yang:key";
139
140 if (node->prev->next && (node->prev->schema == node->schema)) {
141 LY_CHECK_GOTO(rc = lyd_path_list_predicate(node->prev, &dyn, &buflen, &bufused, 0), cleanup);
142 meta_val = dyn;
143 } else {
144 meta_val = "";
145 }
146 } else {
147 meta_name = "yang:value";
148
149 if (node->prev->next && (node->prev->schema == node->schema)) {
150 meta_val = lyd_get_value(node->prev);
151 } else {
152 meta_val = "";
153 }
154 }
155
156 /* create the metadata */
157 LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_val, 0, NULL), cleanup);
158
159cleanup:
160 free(dyn);
161 return rc;
162}
163
Michal Vaskod59035b2020-07-08 12:00:06 +0200164LY_ERR
165lyd_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 +0200166 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
167 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200168{
Michal Vaskocffc3f92022-06-15 07:57:24 +0200169 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
Michal Vaskod59035b2020-07-08 12:00:06 +0200170 const struct lyd_node *parent = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200171
172 assert(diff);
173
Michal Vasko53d48422020-11-13 18:02:29 +0100174 /* replace leaf always needs orig-default and orig-value */
175 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
176
177 /* create on userord needs key/value */
178 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200179 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100180 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200181 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100182
183 /* move on userord needs both key and orig-key/value and orig-value */
184 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200185 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100186 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200187 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
188 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100189
Michal Vaskod59035b2020-07-08 12:00:06 +0200190 /* find the first existing parent */
191 siblings = *diff;
192 while (1) {
193 /* find next node parent */
194 parent = node;
195 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100196 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200197 }
198 if (parent == node) {
199 /* no more parents to find */
200 break;
201 }
202
203 /* check whether it exists in the diff */
204 if (lyd_find_sibling_first(siblings, parent, &match)) {
205 break;
206 }
207
208 /* another parent found */
209 diff_parent = match;
210
211 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200212 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200213 }
214
215 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko695a7f22023-02-14 10:02:10 +0100216 if (diff_parent) {
217 LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
218 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
219 } else {
220 LY_CHECK_RET(lyd_dup_single(node, NULL,
221 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
222 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200223
224 /* find the first duplicated parent */
225 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100226 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200227 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100228 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200229 }
230 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100231 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200232 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100233 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200234 }
235 }
236
237 /* no parent existed, must be manually connected */
238 if (!diff_parent) {
239 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200240 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200241 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200242 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200243 }
244
Michal Vaskod59035b2020-07-08 12:00:06 +0200245 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200246 if (diff_parent && (diff_parent != dup)) {
Michal Vasko2e552792022-11-02 12:15:31 +0100247 LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200248 }
249
250 /* add subtree operation */
Michal Vasko2e552792022-11-02 12:15:31 +0100251 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200252
Michal Vaskocffc3f92022-06-15 07:57:24 +0200253 if (op == LYD_DIFF_OP_CREATE) {
254 /* all nested user-ordered (leaf-)lists need special metadata for create op */
255 LYD_TREE_DFS_BEGIN(dup, elem) {
256 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
257 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
258 }
259 LYD_TREE_DFS_END(dup, elem);
260 }
261 }
262
Michal Vaskod59035b2020-07-08 12:00:06 +0200263 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200264 if (orig_default) {
Michal Vasko2e552792022-11-02 12:15:31 +0100265 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200266 }
267
268 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200269 if (orig_value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100270 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200271 }
272
273 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200274 if (key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100275 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200276 }
277
278 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200279 if (value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100280 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200281 }
282
Michal Vaskoe78faec2021-04-08 17:24:43 +0200283 /* position */
284 if (position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100285 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200286 }
287
Michal Vaskod59035b2020-07-08 12:00:06 +0200288 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200289 if (orig_key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100290 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200291 }
292
Michal Vaskoe78faec2021-04-08 17:24:43 +0200293 /* orig-position */
294 if (orig_position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100295 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200296 }
297
Michal Vaskod59035b2020-07-08 12:00:06 +0200298 return LY_SUCCESS;
299}
300
301/**
302 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
303 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100304 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200305 * @param[in] schema Schema node of the list/leaf-list.
306 * @param[in,out] userord Sized array of userord items.
307 * @return Userord item for all the user-ordered list/leaf-list instances.
308 */
309static struct lyd_diff_userord *
310lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
311{
312 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200313 struct lyd_node *iter;
314 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200315 LY_ARRAY_COUNT_TYPE u;
316
317 LY_ARRAY_FOR(*userord, u) {
318 if ((*userord)[u].schema == schema) {
319 return &(*userord)[u];
320 }
321 }
322
323 /* it was not added yet, add it now */
324 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
325
326 item->schema = schema;
327 item->pos = 0;
328 item->inst = NULL;
329
330 /* store all the instance pointers in the current order */
331 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200332 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
333 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
334 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200335 }
336 }
337
338 return item;
339}
340
341/**
342 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
343 * lists/leaf-lists.
344 *
345 * @param[in] first Node from the first tree, can be NULL (on create).
346 * @param[in] second Node from the second tree, can be NULL (on delete).
347 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100348 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200349 * @param[out] op Operation.
350 * @param[out] orig_default Original default metadata.
351 * @param[out] value Value metadata.
352 * @param[out] orig_value Original value metadata
353 * @param[out] key Key metadata.
354 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200355 * @param[out] position Position metadata.
356 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200357 * @return LY_SUCCESS on success,
358 * @return LY_ENOT if there is no change to be added into diff,
359 * @return LY_ERR value on other errors.
360 */
361static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200362lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100363 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200364 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200365{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200366 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200367 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200368 size_t buflen, bufused;
Michal Vaskoee9b9482023-06-19 13:17:48 +0200369 uint32_t first_pos, second_pos, comp_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200370
371 assert(first || second);
372
373 *orig_default = NULL;
374 *value = NULL;
375 *orig_value = NULL;
376 *key = NULL;
377 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200378 *position = NULL;
379 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200380
381 schema = first ? first->schema : second->schema;
382 assert(lysc_is_userordered(schema));
383
Michal Vaskod59035b2020-07-08 12:00:06 +0200384 /* find user-ordered first position */
385 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200386 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200387 if (userord_item->inst[first_pos] == first) {
388 break;
389 }
390 }
391 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
392 } else {
393 first_pos = 0;
394 }
395
Michal Vaskoe78faec2021-04-08 17:24:43 +0200396 /* prepare position of the next instance */
397 second_pos = userord_item->pos++;
398
Michal Vaskod59035b2020-07-08 12:00:06 +0200399 /* learn operation first */
400 if (!second) {
401 *op = LYD_DIFF_OP_DELETE;
402 } else if (!first) {
403 *op = LYD_DIFF_OP_CREATE;
404 } else {
Michal Vaskoee9b9482023-06-19 13:17:48 +0200405 comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
406 if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200407 /* in first, there is a different instance on the second position, we are going to move 'first' node */
408 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200409 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200410 /* default flag change */
411 *op = LYD_DIFF_OP_NONE;
412 } else {
413 /* no changes */
414 return LY_ENOT;
415 }
416 }
417
418 /*
419 * set each attribute correctly based on the operation and node type
420 */
421
422 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100423 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200424 if (first->flags & LYD_DEFAULT) {
425 *orig_default = "true";
426 } else {
427 *orig_default = "false";
428 }
429 }
430
431 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200432 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
433 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200434 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200435 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200436 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200437 } else {
438 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200439 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200440 }
441 }
442
443 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200444 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
445 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200446 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200447 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200448 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200449 } else {
450 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200451 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200452 }
453 }
454
455 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200456 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
457 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200458 if (second_pos) {
459 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200460 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 +0200461 } else {
462 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200463 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200464 }
465 }
466
467 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200468 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
469 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200470 if (first_pos) {
471 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200472 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 +0200473 } else {
474 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200475 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200476 }
477 }
478
Michal Vaskoe78faec2021-04-08 17:24:43 +0200479 /* position */
480 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
481 if (second_pos) {
482 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
483 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200484 rc = LY_EMEM;
485 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200486 }
487 } else {
488 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200489 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200490 }
491 }
492
493 /* orig-position */
494 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
495 if (first_pos) {
496 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
497 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200498 rc = LY_EMEM;
499 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200500 }
501 } else {
502 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200503 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200504 }
505 }
506
Michal Vaskod59035b2020-07-08 12:00:06 +0200507 /*
508 * update our instances - apply the change
509 */
510 if (*op == LYD_DIFF_OP_CREATE) {
511 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200512 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200513 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
514 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
515 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
516 }
517 LY_ARRAY_INCREMENT(userord_item->inst);
518 userord_item->inst[second_pos] = second;
519
520 } else if (*op == LYD_DIFF_OP_DELETE) {
521 /* remove the instance */
522 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
523 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
524 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
525 }
526 LY_ARRAY_DECREMENT(userord_item->inst);
527
528 } else if (*op == LYD_DIFF_OP_REPLACE) {
529 /* move the instances */
530 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
531 (first_pos - second_pos) * sizeof *userord_item->inst);
532 userord_item->inst[second_pos] = first;
533 }
534
Michal Vaskof9b052a2022-06-08 10:26:53 +0200535cleanup:
536 if (rc) {
537 free(*value);
538 *value = NULL;
539 free(*orig_value);
540 *orig_value = NULL;
541 free(*key);
542 *key = NULL;
543 free(*orig_key);
544 *orig_key = NULL;
545 free(*position);
546 *position = NULL;
547 free(*orig_position);
548 *orig_position = NULL;
549 }
550 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200551}
552
553/**
554 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
555 * lists/leaf-lists.
556 *
557 * @param[in] first Node from the first tree, can be NULL (on create).
558 * @param[in] second Node from the second tree, can be NULL (on delete).
559 * @param[in] options Diff options.
560 * @param[out] op Operation.
561 * @param[out] orig_default Original default metadata.
562 * @param[out] orig_value Original value metadata.
563 * @return LY_SUCCESS on success,
564 * @return LY_ENOT if there is no change to be added into diff,
565 * @return LY_ERR value on other errors.
566 */
567static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200568lyd_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 +0200569 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200570{
571 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200572 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200573
574 assert(first || second);
575
576 *orig_default = NULL;
577 *orig_value = NULL;
578
579 schema = first ? first->schema : second->schema;
580 assert(!lysc_is_userordered(schema));
581
582 /* learn operation first */
583 if (!second) {
584 *op = LYD_DIFF_OP_DELETE;
585 } else if (!first) {
586 *op = LYD_DIFF_OP_CREATE;
587 } else {
588 switch (schema->nodetype) {
589 case LYS_CONTAINER:
590 case LYS_RPC:
591 case LYS_ACTION:
592 case LYS_NOTIF:
593 /* no changes */
594 return LY_ENOT;
595 case LYS_LIST:
596 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200597 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200598 /* default flag change */
599 *op = LYD_DIFF_OP_NONE;
600 } else {
601 /* no changes */
602 return LY_ENOT;
603 }
604 break;
605 case LYS_LEAF:
606 case LYS_ANYXML:
607 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200608 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200609 /* different values */
610 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200611 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200612 /* default flag change */
613 *op = LYD_DIFF_OP_NONE;
614 } else {
615 /* no changes */
616 return LY_ENOT;
617 }
618 break;
619 default:
620 LOGINT_RET(schema->module->ctx);
621 }
622 }
623
624 /*
625 * set each attribute correctly based on the operation and node type
626 */
627
628 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100629 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200630 if (first->flags & LYD_DEFAULT) {
631 *orig_default = "true";
632 } else {
633 *orig_default = "false";
634 }
635 }
636
637 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100638 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
639 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200640 str_val = lyd_get_value(first);
641 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100642 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
643 } else {
644 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
645 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200646 }
647
648 return LY_SUCCESS;
649}
650
651/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200652 * @brief Find a matching instance of a node in a data tree.
653 *
654 * @param[in] siblings Siblings to search in.
655 * @param[in] target Target node to search for.
656 * @param[in] defaults Whether to consider (or ignore) default values.
Michal Vasko271d2e32023-01-31 15:43:19 +0100657 * @param[in,out] dup_inst_ht Duplicate instance cache.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200658 * @param[out] match Found match, NULL if no matching node found.
659 * @return LY_ERR value.
660 */
661static LY_ERR
662lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vasko8efac242023-03-30 08:24:56 +0200663 struct ly_ht **dup_inst_ht, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200664{
Michal Vasko2bd856f2022-12-02 14:03:29 +0100665 LY_ERR r;
666
Michal Vaskodb91fc32023-05-02 14:39:40 +0200667 if (!target->schema) {
668 /* try to find the same opaque node */
669 r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
670 } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200671 /* try to find the exact instance */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100672 r = lyd_find_sibling_first(siblings, target, match);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200673 } else {
674 /* try to simply find the node, there cannot be more instances */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100675 r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
676 }
677 if (r && (r != LY_ENOTFOUND)) {
678 return r;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200679 }
680
Michal Vaskod7c048c2021-05-18 16:12:55 +0200681 /* update match as needed */
Michal Vasko271d2e32023-01-31 15:43:19 +0100682 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200683
684 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
685 /* ignore default nodes */
686 *match = NULL;
687 }
688 return LY_SUCCESS;
689}
690
691/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200692 * @brief Perform diff for all siblings at certain depth, recursively.
693 *
694 * For user-ordered lists/leaf-lists a specific structure is used for storing
695 * the current order. The idea is to apply all the generated diff changes
696 * virtually on the first tree so that we can continue to generate correct
697 * changes after some were already generated.
698 *
699 * The algorithm then uses second tree position-based changes with a before
700 * (preceding) item anchor.
701 *
702 * Example:
703 *
704 * Virtual first tree leaf-list order:
705 * 1 2 [3] 4 5
706 *
707 * Second tree leaf-list order:
708 * 1 2 [5] 3 4
709 *
710 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
711 * match - they do not - move nodes so that the 3rd position node is final ->
712 * -> move node 5 to the 3rd position -> move node 5 after node 2.
713 *
714 * Required properties:
715 * Stored operations (move) should not be affected by later operations -
716 * - would cause a redundantly long list of operations, possibly inifinite.
717 *
718 * Implemenation justification:
719 * First, all delete operations and only then move/create operations are stored.
720 * Also, preceding anchor is used and after each iteration another node is
721 * at its final position. That results in the invariant that all preceding
722 * nodes are final and will not be changed by the later operations, meaning
723 * they can safely be used as anchors for the later operations.
724 *
725 * @param[in] first First tree first sibling.
726 * @param[in] second Second tree first sibling.
727 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200728 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200729 * @param[in,out] diff Diff to append to.
730 * @return LY_ERR value.
731 */
732static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200733lyd_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 +0200734 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200735{
736 LY_ERR ret = LY_SUCCESS;
737 const struct lyd_node *iter_first, *iter_second;
738 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100739 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vasko8efac242023-03-30 08:24:56 +0200740 struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200741 LY_ARRAY_COUNT_TYPE u;
742 enum lyd_diff_op op;
743 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200744 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200745
Michal Vaskod59035b2020-07-08 12:00:06 +0200746 /* compare first tree to the second tree - delete, replace, none */
747 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200748 if (!iter_first->schema) {
749 continue;
750 }
751
Michal Vaskod59035b2020-07-08 12:00:06 +0200752 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200753 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200754 /* skip default nodes */
755 continue;
756 }
757
Michal Vaskoe78faec2021-04-08 17:24:43 +0200758 /* find a match in the second tree */
759 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
760 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200761
762 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100763 /* get (create) userord entry */
764 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
765 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
766
Michal Vaskoe78faec2021-04-08 17:24:43 +0200767 /* we are handling only user-ordered node delete now */
768 if (!match_second) {
769 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100770 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
771 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200772
773 /* there must be changes, it is deleted */
774 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100775 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
776 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200777
778 free(orig_value);
779 free(key);
780 free(value);
781 free(position);
782 free(orig_key);
783 free(orig_position);
784 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200785 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200786 } else {
787 /* get all the attributes */
788 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
789
790 /* add into diff if there are any changes */
791 if (!ret) {
792 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200793 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200794 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100795 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200796 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200797 }
798
799 free(orig_value);
800 LY_CHECK_GOTO(ret, cleanup);
801 } else if (ret == LY_ENOT) {
802 ret = LY_SUCCESS;
803 } else {
804 goto cleanup;
805 }
806 }
807
808 /* check descendants, if any, recursively */
809 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200810 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
811 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200812 }
813
814 if (nosiblings) {
815 break;
816 }
817 }
818
819 /* reset all cached positions */
820 LY_ARRAY_FOR(userord, u) {
821 userord[u].pos = 0;
822 }
823
824 /* compare second tree to the first tree - create, user-ordered move */
825 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200826 if (!iter_second->schema) {
827 continue;
828 }
829
Michal Vaskod59035b2020-07-08 12:00:06 +0200830 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200831 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200832 /* skip default nodes */
833 continue;
834 }
835
Michal Vaskoe78faec2021-04-08 17:24:43 +0200836 /* find a match in the first tree */
837 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
838 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200839
840 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100841 /* get userord entry */
Michal Vaskoaa51cc52022-12-06 09:57:11 +0100842 userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord);
Michal Vasko5da938a2022-03-01 09:19:02 +0100843 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
844
Michal Vaskod59035b2020-07-08 12:00:06 +0200845 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100846 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200847 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200848
849 /* add into diff if there are any changes */
850 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200851 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
852 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200853
854 free(orig_value);
855 free(key);
856 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200857 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200858 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200859 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200860 LY_CHECK_GOTO(ret, cleanup);
861 } else if (ret == LY_ENOT) {
862 ret = LY_SUCCESS;
863 } else {
864 goto cleanup;
865 }
866 } else if (!match_first) {
867 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200868 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 +0200869
870 /* there must be changes, it is created */
871 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200872 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200873
874 free(orig_value);
875 LY_CHECK_GOTO(ret, cleanup);
876 } /* else was handled */
877
878 if (nosiblings) {
879 break;
880 }
881 }
882
883cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200884 lyd_dup_inst_free(dup_inst_first);
885 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200886 LY_ARRAY_FOR(userord, u) {
887 LY_ARRAY_FREE(userord[u].inst);
888 }
889 LY_ARRAY_FREE(userord);
890 return ret;
891}
892
Michal Vasko3a41dff2020-07-15 14:30:28 +0200893static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100894lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
895 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200896{
897 const struct ly_ctx *ctx;
898
899 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
900
901 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200902 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200903 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200904 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200905 } else {
906 ctx = NULL;
907 }
908
909 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
910 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
911 return LY_EINVAL;
912 }
913
914 *diff = NULL;
915
Michal Vasko3a41dff2020-07-15 14:30:28 +0200916 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
917}
918
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100919LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200920lyd_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 +0200921{
922 return lyd_diff(first, second, options, 1, diff);
923}
924
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100925LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200926lyd_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 +0200927{
928 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200929}
930
931/**
Michal Vaskodb91fc32023-05-02 14:39:40 +0200932 * @brief Find metadata/an attribute of a node.
933 *
934 * @param[in] node Node to search.
935 * @param[in] name Metadata/attribute name.
936 * @param[out] meta Metadata found, NULL if not found.
937 * @param[out] attr Attribute found, NULL if not found.
938 */
939static void
940lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
941{
942 struct lyd_meta *m;
943 struct lyd_attr *a;
944
945 *meta = NULL;
946 *attr = NULL;
947
948 if (node->schema) {
949 LY_LIST_FOR(node->meta, m) {
950 if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
951 *meta = m;
952 break;
953 }
954 }
955 } else {
956 LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
957 /* name */
958 if (strcmp(a->name.name, name)) {
959 continue;
960 }
961
962 /* module */
963 switch (a->format) {
964 case LY_VALUE_JSON:
965 if (strcmp(a->name.module_name, "yang")) {
966 continue;
967 }
968 break;
969 case LY_VALUE_XML:
970 if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
971 continue;
972 }
973 break;
974 default:
975 LOGINT(LYD_CTX(node));
976 return;
977 }
978
979 *attr = a;
980 break;
981 }
982 }
983}
984
985/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200986 * @brief Learn operation of a diff node.
987 *
988 * @param[in] diff_node Diff node.
989 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200990 * @return LY_ERR value.
991 */
992static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200993lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200994{
995 struct lyd_meta *meta = NULL;
Michal Vaskodb91fc32023-05-02 14:39:40 +0200996 struct lyd_attr *attr = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200997 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200998 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100999 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +02001000
Michal Vasko9e685082021-01-29 14:49:09 +01001001 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskodb91fc32023-05-02 14:39:40 +02001002 lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
1003 if (!meta && !attr) {
1004 continue;
Michal Vaskod59035b2020-07-08 12:00:06 +02001005 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001006
1007 str = meta ? lyd_get_meta_value(meta) : attr->value;
1008 if ((str[0] == 'r') && (diff_parent != diff_node)) {
1009 /* we do not care about this operation if it's in our parent */
1010 continue;
Michal Vaskod59035b2020-07-08 12:00:06 +02001011 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001012 *op = lyd_diff_str2op(str);
1013 return LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001014 }
Michal Vasko52afd7d2022-01-18 14:08:34 +01001015
Michal Vaskodb91fc32023-05-02 14:39:40 +02001016 /* operation not found */
1017 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
1018 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
1019 free(path);
1020 return LY_EINT;
Michal Vaskod59035b2020-07-08 12:00:06 +02001021}
1022
1023/**
1024 * @brief Insert a diff node into a data tree.
1025 *
1026 * @param[in,out] first_node First sibling of the data tree.
1027 * @param[in] parent_node Data tree sibling parent node.
1028 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001029 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
1030 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +02001031 * @return err_info, NULL on success.
1032 */
1033static LY_ERR
1034lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001035 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +02001036{
1037 LY_ERR ret;
1038 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001039 uint32_t pos, anchor_pos;
1040 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +02001041
1042 assert(new_node);
1043
1044 if (!*first_node) {
1045 if (!parent_node) {
1046 /* no parent or siblings */
1047 *first_node = new_node;
1048 return LY_SUCCESS;
1049 }
1050
1051 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001052 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001053 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +02001054 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +02001055 return LY_EINVAL;
1056 }
Michal Vaskob104f112020-07-17 09:54:54 +02001057 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001058 }
1059
Michal Vasko9e685082021-01-29 14:49:09 +01001060 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001061
Michal Vaskod59035b2020-07-08 12:00:06 +02001062 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +02001063 /* simple insert */
1064 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001065 }
1066
Michal Vaskoe78faec2021-04-08 17:24:43 +02001067 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001068 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001069 if (lysc_is_dup_inst_list(new_node->schema)) {
1070 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001071 if (!anchor_pos) {
1072 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1073 return LY_EINVAL;
1074 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001075
1076 found = 0;
1077 pos = 1;
1078 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1079 if (pos == anchor_pos) {
1080 found = 1;
1081 break;
1082 }
1083 ++pos;
1084 }
1085 if (!found) {
1086 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1087 new_node->schema->name);
1088 return LY_EINVAL;
1089 }
1090 } else {
1091 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1092 if (ret == LY_ENOTFOUND) {
1093 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1094 new_node->schema->name);
1095 return LY_EINVAL;
1096 } else if (ret) {
1097 return ret;
1098 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001099 }
1100
1101 /* insert after */
1102 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1103 assert(new_node->prev == anchor);
1104 if (*first_node == new_node) {
1105 *first_node = anchor;
1106 }
1107 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001108 /* find the first instance */
1109 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1110 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001111
Michal Vaskoea7d3232022-04-19 12:01:36 +02001112 if (anchor) {
1113 /* insert before the first instance */
1114 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1115 if ((*first_node)->prev->next) {
1116 assert(!new_node->prev->next);
1117 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001118 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001119 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001120 /* insert anywhere */
1121 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001122 }
1123 }
1124
1125 return LY_SUCCESS;
1126}
1127
1128/**
1129 * @brief Apply diff subtree on data tree nodes, recursively.
1130 *
1131 * @param[in,out] first_node First sibling of the data tree.
1132 * @param[in] parent_node Parent of the first sibling.
1133 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001134 * @param[in] diff_cb Optional diff callback.
1135 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001136 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001137 * @return LY_ERR value.
1138 */
1139static LY_ERR
1140lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Michal Vasko8efac242023-03-30 08:24:56 +02001141 lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001142{
1143 LY_ERR ret;
1144 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001145 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001146 enum lyd_diff_op op;
1147 struct lyd_meta *meta;
Michal Vasko8efac242023-03-30 08:24:56 +02001148 struct ly_ht *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001149 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001150
1151 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001152 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001153
Michal Vaskoe6323f62020-07-09 15:49:02 +02001154 /* handle specific user-ordered (leaf-)lists operations separately */
1155 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1156 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001157 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001158 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001159 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001160 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001161 /* duplicate the node */
1162 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001163 }
1164
Michal Vaskoe78faec2021-04-08 17:24:43 +02001165 /* get "key", "value", or "position" metadata string value */
1166 if (lysc_is_dup_inst_list(diff_node->schema)) {
1167 meta_str = "yang:position";
1168 } else if (diff_node->schema->nodetype == LYS_LIST) {
1169 meta_str = "yang:key";
1170 } else {
1171 meta_str = "yang:value";
1172 }
1173 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001174 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001175 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001176
Michal Vaskod59035b2020-07-08 12:00:06 +02001177 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001178 if (str_val[0]) {
1179 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001180 } else {
1181 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1182 }
1183 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001184 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001185 lyd_free_tree(match);
1186 }
1187 return ret;
1188 }
1189
1190 goto next_iter_r;
1191 }
1192
1193 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001194 switch (op) {
1195 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001196 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001197 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001198 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001199
1200 if (match->schema->nodetype & LYD_NODE_TERM) {
1201 /* special case of only dflt flag change */
1202 if (diff_node->flags & LYD_DEFAULT) {
1203 match->flags |= LYD_DEFAULT;
1204 } else {
1205 match->flags &= ~LYD_DEFAULT;
1206 }
1207 } else {
1208 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001209 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001210 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1211 LYD_NAME(diff_node));
1212 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001213 }
1214 }
1215 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001216 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001217 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001218 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001219
1220 /* insert it at the end */
1221 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001222 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001223 if (match->flags & LYD_EXT) {
Michal Vasko193dacd2022-10-13 08:43:05 +02001224 ret = lyplg_ext_insert(parent_node, match);
Michal Vasko19175b62022-04-01 09:17:07 +02001225 } else {
1226 ret = lyd_insert_child(parent_node, match);
1227 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001228 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001229 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001230 }
1231 if (ret) {
1232 lyd_free_tree(match);
1233 return ret;
1234 }
1235
1236 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001237 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001238 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001239 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001240 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001241
1242 /* remove it */
1243 if ((match == *first_node) && !match->parent) {
1244 assert(!parent_node);
1245 /* we have removed the top-level node */
1246 *first_node = (*first_node)->next;
1247 }
1248 lyd_free_tree(match);
1249
1250 /* we are not going recursively in this case, the whole subtree was already deleted */
1251 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001252 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001253 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1254 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1255 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1256 return LY_EINVAL;
1257 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001258
1259 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001260 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001261 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001262
Michal Vaskobaba84e2021-02-05 16:33:30 +01001263 /* update the value */
1264 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001265 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001266 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001267 } else {
1268 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001269
Michal Vaskoe78faec2021-04-08 17:24:43 +02001270 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001271 }
1272
1273 /* with flags */
1274 match->flags = diff_node->flags;
1275 break;
1276 default:
1277 LOGINT_RET(ctx);
1278 }
1279
1280next_iter_r:
1281 if (diff_cb) {
1282 /* call callback */
1283 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1284 }
1285
1286 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001287 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001288 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001289 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1290 if (ret) {
1291 break;
1292 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001293 }
1294
Michal Vaskod7c048c2021-05-18 16:12:55 +02001295 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001296 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001297}
1298
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001299LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001300lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001301 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001302{
1303 const struct lyd_node *root;
Michal Vasko8efac242023-03-30 08:24:56 +02001304 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001305 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001306
1307 LY_LIST_FOR(diff, root) {
1308 if (mod && (lyd_owner_module(root) != mod)) {
1309 /* skip data nodes from different modules */
1310 continue;
1311 }
1312
1313 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001314 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1315 if (ret) {
1316 break;
1317 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001318 }
1319
Michal Vaskod7c048c2021-05-18 16:12:55 +02001320 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001321 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001322}
1323
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001324LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001325lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001326{
1327 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1328}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001329
1330/**
1331 * @brief Update operations on a diff node when the new operation is NONE.
1332 *
1333 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001334 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001335 * @param[in] src_diff Current source diff node.
1336 * @return LY_ERR value.
1337 */
1338static LY_ERR
1339lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1340{
1341 switch (cur_op) {
1342 case LYD_DIFF_OP_NONE:
1343 case LYD_DIFF_OP_CREATE:
1344 case LYD_DIFF_OP_REPLACE:
1345 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1346 /* NONE on a term means only its dflt flag was changed */
1347 diff_match->flags &= ~LYD_DEFAULT;
1348 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1349 }
1350 break;
1351 default:
1352 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001353 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1354 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001355 }
1356
1357 return LY_SUCCESS;
1358}
1359
1360/**
Michal Vaskodb91fc32023-05-02 14:39:40 +02001361 * @brief Remove metadata/an attribute from a node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001362 *
Michal Vaskodb91fc32023-05-02 14:39:40 +02001363 * @param[in] node Node to update.
1364 * @param[in] name Metadata/attribute name.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001365 */
1366static void
1367lyd_diff_del_meta(struct lyd_node *node, const char *name)
1368{
1369 struct lyd_meta *meta;
Michal Vaskodb91fc32023-05-02 14:39:40 +02001370 struct lyd_attr *attr;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001371
Michal Vaskodb91fc32023-05-02 14:39:40 +02001372 lyd_diff_find_meta(node, name, &meta, &attr);
1373
1374 if (meta) {
1375 lyd_free_meta_single(meta);
1376 } else if (attr) {
1377 lyd_free_attr_single(LYD_CTX(node), attr);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001378 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001379}
1380
1381/**
1382 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001383 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001384 *
1385 * @param[in] node Node to change.
1386 * @param[in] op Operation to set.
1387 * @return LY_ERR value.
1388 */
1389static LY_ERR
1390lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1391{
Michal Vaskodb91fc32023-05-02 14:39:40 +02001392 lyd_diff_del_meta(node, "operation");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001393
Michal Vaskodb91fc32023-05-02 14:39:40 +02001394 if (node->schema) {
1395 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
1396 } else {
1397 return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001398 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001399}
1400
1401/**
1402 * @brief Update operations on a diff node when the new operation is REPLACE.
1403 *
1404 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001405 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001406 * @param[in] src_diff Current source diff node.
1407 * @return LY_ERR value.
1408 */
1409static LY_ERR
1410lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1411{
1412 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001413 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001414 struct lyd_meta *meta;
1415 const struct lys_module *mod;
1416 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001417 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001418
1419 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001420 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001421 assert(mod);
1422
1423 switch (cur_op) {
1424 case LYD_DIFF_OP_REPLACE:
1425 case LYD_DIFF_OP_CREATE:
1426 switch (diff_match->schema->nodetype) {
1427 case LYS_LIST:
1428 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001429 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001430 * keep orig_key/orig_value (only replace oper) and replace key/value */
1431 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001432 if (lysc_is_dup_inst_list(diff_match->schema)) {
1433 meta_name = "position";
1434 } else if (diff_match->schema->nodetype == LYS_LIST) {
1435 meta_name = "key";
1436 } else {
1437 meta_name = "value";
1438 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001439
1440 lyd_diff_del_meta(diff_match, meta_name);
1441 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001442 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001443 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001444 break;
1445 case LYS_LEAF:
1446 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001447 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001448 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1449 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001450 }
1451
Michal Vaskoe6323f62020-07-09 15:49:02 +02001452 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001453 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001454 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001455 }
1456
Michal Vasko8caadab2020-11-05 17:38:15 +01001457 if (cur_op == LYD_DIFF_OP_REPLACE) {
1458 /* compare values whether there is any change at all */
1459 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001460 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001461 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001462 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1463 if (!ret) {
1464 /* values are the same, remove orig-value meta and set oper to NONE */
1465 lyd_free_meta_single(meta);
1466 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1467 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001468 }
1469
1470 /* modify the default flag */
1471 diff_match->flags &= ~LYD_DEFAULT;
1472 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1473 break;
1474 case LYS_ANYXML:
1475 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001476 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001477 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1478 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001479 }
1480
1481 /* modify the node value */
1482 any = (struct lyd_node_any *)src_diff;
1483 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1484 break;
1485 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001486 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001487 }
1488 break;
1489 case LYD_DIFF_OP_NONE:
1490 /* it is moved now */
1491 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1492
1493 /* change the operation */
1494 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1495
Michal Vaskoe78faec2021-04-08 17:24:43 +02001496 /* set orig-meta and meta */
1497 if (lysc_is_dup_inst_list(diff_match->schema)) {
1498 meta_name = "position";
1499 orig_meta_name = "orig-position";
1500 } else {
1501 meta_name = "key";
1502 orig_meta_name = "orig-key";
1503 }
1504
1505 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001506 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001507 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001508
Michal Vaskoe78faec2021-04-08 17:24:43 +02001509 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001510 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001511 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001512 break;
1513 default:
1514 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001515 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1516 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001517 }
1518
1519 return LY_SUCCESS;
1520}
1521
1522/**
1523 * @brief Update operations in a diff node when the new operation is CREATE.
1524 *
1525 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001526 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001527 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001528 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001529 * @return LY_ERR value.
1530 */
1531static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001532lyd_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 +02001533{
1534 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001535 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001536 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001537 const char *meta_name, *orig_meta_name;
1538 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001539 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001540
1541 switch (cur_op) {
1542 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001543 /* remember current flags */
1544 trg_flags = diff_match->flags;
1545
Michal Vaskoe78faec2021-04-08 17:24:43 +02001546 if (lysc_is_userordered(diff_match->schema)) {
1547 /* get anchor metadata */
1548 if (lysc_is_dup_inst_list(diff_match->schema)) {
1549 meta_name = "yang:position";
1550 orig_meta_name = "yang:orig-position";
1551 } else if (diff_match->schema->nodetype == LYS_LIST) {
1552 meta_name = "yang:key";
1553 orig_meta_name = "yang:orig-key";
1554 } else {
1555 meta_name = "yang:value";
1556 orig_meta_name = "yang:orig-value";
1557 }
1558 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001559 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001560 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001561 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001562
1563 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1564 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001565 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001566 /* deleted + created at another position -> operation REPLACE */
1567 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1568
1569 /* add anchor metadata */
1570 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1571 } else {
1572 /* deleted + created at the same position -> operation NONE */
1573 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1574
1575 /* delete anchor metadata */
1576 lyd_free_meta_single(orig_meta);
1577 }
1578 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1579 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1580 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1581 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1582 }
1583
Radek Krejci55c4bd22021-04-26 08:09:04 +02001584 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1585 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001586 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1587 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1588 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1589 /* deleted + created -> operation NONE */
1590 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1591 } else {
1592 /* we deleted it, but it was created with a different value -> operation REPLACE */
1593 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1594
1595 /* current value is the previous one (meta) */
1596 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001597 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001598
1599 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001600 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001601 }
1602 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001603 /* deleted + created -> operation NONE */
1604 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001605 }
1606
1607 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001608 /* add orig-dflt metadata */
1609 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1610 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1611
Michal Vaskoe6323f62020-07-09 15:49:02 +02001612 /* update dflt flag itself */
1613 diff_match->flags &= ~LYD_DEFAULT;
1614 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001615 }
1616
1617 /* but the operation of its children should remain DELETE */
1618 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1619 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001620 }
1621 break;
1622 default:
1623 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001624 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1625 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001626 }
1627
1628 return LY_SUCCESS;
1629}
1630
1631/**
1632 * @brief Update operations on a diff node when the new operation is DELETE.
1633 *
1634 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001635 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001636 * @param[in] src_diff Current source diff node.
1637 * @return LY_ERR value.
1638 */
1639static LY_ERR
1640lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1641{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001642 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001643 struct lyd_meta *meta;
1644 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001645 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001646
1647 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001648 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 +02001649
1650 switch (cur_op) {
1651 case LYD_DIFF_OP_CREATE:
1652 /* it was created, but then deleted -> set NONE operation */
1653 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1654
1655 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1656 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001657 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1658 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001659 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001660 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001661 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001662 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1663 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001664 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001665 break;
1666 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001667 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001668 if (lysc_is_userordered(diff_match->schema)) {
1669 if (lysc_is_dup_inst_list(diff_match->schema)) {
1670 meta_name = "position";
1671 } else if (diff_match->schema->nodetype == LYS_LIST) {
1672 meta_name = "key";
1673 } else {
1674 meta_name = "value";
1675 }
1676 } else {
1677 assert(diff_match->schema->nodetype == LYS_LEAF);
1678
1679 /* switch value for the original one */
1680 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001681 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001682 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001683 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1684 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001685 }
1686
1687 /* switch default for the original one, then remove the meta */
1688 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001689 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001690 diff_match->flags &= ~LYD_DEFAULT;
1691 if (meta->value.boolean) {
1692 diff_match->flags |= LYD_DEFAULT;
1693 }
1694 lyd_free_meta_single(meta);
1695
1696 meta_name = "orig-value";
1697 }
1698 lyd_diff_del_meta(diff_match, meta_name);
1699
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001700 /* it was being changed, but should be deleted instead -> set DELETE operation */
1701 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1702 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001703 case LYD_DIFF_OP_NONE:
1704 /* it was not modified, but should be deleted -> set DELETE operation */
1705 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001706 break;
1707 default:
1708 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001709 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1710 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001711 }
1712
1713 return LY_SUCCESS;
1714}
1715
1716/**
1717 * @brief Check whether this diff node is redundant (does not change data).
1718 *
1719 * @param[in] diff Diff node.
1720 * @return 0 if not, non-zero if it is.
1721 */
1722static int
1723lyd_diff_is_redundant(struct lyd_node *diff)
1724{
1725 enum lyd_diff_op op;
1726 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1727 struct lyd_node *child;
1728 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001729 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001730
1731 assert(diff);
1732
Michal Vaskoe78faec2021-04-08 17:24:43 +02001733 if (lysc_is_dup_inst_list(diff->schema)) {
1734 /* all descendants are keys */
1735 child = NULL;
1736 } else {
1737 child = lyd_child_no_keys(diff);
1738 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001739 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001740 assert(mod);
1741
1742 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001743 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001744
1745 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001746 /* get metadata names */
1747 if (lysc_is_dup_inst_list(diff->schema)) {
1748 meta_name = "position";
1749 orig_meta_name = "orig-position";
1750 } else if (diff->schema->nodetype == LYS_LIST) {
1751 meta_name = "key";
1752 orig_meta_name = "orig-key";
1753 } else {
1754 meta_name = "value";
1755 orig_meta_name = "orig-value";
1756 }
1757
Michal Vaskoe6323f62020-07-09 15:49:02 +02001758 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001759 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1760 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001761 assert(orig_val_meta && val_meta);
1762
1763 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1764 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001765 lyd_free_meta_single(orig_val_meta);
1766 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001767 if (child) {
1768 /* change operation to NONE, we have siblings */
1769 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1770 return 0;
1771 }
1772
1773 /* redundant node, BUT !!
1774 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1775 * because the data that this is applied on should not change for the diff lifetime.
1776 * However, when we are merging 2 diffs, this conversion is actually lossy because
1777 * if the data change, the move operation can also change its meaning. In this specific
1778 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1779 */
1780 return 1;
1781 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001782 } else if (op == LYD_DIFF_OP_NONE) {
1783 if (!diff->schema) {
1784 /* opaque node with none must be redundant */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001785 return 1;
1786 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001787
1788 if (diff->schema->nodetype & LYD_NODE_TERM) {
1789 /* check whether at least the default flags are different */
1790 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1791 assert(meta);
1792 str = lyd_get_meta_value(meta);
1793
1794 /* if previous and current dflt flags are the same, this node is redundant */
1795 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1796 return 1;
1797 }
1798 return 0;
1799 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001800 }
1801
1802 if (!child && (op == LYD_DIFF_OP_NONE)) {
1803 return 1;
1804 }
1805
1806 return 0;
1807}
1808
1809/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001810 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001811 *
1812 * @param[in] src_diff Source diff node.
1813 * @param[in] diff_parent Current sysrepo diff parent.
1814 * @param[in] diff_cb Optional diff callback.
1815 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001816 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001817 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001818 * @param[in,out] diff Diff root node.
1819 * @return LY_ERR value.
1820 */
1821static LY_ERR
1822lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vasko8efac242023-03-30 08:24:56 +02001823 struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001824{
1825 LY_ERR ret = LY_SUCCESS;
1826 struct lyd_node *child, *diff_node = NULL;
1827 enum lyd_diff_op src_op, cur_op;
Michal Vasko8efac242023-03-30 08:24:56 +02001828 struct ly_ht *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001829
1830 /* get source node operation */
1831 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1832
1833 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001834 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 +02001835
1836 if (diff_node) {
1837 /* get target (current) operation */
1838 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1839
1840 /* merge operations */
1841 switch (src_op) {
1842 case LYD_DIFF_OP_REPLACE:
1843 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1844 break;
1845 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001846 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001847 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001848 goto add_diff;
1849 }
1850
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001851 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001852 break;
1853 case LYD_DIFF_OP_DELETE:
1854 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1855 break;
1856 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001857 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1858 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001859 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1860 break;
1861 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001862 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001863 }
1864 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001865 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001866 return ret;
1867 }
1868
1869 if (diff_cb) {
1870 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001871 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001872 }
1873
1874 /* update diff parent */
1875 diff_parent = diff_node;
1876
Michal Vaskoe78faec2021-04-08 17:24:43 +02001877 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1878 * so there is nothing to merge for these "keys" */
1879 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1880 /* merge src_diff recursively */
1881 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1882 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1883 if (ret) {
1884 break;
1885 }
1886 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001887 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001888 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001889 }
1890 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001891add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001892 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001893 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1894 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
1895 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1896 } else {
1897 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
1898 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1899 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001900
1901 /* insert node into diff if not already */
1902 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001903 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001904 }
1905
1906 /* update operation */
1907 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1908
1909 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001910 /* call callback with no source diff node since it was duplicated and just added */
1911 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001912 }
1913
1914 /* update diff parent */
1915 diff_parent = diff_node;
1916 }
1917
1918 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001919 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001920 if (diff_parent == *diff) {
1921 *diff = (*diff)->next;
1922 }
1923 lyd_free_tree(diff_parent);
1924 }
1925
1926 return LY_SUCCESS;
1927}
1928
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001929LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001930lyd_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 +01001931 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001932{
1933 const struct lyd_node *src_root;
Michal Vasko8efac242023-03-30 08:24:56 +02001934 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001935 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001936
1937 LY_LIST_FOR(src_diff, src_root) {
1938 if (mod && (lyd_owner_module(src_root) != mod)) {
1939 /* skip data nodes from different modules */
1940 continue;
1941 }
1942
1943 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001944 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 +02001945 }
1946
Michal Vaskoe78faec2021-04-08 17:24:43 +02001947cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001948 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001949 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001950}
1951
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001952LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001953lyd_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 +01001954 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001955{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001956 LY_ERR ret;
Michal Vasko8efac242023-03-30 08:24:56 +02001957 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001958
Michal Vasko04f85912020-08-07 12:14:58 +02001959 if (!src_sibling) {
1960 return LY_SUCCESS;
1961 }
1962
Michal Vaskoe78faec2021-04-08 17:24:43 +02001963 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 +02001964 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001965 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001966}
1967
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001968LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001969lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001970{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001971 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001972}
Michal Vasko4231fb62020-07-13 13:54:47 +02001973
1974static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001975lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001976{
1977 LY_ERR ret = LY_SUCCESS;
1978 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001979 const char *val1 = NULL;
1980 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001981 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001982
Michal Vaskobaba84e2021-02-05 16:33:30 +01001983 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1984
1985 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001986 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001987
1988 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001989 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001990
1991 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001992 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001993 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001994 } else {
1995 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1996 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001997
1998 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001999 flags = node->flags;
2000 if (node->schema->nodetype == LYS_LEAF) {
2001 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
2002 } else {
2003 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02002004
Michal Vaskobaba84e2021-02-05 16:33:30 +01002005 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
2006 }
2007 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002008 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
2009
2010cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002011 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002012 return ret;
2013}
2014
2015static LY_ERR
2016lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
2017{
2018 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002019 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02002020
2021 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01002022 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02002023
2024 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002025 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02002026 flag1 = LYD_DEFAULT;
2027 } else {
2028 flag1 = 0;
2029 }
2030
2031 /* current default */
2032 flag2 = node->flags & LYD_DEFAULT;
2033
Michal Vasko610e93b2020-11-09 20:58:32 +01002034 if (flag1 == flag2) {
2035 /* no default state change so nothing to reverse */
2036 return LY_SUCCESS;
2037 }
2038
Michal Vasko4231fb62020-07-13 13:54:47 +02002039 /* switch defaults */
2040 node->flags &= ~LYD_DEFAULT;
2041 node->flags |= flag1;
2042 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
2043
2044 return LY_SUCCESS;
2045}
2046
2047static LY_ERR
2048lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
2049{
2050 LY_ERR ret = LY_SUCCESS;
2051 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002052 const char *val1 = NULL;
2053 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02002054
2055 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002056 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002057
2058 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002059 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002060
2061 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002062 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02002063
2064 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002065 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02002066
2067 /* switch values */
2068 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2069 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2070
2071cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002072 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002073 return ret;
2074}
2075
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002076/**
2077 * @brief Remove specific operation from all the nodes in a subtree.
2078 *
2079 * @param[in] diff Diff subtree to process.
2080 * @param[in] op Only expected operation.
2081 * @return LY_ERR value.
2082 */
2083static LY_ERR
2084lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2085{
2086 struct lyd_node *elem;
2087 struct lyd_meta *meta;
2088
2089 LYD_TREE_DFS_BEGIN(diff, elem) {
2090 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2091 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002092 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 +01002093 lyd_free_meta_single(meta);
2094 }
2095
2096 LYD_TREE_DFS_END(diff, elem);
2097 }
2098
2099 return LY_SUCCESS;
2100}
2101
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002102LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002103lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002104{
2105 LY_ERR ret = LY_SUCCESS;
2106 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002107 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002108 enum lyd_diff_op op;
2109
2110 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2111
2112 if (!src_diff) {
2113 *diff = NULL;
2114 return LY_SUCCESS;
2115 }
2116
2117 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002118 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002119
2120 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002121 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2122 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002123
2124 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002125 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002126 /* skip all keys */
2127 if (!lysc_is_key(elem->schema)) {
2128 /* find operation attribute, if any */
2129 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002130
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002131 switch (op) {
2132 case LYD_DIFF_OP_CREATE:
2133 /* reverse create to delete */
2134 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002135
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002136 /* check all the children for the same operation, nothing else is expected */
2137 LY_LIST_FOR(lyd_child(elem), iter) {
2138 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2139 }
2140
Michal Vasko9e070522021-03-05 14:00:14 +01002141 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002142 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002143 case LYD_DIFF_OP_DELETE:
2144 /* reverse delete to create */
2145 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002146
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002147 /* check all the children for the same operation, nothing else is expected */
2148 LY_LIST_FOR(lyd_child(elem), iter) {
2149 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2150 }
2151
Michal Vasko9e070522021-03-05 14:00:14 +01002152 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002153 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002154 case LYD_DIFF_OP_REPLACE:
2155 switch (elem->schema->nodetype) {
2156 case LYS_LEAF:
2157 /* leaf value change */
2158 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2159 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2160 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002161 case LYS_ANYXML:
2162 case LYS_ANYDATA:
2163 /* any value change */
2164 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2165 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002166 case LYS_LEAFLIST:
2167 /* leaf-list move */
2168 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002169 if (lysc_is_dup_inst_list(elem->schema)) {
2170 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2171 } else {
2172 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2173 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002174 break;
2175 case LYS_LIST:
2176 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002177 if (lysc_is_dup_inst_list(elem->schema)) {
2178 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2179 } else {
2180 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2181 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002182 break;
2183 default:
2184 LOGINT(LYD_CTX(src_diff));
2185 ret = LY_EINT;
2186 goto cleanup;
2187 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002188 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002189 case LYD_DIFF_OP_NONE:
2190 switch (elem->schema->nodetype) {
2191 case LYS_LEAF:
2192 case LYS_LEAFLIST:
2193 /* default flag change */
2194 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2195 break;
2196 default:
2197 /* nothing to do */
2198 break;
2199 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002200 break;
2201 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002202 }
2203
Michal Vasko56daf732020-08-10 10:57:18 +02002204 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002205 }
2206 }
2207
2208cleanup:
2209 if (ret) {
2210 lyd_free_siblings(*diff);
2211 *diff = NULL;
2212 }
2213 return ret;
2214}