blob: 63c2fe3937d55ad3af31959719eca1cabcd4f7ac [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 Vasko3a41dff2020-07-15 14:30:28 +0200216 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100217 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200218
219 /* find the first duplicated parent */
220 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100221 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200222 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100223 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200224 }
225 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100226 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200227 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100228 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200229 }
230 }
231
232 /* no parent existed, must be manually connected */
233 if (!diff_parent) {
234 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200235 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200236 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200237 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200238 }
239
Michal Vaskod59035b2020-07-08 12:00:06 +0200240 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200241 if (diff_parent && (diff_parent != dup)) {
Michal Vasko2e552792022-11-02 12:15:31 +0100242 LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200243 }
244
245 /* add subtree operation */
Michal Vasko2e552792022-11-02 12:15:31 +0100246 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200247
Michal Vaskocffc3f92022-06-15 07:57:24 +0200248 if (op == LYD_DIFF_OP_CREATE) {
249 /* all nested user-ordered (leaf-)lists need special metadata for create op */
250 LYD_TREE_DFS_BEGIN(dup, elem) {
251 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
252 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
253 }
254 LYD_TREE_DFS_END(dup, elem);
255 }
256 }
257
Michal Vaskod59035b2020-07-08 12:00:06 +0200258 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200259 if (orig_default) {
Michal Vasko2e552792022-11-02 12:15:31 +0100260 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200261 }
262
263 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200264 if (orig_value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100265 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200266 }
267
268 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200269 if (key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100270 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200271 }
272
273 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200274 if (value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100275 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200276 }
277
Michal Vaskoe78faec2021-04-08 17:24:43 +0200278 /* position */
279 if (position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100280 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200281 }
282
Michal Vaskod59035b2020-07-08 12:00:06 +0200283 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200284 if (orig_key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100285 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200286 }
287
Michal Vaskoe78faec2021-04-08 17:24:43 +0200288 /* orig-position */
289 if (orig_position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100290 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200291 }
292
Michal Vaskod59035b2020-07-08 12:00:06 +0200293 return LY_SUCCESS;
294}
295
296/**
297 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
298 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100299 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200300 * @param[in] schema Schema node of the list/leaf-list.
301 * @param[in,out] userord Sized array of userord items.
302 * @return Userord item for all the user-ordered list/leaf-list instances.
303 */
304static struct lyd_diff_userord *
305lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
306{
307 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200308 struct lyd_node *iter;
309 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200310 LY_ARRAY_COUNT_TYPE u;
311
312 LY_ARRAY_FOR(*userord, u) {
313 if ((*userord)[u].schema == schema) {
314 return &(*userord)[u];
315 }
316 }
317
318 /* it was not added yet, add it now */
319 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
320
321 item->schema = schema;
322 item->pos = 0;
323 item->inst = NULL;
324
325 /* store all the instance pointers in the current order */
326 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200327 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
328 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
329 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200330 }
331 }
332
333 return item;
334}
335
336/**
337 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
338 * lists/leaf-lists.
339 *
340 * @param[in] first Node from the first tree, can be NULL (on create).
341 * @param[in] second Node from the second tree, can be NULL (on delete).
342 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100343 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200344 * @param[out] op Operation.
345 * @param[out] orig_default Original default metadata.
346 * @param[out] value Value metadata.
347 * @param[out] orig_value Original value metadata
348 * @param[out] key Key metadata.
349 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200350 * @param[out] position Position metadata.
351 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200352 * @return LY_SUCCESS on success,
353 * @return LY_ENOT if there is no change to be added into diff,
354 * @return LY_ERR value on other errors.
355 */
356static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200357lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100358 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200359 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200360{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200361 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200362 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200363 size_t buflen, bufused;
364 uint32_t first_pos, second_pos;
Michal Vaskod59035b2020-07-08 12:00:06 +0200365
366 assert(first || second);
367
368 *orig_default = NULL;
369 *value = NULL;
370 *orig_value = NULL;
371 *key = NULL;
372 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200373 *position = NULL;
374 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200375
376 schema = first ? first->schema : second->schema;
377 assert(lysc_is_userordered(schema));
378
Michal Vaskod59035b2020-07-08 12:00:06 +0200379 /* find user-ordered first position */
380 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200381 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200382 if (userord_item->inst[first_pos] == first) {
383 break;
384 }
385 }
386 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
387 } else {
388 first_pos = 0;
389 }
390
Michal Vaskoe78faec2021-04-08 17:24:43 +0200391 /* prepare position of the next instance */
392 second_pos = userord_item->pos++;
393
Michal Vaskod59035b2020-07-08 12:00:06 +0200394 /* learn operation first */
395 if (!second) {
396 *op = LYD_DIFF_OP_DELETE;
397 } else if (!first) {
398 *op = LYD_DIFF_OP_CREATE;
399 } else {
Michal Vasko8f359bf2020-07-28 10:41:15 +0200400 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200401 /* in first, there is a different instance on the second position, we are going to move 'first' node */
402 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200403 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200404 /* default flag change */
405 *op = LYD_DIFF_OP_NONE;
406 } else {
407 /* no changes */
408 return LY_ENOT;
409 }
410 }
411
412 /*
413 * set each attribute correctly based on the operation and node type
414 */
415
416 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100417 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200418 if (first->flags & LYD_DEFAULT) {
419 *orig_default = "true";
420 } else {
421 *orig_default = "false";
422 }
423 }
424
425 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200426 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
427 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200428 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200429 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200430 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200431 } else {
432 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200433 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200434 }
435 }
436
437 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200438 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
439 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200440 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200441 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200442 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200443 } else {
444 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200445 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200446 }
447 }
448
449 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200450 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
451 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200452 if (second_pos) {
453 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200454 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 +0200455 } else {
456 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200457 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200458 }
459 }
460
461 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200462 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
463 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200464 if (first_pos) {
465 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200466 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 +0200467 } else {
468 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200469 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200470 }
471 }
472
Michal Vaskoe78faec2021-04-08 17:24:43 +0200473 /* position */
474 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
475 if (second_pos) {
476 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
477 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200478 rc = LY_EMEM;
479 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200480 }
481 } else {
482 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200483 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200484 }
485 }
486
487 /* orig-position */
488 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
489 if (first_pos) {
490 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
491 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200492 rc = LY_EMEM;
493 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200494 }
495 } else {
496 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200497 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200498 }
499 }
500
Michal Vaskod59035b2020-07-08 12:00:06 +0200501 /*
502 * update our instances - apply the change
503 */
504 if (*op == LYD_DIFF_OP_CREATE) {
505 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200506 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200507 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
508 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
509 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
510 }
511 LY_ARRAY_INCREMENT(userord_item->inst);
512 userord_item->inst[second_pos] = second;
513
514 } else if (*op == LYD_DIFF_OP_DELETE) {
515 /* remove the instance */
516 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
517 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
518 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
519 }
520 LY_ARRAY_DECREMENT(userord_item->inst);
521
522 } else if (*op == LYD_DIFF_OP_REPLACE) {
523 /* move the instances */
524 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
525 (first_pos - second_pos) * sizeof *userord_item->inst);
526 userord_item->inst[second_pos] = first;
527 }
528
Michal Vaskof9b052a2022-06-08 10:26:53 +0200529cleanup:
530 if (rc) {
531 free(*value);
532 *value = NULL;
533 free(*orig_value);
534 *orig_value = NULL;
535 free(*key);
536 *key = NULL;
537 free(*orig_key);
538 *orig_key = NULL;
539 free(*position);
540 *position = NULL;
541 free(*orig_position);
542 *orig_position = NULL;
543 }
544 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200545}
546
547/**
548 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
549 * lists/leaf-lists.
550 *
551 * @param[in] first Node from the first tree, can be NULL (on create).
552 * @param[in] second Node from the second tree, can be NULL (on delete).
553 * @param[in] options Diff options.
554 * @param[out] op Operation.
555 * @param[out] orig_default Original default metadata.
556 * @param[out] orig_value Original value metadata.
557 * @return LY_SUCCESS on success,
558 * @return LY_ENOT if there is no change to be added into diff,
559 * @return LY_ERR value on other errors.
560 */
561static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200562lyd_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 +0200563 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200564{
565 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200566 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200567
568 assert(first || second);
569
570 *orig_default = NULL;
571 *orig_value = NULL;
572
573 schema = first ? first->schema : second->schema;
574 assert(!lysc_is_userordered(schema));
575
576 /* learn operation first */
577 if (!second) {
578 *op = LYD_DIFF_OP_DELETE;
579 } else if (!first) {
580 *op = LYD_DIFF_OP_CREATE;
581 } else {
582 switch (schema->nodetype) {
583 case LYS_CONTAINER:
584 case LYS_RPC:
585 case LYS_ACTION:
586 case LYS_NOTIF:
587 /* no changes */
588 return LY_ENOT;
589 case LYS_LIST:
590 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200591 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200592 /* default flag change */
593 *op = LYD_DIFF_OP_NONE;
594 } else {
595 /* no changes */
596 return LY_ENOT;
597 }
598 break;
599 case LYS_LEAF:
600 case LYS_ANYXML:
601 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200602 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200603 /* different values */
604 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200605 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200606 /* default flag change */
607 *op = LYD_DIFF_OP_NONE;
608 } else {
609 /* no changes */
610 return LY_ENOT;
611 }
612 break;
613 default:
614 LOGINT_RET(schema->module->ctx);
615 }
616 }
617
618 /*
619 * set each attribute correctly based on the operation and node type
620 */
621
622 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100623 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200624 if (first->flags & LYD_DEFAULT) {
625 *orig_default = "true";
626 } else {
627 *orig_default = "false";
628 }
629 }
630
631 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100632 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
633 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200634 str_val = lyd_get_value(first);
635 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100636 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
637 } else {
638 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
639 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200640 }
641
642 return LY_SUCCESS;
643}
644
645/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200646 * @brief Find a matching instance of a node in a data tree.
647 *
648 * @param[in] siblings Siblings to search in.
649 * @param[in] target Target node to search for.
650 * @param[in] defaults Whether to consider (or ignore) default values.
Michal Vasko271d2e32023-01-31 15:43:19 +0100651 * @param[in,out] dup_inst_ht Duplicate instance cache.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200652 * @param[out] match Found match, NULL if no matching node found.
653 * @return LY_ERR value.
654 */
655static LY_ERR
656lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vasko271d2e32023-01-31 15:43:19 +0100657 struct hash_table **dup_inst_ht, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200658{
Michal Vasko2bd856f2022-12-02 14:03:29 +0100659 LY_ERR r;
660
Michal Vaskoe78faec2021-04-08 17:24:43 +0200661 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
662 /* try to find the exact instance */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100663 r = lyd_find_sibling_first(siblings, target, match);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200664 } else {
665 /* try to simply find the node, there cannot be more instances */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100666 r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
667 }
668 if (r && (r != LY_ENOTFOUND)) {
669 return r;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200670 }
671
Michal Vaskod7c048c2021-05-18 16:12:55 +0200672 /* update match as needed */
Michal Vasko271d2e32023-01-31 15:43:19 +0100673 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200674
675 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
676 /* ignore default nodes */
677 *match = NULL;
678 }
679 return LY_SUCCESS;
680}
681
682/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200683 * @brief Perform diff for all siblings at certain depth, recursively.
684 *
685 * For user-ordered lists/leaf-lists a specific structure is used for storing
686 * the current order. The idea is to apply all the generated diff changes
687 * virtually on the first tree so that we can continue to generate correct
688 * changes after some were already generated.
689 *
690 * The algorithm then uses second tree position-based changes with a before
691 * (preceding) item anchor.
692 *
693 * Example:
694 *
695 * Virtual first tree leaf-list order:
696 * 1 2 [3] 4 5
697 *
698 * Second tree leaf-list order:
699 * 1 2 [5] 3 4
700 *
701 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
702 * match - they do not - move nodes so that the 3rd position node is final ->
703 * -> move node 5 to the 3rd position -> move node 5 after node 2.
704 *
705 * Required properties:
706 * Stored operations (move) should not be affected by later operations -
707 * - would cause a redundantly long list of operations, possibly inifinite.
708 *
709 * Implemenation justification:
710 * First, all delete operations and only then move/create operations are stored.
711 * Also, preceding anchor is used and after each iteration another node is
712 * at its final position. That results in the invariant that all preceding
713 * nodes are final and will not be changed by the later operations, meaning
714 * they can safely be used as anchors for the later operations.
715 *
716 * @param[in] first First tree first sibling.
717 * @param[in] second Second tree first sibling.
718 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200719 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200720 * @param[in,out] diff Diff to append to.
721 * @return LY_ERR value.
722 */
723static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200724lyd_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 +0200725 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200726{
727 LY_ERR ret = LY_SUCCESS;
728 const struct lyd_node *iter_first, *iter_second;
729 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100730 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vasko271d2e32023-01-31 15:43:19 +0100731 struct hash_table *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200732 LY_ARRAY_COUNT_TYPE u;
733 enum lyd_diff_op op;
734 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200735 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200736
Michal Vaskod59035b2020-07-08 12:00:06 +0200737 /* compare first tree to the second tree - delete, replace, none */
738 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200739 if (!iter_first->schema) {
740 continue;
741 }
742
Michal Vaskod59035b2020-07-08 12:00:06 +0200743 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200744 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200745 /* skip default nodes */
746 continue;
747 }
748
Michal Vaskoe78faec2021-04-08 17:24:43 +0200749 /* find a match in the second tree */
750 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
751 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200752
753 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100754 /* get (create) userord entry */
755 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
756 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
757
Michal Vaskoe78faec2021-04-08 17:24:43 +0200758 /* we are handling only user-ordered node delete now */
759 if (!match_second) {
760 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100761 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
762 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200763
764 /* there must be changes, it is deleted */
765 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100766 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
767 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200768
769 free(orig_value);
770 free(key);
771 free(value);
772 free(position);
773 free(orig_key);
774 free(orig_position);
775 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200776 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200777 } else {
778 /* get all the attributes */
779 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
780
781 /* add into diff if there are any changes */
782 if (!ret) {
783 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200784 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200785 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100786 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200787 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200788 }
789
790 free(orig_value);
791 LY_CHECK_GOTO(ret, cleanup);
792 } else if (ret == LY_ENOT) {
793 ret = LY_SUCCESS;
794 } else {
795 goto cleanup;
796 }
797 }
798
799 /* check descendants, if any, recursively */
800 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200801 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
802 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200803 }
804
805 if (nosiblings) {
806 break;
807 }
808 }
809
810 /* reset all cached positions */
811 LY_ARRAY_FOR(userord, u) {
812 userord[u].pos = 0;
813 }
814
815 /* compare second tree to the first tree - create, user-ordered move */
816 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200817 if (!iter_second->schema) {
818 continue;
819 }
820
Michal Vaskod59035b2020-07-08 12:00:06 +0200821 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200822 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200823 /* skip default nodes */
824 continue;
825 }
826
Michal Vaskoe78faec2021-04-08 17:24:43 +0200827 /* find a match in the first tree */
828 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
829 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200830
831 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100832 /* get userord entry */
Michal Vaskoaa51cc52022-12-06 09:57:11 +0100833 userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord);
Michal Vasko5da938a2022-03-01 09:19:02 +0100834 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
835
Michal Vaskod59035b2020-07-08 12:00:06 +0200836 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100837 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200838 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200839
840 /* add into diff if there are any changes */
841 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200842 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
843 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200844
845 free(orig_value);
846 free(key);
847 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200848 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200849 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200850 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200851 LY_CHECK_GOTO(ret, cleanup);
852 } else if (ret == LY_ENOT) {
853 ret = LY_SUCCESS;
854 } else {
855 goto cleanup;
856 }
857 } else if (!match_first) {
858 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200859 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 +0200860
861 /* there must be changes, it is created */
862 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200863 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200864
865 free(orig_value);
866 LY_CHECK_GOTO(ret, cleanup);
867 } /* else was handled */
868
869 if (nosiblings) {
870 break;
871 }
872 }
873
874cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200875 lyd_dup_inst_free(dup_inst_first);
876 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200877 LY_ARRAY_FOR(userord, u) {
878 LY_ARRAY_FREE(userord[u].inst);
879 }
880 LY_ARRAY_FREE(userord);
881 return ret;
882}
883
Michal Vasko3a41dff2020-07-15 14:30:28 +0200884static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100885lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
886 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200887{
888 const struct ly_ctx *ctx;
889
890 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
891
892 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200893 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200894 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200895 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200896 } else {
897 ctx = NULL;
898 }
899
900 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
901 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
902 return LY_EINVAL;
903 }
904
905 *diff = NULL;
906
Michal Vasko3a41dff2020-07-15 14:30:28 +0200907 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
908}
909
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100910LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200911lyd_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 +0200912{
913 return lyd_diff(first, second, options, 1, diff);
914}
915
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100916LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200917lyd_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 +0200918{
919 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200920}
921
922/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200923 * @brief Learn operation of a diff node.
924 *
925 * @param[in] diff_node Diff node.
926 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200927 * @return LY_ERR value.
928 */
929static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200930lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200931{
932 struct lyd_meta *meta = NULL;
933 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200934 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100935 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +0200936
Michal Vasko9e685082021-01-29 14:49:09 +0100937 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200938 LY_LIST_FOR(diff_parent->meta, meta) {
939 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200940 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200941 if ((str[0] == 'r') && (diff_parent != diff_node)) {
942 /* we do not care about this operation if it's in our parent */
943 continue;
944 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200945 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200946 break;
947 }
948 }
949 if (meta) {
950 break;
951 }
952 }
Michal Vasko52afd7d2022-01-18 14:08:34 +0100953
954 if (!meta) {
955 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
956 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
957 free(path);
958 return LY_EINT;
959 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200960
Michal Vaskod59035b2020-07-08 12:00:06 +0200961 return LY_SUCCESS;
962}
963
964/**
965 * @brief Insert a diff node into a data tree.
966 *
967 * @param[in,out] first_node First sibling of the data tree.
968 * @param[in] parent_node Data tree sibling parent node.
969 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200970 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
971 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200972 * @return err_info, NULL on success.
973 */
974static LY_ERR
975lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200976 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200977{
978 LY_ERR ret;
979 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200980 uint32_t pos, anchor_pos;
981 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200982
983 assert(new_node);
984
985 if (!*first_node) {
986 if (!parent_node) {
987 /* no parent or siblings */
988 *first_node = new_node;
989 return LY_SUCCESS;
990 }
991
992 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200993 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200994 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200995 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200996 return LY_EINVAL;
997 }
Michal Vaskob104f112020-07-17 09:54:54 +0200998 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200999 }
1000
Michal Vasko9e685082021-01-29 14:49:09 +01001001 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001002
Michal Vaskod59035b2020-07-08 12:00:06 +02001003 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +02001004 /* simple insert */
1005 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001006 }
1007
Michal Vaskoe78faec2021-04-08 17:24:43 +02001008 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001009 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001010 if (lysc_is_dup_inst_list(new_node->schema)) {
1011 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001012 if (!anchor_pos) {
1013 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1014 return LY_EINVAL;
1015 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001016
1017 found = 0;
1018 pos = 1;
1019 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1020 if (pos == anchor_pos) {
1021 found = 1;
1022 break;
1023 }
1024 ++pos;
1025 }
1026 if (!found) {
1027 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1028 new_node->schema->name);
1029 return LY_EINVAL;
1030 }
1031 } else {
1032 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1033 if (ret == LY_ENOTFOUND) {
1034 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1035 new_node->schema->name);
1036 return LY_EINVAL;
1037 } else if (ret) {
1038 return ret;
1039 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001040 }
1041
1042 /* insert after */
1043 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1044 assert(new_node->prev == anchor);
1045 if (*first_node == new_node) {
1046 *first_node = anchor;
1047 }
1048 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001049 /* find the first instance */
1050 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1051 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001052
Michal Vaskoea7d3232022-04-19 12:01:36 +02001053 if (anchor) {
1054 /* insert before the first instance */
1055 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1056 if ((*first_node)->prev->next) {
1057 assert(!new_node->prev->next);
1058 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001059 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001060 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001061 /* insert anywhere */
1062 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001063 }
1064 }
1065
1066 return LY_SUCCESS;
1067}
1068
1069/**
1070 * @brief Apply diff subtree on data tree nodes, recursively.
1071 *
1072 * @param[in,out] first_node First sibling of the data tree.
1073 * @param[in] parent_node Parent of the first sibling.
1074 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001075 * @param[in] diff_cb Optional diff callback.
1076 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001077 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001078 * @return LY_ERR value.
1079 */
1080static LY_ERR
1081lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Michal Vasko271d2e32023-01-31 15:43:19 +01001082 lyd_diff_cb diff_cb, void *cb_data, struct hash_table **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001083{
1084 LY_ERR ret;
1085 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001086 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001087 enum lyd_diff_op op;
1088 struct lyd_meta *meta;
Michal Vasko271d2e32023-01-31 15:43:19 +01001089 struct hash_table *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001090 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001091
1092 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001093 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001094
Michal Vaskoe6323f62020-07-09 15:49:02 +02001095 /* handle specific user-ordered (leaf-)lists operations separately */
1096 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1097 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001098 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001099 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001100 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001101 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001102 /* duplicate the node */
1103 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001104 }
1105
Michal Vaskoe78faec2021-04-08 17:24:43 +02001106 /* get "key", "value", or "position" metadata string value */
1107 if (lysc_is_dup_inst_list(diff_node->schema)) {
1108 meta_str = "yang:position";
1109 } else if (diff_node->schema->nodetype == LYS_LIST) {
1110 meta_str = "yang:key";
1111 } else {
1112 meta_str = "yang:value";
1113 }
1114 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001115 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001116 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001117
Michal Vaskod59035b2020-07-08 12:00:06 +02001118 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001119 if (str_val[0]) {
1120 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001121 } else {
1122 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1123 }
1124 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001125 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001126 lyd_free_tree(match);
1127 }
1128 return ret;
1129 }
1130
1131 goto next_iter_r;
1132 }
1133
1134 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001135 switch (op) {
1136 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001137 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001138 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001139 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001140
1141 if (match->schema->nodetype & LYD_NODE_TERM) {
1142 /* special case of only dflt flag change */
1143 if (diff_node->flags & LYD_DEFAULT) {
1144 match->flags |= LYD_DEFAULT;
1145 } else {
1146 match->flags &= ~LYD_DEFAULT;
1147 }
1148 } else {
1149 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001150 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001151 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1152 LYD_NAME(diff_node));
1153 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001154 }
1155 }
1156 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001157 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001158 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001159 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001160
1161 /* insert it at the end */
1162 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001163 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001164 if (match->flags & LYD_EXT) {
Michal Vasko193dacd2022-10-13 08:43:05 +02001165 ret = lyplg_ext_insert(parent_node, match);
Michal Vasko19175b62022-04-01 09:17:07 +02001166 } else {
1167 ret = lyd_insert_child(parent_node, match);
1168 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001169 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001170 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001171 }
1172 if (ret) {
1173 lyd_free_tree(match);
1174 return ret;
1175 }
1176
1177 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001178 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001179 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001180 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001181 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001182
1183 /* remove it */
1184 if ((match == *first_node) && !match->parent) {
1185 assert(!parent_node);
1186 /* we have removed the top-level node */
1187 *first_node = (*first_node)->next;
1188 }
1189 lyd_free_tree(match);
1190
1191 /* we are not going recursively in this case, the whole subtree was already deleted */
1192 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001193 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001194 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1195 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1196 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1197 return LY_EINVAL;
1198 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001199
1200 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001201 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001202 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001203
Michal Vaskobaba84e2021-02-05 16:33:30 +01001204 /* update the value */
1205 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001206 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001207 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001208 } else {
1209 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001210
Michal Vaskoe78faec2021-04-08 17:24:43 +02001211 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001212 }
1213
1214 /* with flags */
1215 match->flags = diff_node->flags;
1216 break;
1217 default:
1218 LOGINT_RET(ctx);
1219 }
1220
1221next_iter_r:
1222 if (diff_cb) {
1223 /* call callback */
1224 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1225 }
1226
1227 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001228 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001229 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001230 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1231 if (ret) {
1232 break;
1233 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001234 }
1235
Michal Vaskod7c048c2021-05-18 16:12:55 +02001236 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001237 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001238}
1239
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001240LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001241lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001242 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001243{
1244 const struct lyd_node *root;
Michal Vasko271d2e32023-01-31 15:43:19 +01001245 struct hash_table *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001246 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001247
1248 LY_LIST_FOR(diff, root) {
1249 if (mod && (lyd_owner_module(root) != mod)) {
1250 /* skip data nodes from different modules */
1251 continue;
1252 }
1253
1254 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001255 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1256 if (ret) {
1257 break;
1258 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001259 }
1260
Michal Vaskod7c048c2021-05-18 16:12:55 +02001261 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001262 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001263}
1264
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001265LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001266lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001267{
1268 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1269}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001270
1271/**
1272 * @brief Update operations on a diff node when the new operation is NONE.
1273 *
1274 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001275 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001276 * @param[in] src_diff Current source diff node.
1277 * @return LY_ERR value.
1278 */
1279static LY_ERR
1280lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1281{
1282 switch (cur_op) {
1283 case LYD_DIFF_OP_NONE:
1284 case LYD_DIFF_OP_CREATE:
1285 case LYD_DIFF_OP_REPLACE:
1286 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1287 /* NONE on a term means only its dflt flag was changed */
1288 diff_match->flags &= ~LYD_DEFAULT;
1289 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1290 }
1291 break;
1292 default:
1293 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001294 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1295 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001296 }
1297
1298 return LY_SUCCESS;
1299}
1300
1301/**
1302 * @brief Remove an attribute from a node.
1303 *
1304 * @param[in] node Node with the metadata.
1305 * @param[in] name Metadata name.
1306 */
1307static void
1308lyd_diff_del_meta(struct lyd_node *node, const char *name)
1309{
1310 struct lyd_meta *meta;
1311
1312 LY_LIST_FOR(node->meta, meta) {
1313 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001314 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001315 return;
1316 }
1317 }
1318
1319 assert(0);
1320}
1321
1322/**
1323 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001324 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001325 *
1326 * @param[in] node Node to change.
1327 * @param[in] op Operation to set.
1328 * @return LY_ERR value.
1329 */
1330static LY_ERR
1331lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1332{
1333 struct lyd_meta *meta;
1334
1335 LY_LIST_FOR(node->meta, meta) {
1336 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001337 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001338 break;
1339 }
1340 }
1341
Michal Vasko871a0252020-11-11 18:35:24 +01001342 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001343}
1344
1345/**
1346 * @brief Update operations on a diff node when the new operation is REPLACE.
1347 *
1348 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001349 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001350 * @param[in] src_diff Current source diff node.
1351 * @return LY_ERR value.
1352 */
1353static LY_ERR
1354lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1355{
1356 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001357 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001358 struct lyd_meta *meta;
1359 const struct lys_module *mod;
1360 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001361 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001362
1363 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001364 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001365 assert(mod);
1366
1367 switch (cur_op) {
1368 case LYD_DIFF_OP_REPLACE:
1369 case LYD_DIFF_OP_CREATE:
1370 switch (diff_match->schema->nodetype) {
1371 case LYS_LIST:
1372 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001373 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001374 * keep orig_key/orig_value (only replace oper) and replace key/value */
1375 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001376 if (lysc_is_dup_inst_list(diff_match->schema)) {
1377 meta_name = "position";
1378 } else if (diff_match->schema->nodetype == LYS_LIST) {
1379 meta_name = "key";
1380 } else {
1381 meta_name = "value";
1382 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001383
1384 lyd_diff_del_meta(diff_match, meta_name);
1385 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001386 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001387 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001388 break;
1389 case LYS_LEAF:
1390 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001391 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001392 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1393 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001394 }
1395
Michal Vaskoe6323f62020-07-09 15:49:02 +02001396 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001397 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001398 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001399 }
1400
Michal Vasko8caadab2020-11-05 17:38:15 +01001401 if (cur_op == LYD_DIFF_OP_REPLACE) {
1402 /* compare values whether there is any change at all */
1403 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001404 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001405 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001406 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1407 if (!ret) {
1408 /* values are the same, remove orig-value meta and set oper to NONE */
1409 lyd_free_meta_single(meta);
1410 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1411 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001412 }
1413
1414 /* modify the default flag */
1415 diff_match->flags &= ~LYD_DEFAULT;
1416 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1417 break;
1418 case LYS_ANYXML:
1419 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001420 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001421 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1422 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001423 }
1424
1425 /* modify the node value */
1426 any = (struct lyd_node_any *)src_diff;
1427 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1428 break;
1429 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001430 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001431 }
1432 break;
1433 case LYD_DIFF_OP_NONE:
1434 /* it is moved now */
1435 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1436
1437 /* change the operation */
1438 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1439
Michal Vaskoe78faec2021-04-08 17:24:43 +02001440 /* set orig-meta and meta */
1441 if (lysc_is_dup_inst_list(diff_match->schema)) {
1442 meta_name = "position";
1443 orig_meta_name = "orig-position";
1444 } else {
1445 meta_name = "key";
1446 orig_meta_name = "orig-key";
1447 }
1448
1449 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001450 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001451 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001452
Michal Vaskoe78faec2021-04-08 17:24:43 +02001453 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001454 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001455 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001456 break;
1457 default:
1458 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001459 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1460 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001461 }
1462
1463 return LY_SUCCESS;
1464}
1465
1466/**
1467 * @brief Update operations in a diff node when the new operation is CREATE.
1468 *
1469 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001470 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001471 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001472 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001473 * @return LY_ERR value.
1474 */
1475static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001476lyd_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 +02001477{
1478 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001479 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001480 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001481 const char *meta_name, *orig_meta_name;
1482 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001483 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001484
1485 switch (cur_op) {
1486 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001487 /* remember current flags */
1488 trg_flags = diff_match->flags;
1489
Michal Vaskoe78faec2021-04-08 17:24:43 +02001490 if (lysc_is_userordered(diff_match->schema)) {
1491 /* get anchor metadata */
1492 if (lysc_is_dup_inst_list(diff_match->schema)) {
1493 meta_name = "yang:position";
1494 orig_meta_name = "yang:orig-position";
1495 } else if (diff_match->schema->nodetype == LYS_LIST) {
1496 meta_name = "yang:key";
1497 orig_meta_name = "yang:orig-key";
1498 } else {
1499 meta_name = "yang:value";
1500 orig_meta_name = "yang:orig-value";
1501 }
1502 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001503 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001504 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001505 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001506
1507 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1508 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001509 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001510 /* deleted + created at another position -> operation REPLACE */
1511 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1512
1513 /* add anchor metadata */
1514 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1515 } else {
1516 /* deleted + created at the same position -> operation NONE */
1517 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1518
1519 /* delete anchor metadata */
1520 lyd_free_meta_single(orig_meta);
1521 }
1522 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1523 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1524 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1525 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1526 }
1527
Radek Krejci55c4bd22021-04-26 08:09:04 +02001528 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1529 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001530 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1531 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1532 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1533 /* deleted + created -> operation NONE */
1534 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1535 } else {
1536 /* we deleted it, but it was created with a different value -> operation REPLACE */
1537 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1538
1539 /* current value is the previous one (meta) */
1540 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001541 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001542
1543 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001544 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001545 }
1546 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001547 /* deleted + created -> operation NONE */
1548 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001549 }
1550
1551 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001552 /* add orig-dflt metadata */
1553 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1554 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1555
Michal Vaskoe6323f62020-07-09 15:49:02 +02001556 /* update dflt flag itself */
1557 diff_match->flags &= ~LYD_DEFAULT;
1558 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001559 }
1560
1561 /* but the operation of its children should remain DELETE */
1562 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1563 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001564 }
1565 break;
1566 default:
1567 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001568 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1569 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001570 }
1571
1572 return LY_SUCCESS;
1573}
1574
1575/**
1576 * @brief Update operations on a diff node when the new operation is DELETE.
1577 *
1578 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001579 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001580 * @param[in] src_diff Current source diff node.
1581 * @return LY_ERR value.
1582 */
1583static LY_ERR
1584lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1585{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001586 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001587 struct lyd_meta *meta;
1588 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001589 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001590
1591 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001592 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 +02001593
1594 switch (cur_op) {
1595 case LYD_DIFF_OP_CREATE:
1596 /* it was created, but then deleted -> set NONE operation */
1597 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1598
1599 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1600 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001601 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1602 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001603 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001604 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001605 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001606 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1607 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001608 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001609 break;
1610 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001611 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001612 if (lysc_is_userordered(diff_match->schema)) {
1613 if (lysc_is_dup_inst_list(diff_match->schema)) {
1614 meta_name = "position";
1615 } else if (diff_match->schema->nodetype == LYS_LIST) {
1616 meta_name = "key";
1617 } else {
1618 meta_name = "value";
1619 }
1620 } else {
1621 assert(diff_match->schema->nodetype == LYS_LEAF);
1622
1623 /* switch value for the original one */
1624 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001625 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001626 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001627 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1628 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001629 }
1630
1631 /* switch default for the original one, then remove the meta */
1632 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001633 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001634 diff_match->flags &= ~LYD_DEFAULT;
1635 if (meta->value.boolean) {
1636 diff_match->flags |= LYD_DEFAULT;
1637 }
1638 lyd_free_meta_single(meta);
1639
1640 meta_name = "orig-value";
1641 }
1642 lyd_diff_del_meta(diff_match, meta_name);
1643
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001644 /* it was being changed, but should be deleted instead -> set DELETE operation */
1645 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1646 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001647 case LYD_DIFF_OP_NONE:
1648 /* it was not modified, but should be deleted -> set DELETE operation */
1649 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001650 break;
1651 default:
1652 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001653 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1654 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001655 }
1656
1657 return LY_SUCCESS;
1658}
1659
1660/**
1661 * @brief Check whether this diff node is redundant (does not change data).
1662 *
1663 * @param[in] diff Diff node.
1664 * @return 0 if not, non-zero if it is.
1665 */
1666static int
1667lyd_diff_is_redundant(struct lyd_node *diff)
1668{
1669 enum lyd_diff_op op;
1670 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1671 struct lyd_node *child;
1672 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001673 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001674
1675 assert(diff);
1676
Michal Vaskoe78faec2021-04-08 17:24:43 +02001677 if (lysc_is_dup_inst_list(diff->schema)) {
1678 /* all descendants are keys */
1679 child = NULL;
1680 } else {
1681 child = lyd_child_no_keys(diff);
1682 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001683 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001684 assert(mod);
1685
1686 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001687 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001688
1689 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001690 /* get metadata names */
1691 if (lysc_is_dup_inst_list(diff->schema)) {
1692 meta_name = "position";
1693 orig_meta_name = "orig-position";
1694 } else if (diff->schema->nodetype == LYS_LIST) {
1695 meta_name = "key";
1696 orig_meta_name = "orig-key";
1697 } else {
1698 meta_name = "value";
1699 orig_meta_name = "orig-value";
1700 }
1701
Michal Vaskoe6323f62020-07-09 15:49:02 +02001702 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001703 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1704 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001705 assert(orig_val_meta && val_meta);
1706
1707 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1708 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001709 lyd_free_meta_single(orig_val_meta);
1710 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001711 if (child) {
1712 /* change operation to NONE, we have siblings */
1713 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1714 return 0;
1715 }
1716
1717 /* redundant node, BUT !!
1718 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1719 * because the data that this is applied on should not change for the diff lifetime.
1720 * However, when we are merging 2 diffs, this conversion is actually lossy because
1721 * if the data change, the move operation can also change its meaning. In this specific
1722 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1723 */
1724 return 1;
1725 }
1726 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1727 /* check whether at least the default flags are different */
1728 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1729 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001730 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001731
1732 /* if previous and current dflt flags are the same, this node is redundant */
1733 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1734 return 1;
1735 }
1736 return 0;
1737 }
1738
1739 if (!child && (op == LYD_DIFF_OP_NONE)) {
1740 return 1;
1741 }
1742
1743 return 0;
1744}
1745
1746/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001747 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001748 *
1749 * @param[in] src_diff Source diff node.
1750 * @param[in] diff_parent Current sysrepo diff parent.
1751 * @param[in] diff_cb Optional diff callback.
1752 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001753 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001754 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001755 * @param[in,out] diff Diff root node.
1756 * @return LY_ERR value.
1757 */
1758static LY_ERR
1759lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vasko271d2e32023-01-31 15:43:19 +01001760 struct hash_table **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001761{
1762 LY_ERR ret = LY_SUCCESS;
1763 struct lyd_node *child, *diff_node = NULL;
1764 enum lyd_diff_op src_op, cur_op;
Michal Vasko271d2e32023-01-31 15:43:19 +01001765 struct hash_table *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001766
1767 /* get source node operation */
1768 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1769
1770 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001771 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 +02001772
1773 if (diff_node) {
1774 /* get target (current) operation */
1775 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1776
1777 /* merge operations */
1778 switch (src_op) {
1779 case LYD_DIFF_OP_REPLACE:
1780 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1781 break;
1782 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001783 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001784 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001785 goto add_diff;
1786 }
1787
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001788 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001789 break;
1790 case LYD_DIFF_OP_DELETE:
1791 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1792 break;
1793 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001794 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1795 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001796 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1797 break;
1798 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001799 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001800 }
1801 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001802 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001803 return ret;
1804 }
1805
1806 if (diff_cb) {
1807 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001808 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001809 }
1810
1811 /* update diff parent */
1812 diff_parent = diff_node;
1813
Michal Vaskoe78faec2021-04-08 17:24:43 +02001814 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1815 * so there is nothing to merge for these "keys" */
1816 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1817 /* merge src_diff recursively */
1818 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1819 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1820 if (ret) {
1821 break;
1822 }
1823 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001824 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001825 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001826 }
1827 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001828add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001829 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001830 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1831 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
1832 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1833 } else {
1834 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
1835 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1836 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001837
1838 /* insert node into diff if not already */
1839 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001840 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001841 }
1842
1843 /* update operation */
1844 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1845
1846 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001847 /* call callback with no source diff node since it was duplicated and just added */
1848 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001849 }
1850
1851 /* update diff parent */
1852 diff_parent = diff_node;
1853 }
1854
1855 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001856 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001857 if (diff_parent == *diff) {
1858 *diff = (*diff)->next;
1859 }
1860 lyd_free_tree(diff_parent);
1861 }
1862
1863 return LY_SUCCESS;
1864}
1865
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001866LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001867lyd_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 +01001868 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001869{
1870 const struct lyd_node *src_root;
Michal Vasko271d2e32023-01-31 15:43:19 +01001871 struct hash_table *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001872 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001873
1874 LY_LIST_FOR(src_diff, src_root) {
1875 if (mod && (lyd_owner_module(src_root) != mod)) {
1876 /* skip data nodes from different modules */
1877 continue;
1878 }
1879
1880 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001881 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 +02001882 }
1883
Michal Vaskoe78faec2021-04-08 17:24:43 +02001884cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001885 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001886 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001887}
1888
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001889LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001890lyd_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 +01001891 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001892{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001893 LY_ERR ret;
Michal Vasko271d2e32023-01-31 15:43:19 +01001894 struct hash_table *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001895
Michal Vasko04f85912020-08-07 12:14:58 +02001896 if (!src_sibling) {
1897 return LY_SUCCESS;
1898 }
1899
Michal Vaskoe78faec2021-04-08 17:24:43 +02001900 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 +02001901 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001902 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001903}
1904
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001905LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001906lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001907{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001908 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001909}
Michal Vasko4231fb62020-07-13 13:54:47 +02001910
1911static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001912lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001913{
1914 LY_ERR ret = LY_SUCCESS;
1915 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001916 const char *val1 = NULL;
1917 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001918 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001919
Michal Vaskobaba84e2021-02-05 16:33:30 +01001920 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1921
1922 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001923 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001924
1925 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001926 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001927
1928 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001929 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001930 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001931 } else {
1932 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1933 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001934
1935 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001936 flags = node->flags;
1937 if (node->schema->nodetype == LYS_LEAF) {
1938 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1939 } else {
1940 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02001941
Michal Vaskobaba84e2021-02-05 16:33:30 +01001942 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1943 }
1944 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001945 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1946
1947cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001948 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001949 return ret;
1950}
1951
1952static LY_ERR
1953lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1954{
1955 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001956 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001957
1958 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001959 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001960
1961 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001962 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001963 flag1 = LYD_DEFAULT;
1964 } else {
1965 flag1 = 0;
1966 }
1967
1968 /* current default */
1969 flag2 = node->flags & LYD_DEFAULT;
1970
Michal Vasko610e93b2020-11-09 20:58:32 +01001971 if (flag1 == flag2) {
1972 /* no default state change so nothing to reverse */
1973 return LY_SUCCESS;
1974 }
1975
Michal Vasko4231fb62020-07-13 13:54:47 +02001976 /* switch defaults */
1977 node->flags &= ~LYD_DEFAULT;
1978 node->flags |= flag1;
1979 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1980
1981 return LY_SUCCESS;
1982}
1983
1984static LY_ERR
1985lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1986{
1987 LY_ERR ret = LY_SUCCESS;
1988 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001989 const char *val1 = NULL;
1990 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001991
1992 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001993 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001994
1995 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001996 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001997
1998 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001999 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02002000
2001 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002002 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02002003
2004 /* switch values */
2005 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2006 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2007
2008cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002009 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002010 return ret;
2011}
2012
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002013/**
2014 * @brief Remove specific operation from all the nodes in a subtree.
2015 *
2016 * @param[in] diff Diff subtree to process.
2017 * @param[in] op Only expected operation.
2018 * @return LY_ERR value.
2019 */
2020static LY_ERR
2021lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2022{
2023 struct lyd_node *elem;
2024 struct lyd_meta *meta;
2025
2026 LYD_TREE_DFS_BEGIN(diff, elem) {
2027 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2028 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002029 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 +01002030 lyd_free_meta_single(meta);
2031 }
2032
2033 LYD_TREE_DFS_END(diff, elem);
2034 }
2035
2036 return LY_SUCCESS;
2037}
2038
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002039LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002040lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002041{
2042 LY_ERR ret = LY_SUCCESS;
2043 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002044 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002045 enum lyd_diff_op op;
2046
2047 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2048
2049 if (!src_diff) {
2050 *diff = NULL;
2051 return LY_SUCCESS;
2052 }
2053
2054 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002055 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002056
2057 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002058 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2059 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002060
2061 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002062 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002063 /* skip all keys */
2064 if (!lysc_is_key(elem->schema)) {
2065 /* find operation attribute, if any */
2066 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002067
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002068 switch (op) {
2069 case LYD_DIFF_OP_CREATE:
2070 /* reverse create to delete */
2071 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002072
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002073 /* check all the children for the same operation, nothing else is expected */
2074 LY_LIST_FOR(lyd_child(elem), iter) {
2075 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2076 }
2077
Michal Vasko9e070522021-03-05 14:00:14 +01002078 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002079 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002080 case LYD_DIFF_OP_DELETE:
2081 /* reverse delete to create */
2082 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002083
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002084 /* check all the children for the same operation, nothing else is expected */
2085 LY_LIST_FOR(lyd_child(elem), iter) {
2086 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2087 }
2088
Michal Vasko9e070522021-03-05 14:00:14 +01002089 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002090 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002091 case LYD_DIFF_OP_REPLACE:
2092 switch (elem->schema->nodetype) {
2093 case LYS_LEAF:
2094 /* leaf value change */
2095 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2096 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2097 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002098 case LYS_ANYXML:
2099 case LYS_ANYDATA:
2100 /* any value change */
2101 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2102 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002103 case LYS_LEAFLIST:
2104 /* leaf-list move */
2105 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002106 if (lysc_is_dup_inst_list(elem->schema)) {
2107 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2108 } else {
2109 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2110 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002111 break;
2112 case LYS_LIST:
2113 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002114 if (lysc_is_dup_inst_list(elem->schema)) {
2115 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2116 } else {
2117 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2118 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002119 break;
2120 default:
2121 LOGINT(LYD_CTX(src_diff));
2122 ret = LY_EINT;
2123 goto cleanup;
2124 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002125 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002126 case LYD_DIFF_OP_NONE:
2127 switch (elem->schema->nodetype) {
2128 case LYS_LEAF:
2129 case LYS_LEAFLIST:
2130 /* default flag change */
2131 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2132 break;
2133 default:
2134 /* nothing to do */
2135 break;
2136 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002137 break;
2138 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002139 }
2140
Michal Vasko56daf732020-08-10 10:57:18 +02002141 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002142 }
2143 }
2144
2145cleanup:
2146 if (ret) {
2147 lyd_free_siblings(*diff);
2148 *diff = NULL;
2149 }
2150 return ret;
2151}