blob: 4ef91154ae61841be7ec9f4aca2bc351325d78f6 [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.
651 * @param[in,out] dup_inst_cache Duplicate instance cache.
652 * @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 Vaskod7c048c2021-05-18 16:12:55 +0200657 struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200658{
Michal Vaskoe78faec2021-04-08 17:24:43 +0200659 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
660 /* try to find the exact instance */
661 lyd_find_sibling_first(siblings, target, match);
662 } else {
663 /* try to simply find the node, there cannot be more instances */
664 lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
665 }
666
Michal Vaskod7c048c2021-05-18 16:12:55 +0200667 /* update match as needed */
668 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200669
670 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
671 /* ignore default nodes */
672 *match = NULL;
673 }
674 return LY_SUCCESS;
675}
676
677/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200678 * @brief Perform diff for all siblings at certain depth, recursively.
679 *
680 * For user-ordered lists/leaf-lists a specific structure is used for storing
681 * the current order. The idea is to apply all the generated diff changes
682 * virtually on the first tree so that we can continue to generate correct
683 * changes after some were already generated.
684 *
685 * The algorithm then uses second tree position-based changes with a before
686 * (preceding) item anchor.
687 *
688 * Example:
689 *
690 * Virtual first tree leaf-list order:
691 * 1 2 [3] 4 5
692 *
693 * Second tree leaf-list order:
694 * 1 2 [5] 3 4
695 *
696 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
697 * match - they do not - move nodes so that the 3rd position node is final ->
698 * -> move node 5 to the 3rd position -> move node 5 after node 2.
699 *
700 * Required properties:
701 * Stored operations (move) should not be affected by later operations -
702 * - would cause a redundantly long list of operations, possibly inifinite.
703 *
704 * Implemenation justification:
705 * First, all delete operations and only then move/create operations are stored.
706 * Also, preceding anchor is used and after each iteration another node is
707 * at its final position. That results in the invariant that all preceding
708 * nodes are final and will not be changed by the later operations, meaning
709 * they can safely be used as anchors for the later operations.
710 *
711 * @param[in] first First tree first sibling.
712 * @param[in] second Second tree first sibling.
713 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200714 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200715 * @param[in,out] diff Diff to append to.
716 * @return LY_ERR value.
717 */
718static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200719lyd_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 +0200720 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200721{
722 LY_ERR ret = LY_SUCCESS;
723 const struct lyd_node *iter_first, *iter_second;
724 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100725 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200726 struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200727 LY_ARRAY_COUNT_TYPE u;
728 enum lyd_diff_op op;
729 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200730 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200731
Michal Vaskod59035b2020-07-08 12:00:06 +0200732 /* compare first tree to the second tree - delete, replace, none */
733 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200734 if (!iter_first->schema) {
735 continue;
736 }
737
Michal Vaskod59035b2020-07-08 12:00:06 +0200738 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200739 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200740 /* skip default nodes */
741 continue;
742 }
743
Michal Vaskoe78faec2021-04-08 17:24:43 +0200744 /* find a match in the second tree */
745 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
746 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200747
748 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100749 /* get (create) userord entry */
750 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
751 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
752
Michal Vaskoe78faec2021-04-08 17:24:43 +0200753 /* we are handling only user-ordered node delete now */
754 if (!match_second) {
755 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100756 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
757 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200758
759 /* there must be changes, it is deleted */
760 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100761 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
762 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200763
764 free(orig_value);
765 free(key);
766 free(value);
767 free(position);
768 free(orig_key);
769 free(orig_position);
770 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200771 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200772 } else {
773 /* get all the attributes */
774 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
775
776 /* add into diff if there are any changes */
777 if (!ret) {
778 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200779 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200780 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100781 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200782 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200783 }
784
785 free(orig_value);
786 LY_CHECK_GOTO(ret, cleanup);
787 } else if (ret == LY_ENOT) {
788 ret = LY_SUCCESS;
789 } else {
790 goto cleanup;
791 }
792 }
793
794 /* check descendants, if any, recursively */
795 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200796 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
797 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200798 }
799
800 if (nosiblings) {
801 break;
802 }
803 }
804
805 /* reset all cached positions */
806 LY_ARRAY_FOR(userord, u) {
807 userord[u].pos = 0;
808 }
809
810 /* compare second tree to the first tree - create, user-ordered move */
811 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200812 if (!iter_second->schema) {
813 continue;
814 }
815
Michal Vaskod59035b2020-07-08 12:00:06 +0200816 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200817 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200818 /* skip default nodes */
819 continue;
820 }
821
Michal Vaskoe78faec2021-04-08 17:24:43 +0200822 /* find a match in the first tree */
823 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
824 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200825
826 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100827 /* get userord entry */
828 userord_item = lyd_diff_userord_get(NULL, iter_second->schema, &userord);
829 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
830
Michal Vaskod59035b2020-07-08 12:00:06 +0200831 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100832 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200833 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200834
835 /* add into diff if there are any changes */
836 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200837 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
838 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200839
840 free(orig_value);
841 free(key);
842 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200843 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200844 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200845 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200846 LY_CHECK_GOTO(ret, cleanup);
847 } else if (ret == LY_ENOT) {
848 ret = LY_SUCCESS;
849 } else {
850 goto cleanup;
851 }
852 } else if (!match_first) {
853 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200854 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 +0200855
856 /* there must be changes, it is created */
857 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200858 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200859
860 free(orig_value);
861 LY_CHECK_GOTO(ret, cleanup);
862 } /* else was handled */
863
864 if (nosiblings) {
865 break;
866 }
867 }
868
869cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200870 lyd_dup_inst_free(dup_inst_first);
871 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200872 LY_ARRAY_FOR(userord, u) {
873 LY_ARRAY_FREE(userord[u].inst);
874 }
875 LY_ARRAY_FREE(userord);
876 return ret;
877}
878
Michal Vasko3a41dff2020-07-15 14:30:28 +0200879static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100880lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
881 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200882{
883 const struct ly_ctx *ctx;
884
885 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
886
887 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200888 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200889 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200890 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200891 } else {
892 ctx = NULL;
893 }
894
895 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
896 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
897 return LY_EINVAL;
898 }
899
900 *diff = NULL;
901
Michal Vasko3a41dff2020-07-15 14:30:28 +0200902 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
903}
904
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100905LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200906lyd_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 +0200907{
908 return lyd_diff(first, second, options, 1, diff);
909}
910
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100911LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200912lyd_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 +0200913{
914 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200915}
916
917/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200918 * @brief Learn operation of a diff node.
919 *
920 * @param[in] diff_node Diff node.
921 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200922 * @return LY_ERR value.
923 */
924static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200925lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200926{
927 struct lyd_meta *meta = NULL;
928 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200929 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100930 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +0200931
Michal Vasko9e685082021-01-29 14:49:09 +0100932 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200933 LY_LIST_FOR(diff_parent->meta, meta) {
934 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200935 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200936 if ((str[0] == 'r') && (diff_parent != diff_node)) {
937 /* we do not care about this operation if it's in our parent */
938 continue;
939 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200940 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200941 break;
942 }
943 }
944 if (meta) {
945 break;
946 }
947 }
Michal Vasko52afd7d2022-01-18 14:08:34 +0100948
949 if (!meta) {
950 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
951 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
952 free(path);
953 return LY_EINT;
954 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200955
Michal Vaskod59035b2020-07-08 12:00:06 +0200956 return LY_SUCCESS;
957}
958
959/**
960 * @brief Insert a diff node into a data tree.
961 *
962 * @param[in,out] first_node First sibling of the data tree.
963 * @param[in] parent_node Data tree sibling parent node.
964 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200965 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
966 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200967 * @return err_info, NULL on success.
968 */
969static LY_ERR
970lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200971 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200972{
973 LY_ERR ret;
974 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200975 uint32_t pos, anchor_pos;
976 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200977
978 assert(new_node);
979
980 if (!*first_node) {
981 if (!parent_node) {
982 /* no parent or siblings */
983 *first_node = new_node;
984 return LY_SUCCESS;
985 }
986
987 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200988 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200989 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200990 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200991 return LY_EINVAL;
992 }
Michal Vaskob104f112020-07-17 09:54:54 +0200993 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200994 }
995
Michal Vasko9e685082021-01-29 14:49:09 +0100996 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200997
Michal Vaskod59035b2020-07-08 12:00:06 +0200998 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200999 /* simple insert */
1000 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001001 }
1002
Michal Vaskoe78faec2021-04-08 17:24:43 +02001003 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001004 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001005 if (lysc_is_dup_inst_list(new_node->schema)) {
1006 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001007 if (!anchor_pos) {
1008 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1009 return LY_EINVAL;
1010 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001011
1012 found = 0;
1013 pos = 1;
1014 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1015 if (pos == anchor_pos) {
1016 found = 1;
1017 break;
1018 }
1019 ++pos;
1020 }
1021 if (!found) {
1022 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1023 new_node->schema->name);
1024 return LY_EINVAL;
1025 }
1026 } else {
1027 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1028 if (ret == LY_ENOTFOUND) {
1029 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1030 new_node->schema->name);
1031 return LY_EINVAL;
1032 } else if (ret) {
1033 return ret;
1034 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001035 }
1036
1037 /* insert after */
1038 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1039 assert(new_node->prev == anchor);
1040 if (*first_node == new_node) {
1041 *first_node = anchor;
1042 }
1043 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001044 /* find the first instance */
1045 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1046 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001047
Michal Vaskoea7d3232022-04-19 12:01:36 +02001048 if (anchor) {
1049 /* insert before the first instance */
1050 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1051 if ((*first_node)->prev->next) {
1052 assert(!new_node->prev->next);
1053 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001054 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001055 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001056 /* insert anywhere */
1057 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001058 }
1059 }
1060
1061 return LY_SUCCESS;
1062}
1063
1064/**
1065 * @brief Apply diff subtree on data tree nodes, recursively.
1066 *
1067 * @param[in,out] first_node First sibling of the data tree.
1068 * @param[in] parent_node Parent of the first sibling.
1069 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001070 * @param[in] diff_cb Optional diff callback.
1071 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001072 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001073 * @return LY_ERR value.
1074 */
1075static LY_ERR
1076lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Michal Vaskod7c048c2021-05-18 16:12:55 +02001077 lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001078{
1079 LY_ERR ret;
1080 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001081 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001082 enum lyd_diff_op op;
1083 struct lyd_meta *meta;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001084 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001085 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001086
1087 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001088 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001089
Michal Vaskoe6323f62020-07-09 15:49:02 +02001090 /* handle specific user-ordered (leaf-)lists operations separately */
1091 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1092 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001093 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001094 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001095 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001096 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001097 /* duplicate the node */
1098 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001099 }
1100
Michal Vaskoe78faec2021-04-08 17:24:43 +02001101 /* get "key", "value", or "position" metadata string value */
1102 if (lysc_is_dup_inst_list(diff_node->schema)) {
1103 meta_str = "yang:position";
1104 } else if (diff_node->schema->nodetype == LYS_LIST) {
1105 meta_str = "yang:key";
1106 } else {
1107 meta_str = "yang:value";
1108 }
1109 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001110 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001111 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001112
Michal Vaskod59035b2020-07-08 12:00:06 +02001113 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001114 if (str_val[0]) {
1115 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001116 } else {
1117 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1118 }
1119 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001120 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001121 lyd_free_tree(match);
1122 }
1123 return ret;
1124 }
1125
1126 goto next_iter_r;
1127 }
1128
1129 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001130 switch (op) {
1131 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001132 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001133 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001134 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001135
1136 if (match->schema->nodetype & LYD_NODE_TERM) {
1137 /* special case of only dflt flag change */
1138 if (diff_node->flags & LYD_DEFAULT) {
1139 match->flags |= LYD_DEFAULT;
1140 } else {
1141 match->flags &= ~LYD_DEFAULT;
1142 }
1143 } else {
1144 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001145 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001146 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1147 LYD_NAME(diff_node));
1148 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001149 }
1150 }
1151 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001152 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001153 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001154 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001155
1156 /* insert it at the end */
1157 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001158 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001159 if (match->flags & LYD_EXT) {
Michal Vasko193dacd2022-10-13 08:43:05 +02001160 ret = lyplg_ext_insert(parent_node, match);
Michal Vasko19175b62022-04-01 09:17:07 +02001161 } else {
1162 ret = lyd_insert_child(parent_node, match);
1163 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001164 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001165 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001166 }
1167 if (ret) {
1168 lyd_free_tree(match);
1169 return ret;
1170 }
1171
1172 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001173 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001174 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001175 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001176 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001177
1178 /* remove it */
1179 if ((match == *first_node) && !match->parent) {
1180 assert(!parent_node);
1181 /* we have removed the top-level node */
1182 *first_node = (*first_node)->next;
1183 }
1184 lyd_free_tree(match);
1185
1186 /* we are not going recursively in this case, the whole subtree was already deleted */
1187 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001188 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001189 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1190 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1191 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1192 return LY_EINVAL;
1193 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001194
1195 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001196 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001197 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001198
Michal Vaskobaba84e2021-02-05 16:33:30 +01001199 /* update the value */
1200 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001201 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001202 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001203 } else {
1204 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001205
Michal Vaskoe78faec2021-04-08 17:24:43 +02001206 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001207 }
1208
1209 /* with flags */
1210 match->flags = diff_node->flags;
1211 break;
1212 default:
1213 LOGINT_RET(ctx);
1214 }
1215
1216next_iter_r:
1217 if (diff_cb) {
1218 /* call callback */
1219 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1220 }
1221
1222 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001223 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001224 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001225 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1226 if (ret) {
1227 break;
1228 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001229 }
1230
Michal Vaskod7c048c2021-05-18 16:12:55 +02001231 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001232 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001233}
1234
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001235LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001236lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001237 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001238{
1239 const struct lyd_node *root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001240 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001241 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001242
1243 LY_LIST_FOR(diff, root) {
1244 if (mod && (lyd_owner_module(root) != mod)) {
1245 /* skip data nodes from different modules */
1246 continue;
1247 }
1248
1249 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001250 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1251 if (ret) {
1252 break;
1253 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001254 }
1255
Michal Vaskod7c048c2021-05-18 16:12:55 +02001256 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001257 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001258}
1259
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001260LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001261lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001262{
1263 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1264}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001265
1266/**
1267 * @brief Update operations on a diff node when the new operation is NONE.
1268 *
1269 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001270 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001271 * @param[in] src_diff Current source diff node.
1272 * @return LY_ERR value.
1273 */
1274static LY_ERR
1275lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1276{
1277 switch (cur_op) {
1278 case LYD_DIFF_OP_NONE:
1279 case LYD_DIFF_OP_CREATE:
1280 case LYD_DIFF_OP_REPLACE:
1281 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1282 /* NONE on a term means only its dflt flag was changed */
1283 diff_match->flags &= ~LYD_DEFAULT;
1284 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1285 }
1286 break;
1287 default:
1288 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001289 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1290 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001291 }
1292
1293 return LY_SUCCESS;
1294}
1295
1296/**
1297 * @brief Remove an attribute from a node.
1298 *
1299 * @param[in] node Node with the metadata.
1300 * @param[in] name Metadata name.
1301 */
1302static void
1303lyd_diff_del_meta(struct lyd_node *node, const char *name)
1304{
1305 struct lyd_meta *meta;
1306
1307 LY_LIST_FOR(node->meta, meta) {
1308 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001309 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001310 return;
1311 }
1312 }
1313
1314 assert(0);
1315}
1316
1317/**
1318 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001319 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001320 *
1321 * @param[in] node Node to change.
1322 * @param[in] op Operation to set.
1323 * @return LY_ERR value.
1324 */
1325static LY_ERR
1326lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1327{
1328 struct lyd_meta *meta;
1329
1330 LY_LIST_FOR(node->meta, meta) {
1331 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001332 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001333 break;
1334 }
1335 }
1336
Michal Vasko871a0252020-11-11 18:35:24 +01001337 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001338}
1339
1340/**
1341 * @brief Update operations on a diff node when the new operation is REPLACE.
1342 *
1343 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001344 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001345 * @param[in] src_diff Current source diff node.
1346 * @return LY_ERR value.
1347 */
1348static LY_ERR
1349lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1350{
1351 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001352 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001353 struct lyd_meta *meta;
1354 const struct lys_module *mod;
1355 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001356 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001357
1358 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001359 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001360 assert(mod);
1361
1362 switch (cur_op) {
1363 case LYD_DIFF_OP_REPLACE:
1364 case LYD_DIFF_OP_CREATE:
1365 switch (diff_match->schema->nodetype) {
1366 case LYS_LIST:
1367 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001368 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001369 * keep orig_key/orig_value (only replace oper) and replace key/value */
1370 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001371 if (lysc_is_dup_inst_list(diff_match->schema)) {
1372 meta_name = "position";
1373 } else if (diff_match->schema->nodetype == LYS_LIST) {
1374 meta_name = "key";
1375 } else {
1376 meta_name = "value";
1377 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001378
1379 lyd_diff_del_meta(diff_match, meta_name);
1380 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001381 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001382 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001383 break;
1384 case LYS_LEAF:
1385 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001386 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001387 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1388 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001389 }
1390
Michal Vaskoe6323f62020-07-09 15:49:02 +02001391 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001392 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001393 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001394 }
1395
Michal Vasko8caadab2020-11-05 17:38:15 +01001396 if (cur_op == LYD_DIFF_OP_REPLACE) {
1397 /* compare values whether there is any change at all */
1398 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001399 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001400 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001401 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1402 if (!ret) {
1403 /* values are the same, remove orig-value meta and set oper to NONE */
1404 lyd_free_meta_single(meta);
1405 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1406 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001407 }
1408
1409 /* modify the default flag */
1410 diff_match->flags &= ~LYD_DEFAULT;
1411 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1412 break;
1413 case LYS_ANYXML:
1414 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001415 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001416 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1417 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001418 }
1419
1420 /* modify the node value */
1421 any = (struct lyd_node_any *)src_diff;
1422 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1423 break;
1424 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001425 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001426 }
1427 break;
1428 case LYD_DIFF_OP_NONE:
1429 /* it is moved now */
1430 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1431
1432 /* change the operation */
1433 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1434
Michal Vaskoe78faec2021-04-08 17:24:43 +02001435 /* set orig-meta and meta */
1436 if (lysc_is_dup_inst_list(diff_match->schema)) {
1437 meta_name = "position";
1438 orig_meta_name = "orig-position";
1439 } else {
1440 meta_name = "key";
1441 orig_meta_name = "orig-key";
1442 }
1443
1444 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001445 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001446 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001447
Michal Vaskoe78faec2021-04-08 17:24:43 +02001448 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001449 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001450 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001451 break;
1452 default:
1453 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001454 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1455 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001456 }
1457
1458 return LY_SUCCESS;
1459}
1460
1461/**
1462 * @brief Update operations in a diff node when the new operation is CREATE.
1463 *
1464 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001465 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001466 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001467 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001468 * @return LY_ERR value.
1469 */
1470static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001471lyd_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 +02001472{
1473 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001474 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001475 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001476 const char *meta_name, *orig_meta_name;
1477 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001478 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001479
1480 switch (cur_op) {
1481 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001482 /* remember current flags */
1483 trg_flags = diff_match->flags;
1484
Michal Vaskoe78faec2021-04-08 17:24:43 +02001485 if (lysc_is_userordered(diff_match->schema)) {
1486 /* get anchor metadata */
1487 if (lysc_is_dup_inst_list(diff_match->schema)) {
1488 meta_name = "yang:position";
1489 orig_meta_name = "yang:orig-position";
1490 } else if (diff_match->schema->nodetype == LYS_LIST) {
1491 meta_name = "yang:key";
1492 orig_meta_name = "yang:orig-key";
1493 } else {
1494 meta_name = "yang:value";
1495 orig_meta_name = "yang:orig-value";
1496 }
1497 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001498 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001499 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001500 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001501
1502 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1503 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001504 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001505 /* deleted + created at another position -> operation REPLACE */
1506 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1507
1508 /* add anchor metadata */
1509 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1510 } else {
1511 /* deleted + created at the same position -> operation NONE */
1512 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1513
1514 /* delete anchor metadata */
1515 lyd_free_meta_single(orig_meta);
1516 }
1517 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1518 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1519 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1520 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1521 }
1522
Radek Krejci55c4bd22021-04-26 08:09:04 +02001523 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1524 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001525 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1526 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1527 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1528 /* deleted + created -> operation NONE */
1529 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1530 } else {
1531 /* we deleted it, but it was created with a different value -> operation REPLACE */
1532 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1533
1534 /* current value is the previous one (meta) */
1535 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001536 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001537
1538 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001539 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001540 }
1541 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001542 /* deleted + created -> operation NONE */
1543 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001544 }
1545
1546 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001547 /* add orig-dflt metadata */
1548 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1549 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1550
Michal Vaskoe6323f62020-07-09 15:49:02 +02001551 /* update dflt flag itself */
1552 diff_match->flags &= ~LYD_DEFAULT;
1553 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001554 }
1555
1556 /* but the operation of its children should remain DELETE */
1557 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1558 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001559 }
1560 break;
1561 default:
1562 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001563 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1564 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001565 }
1566
1567 return LY_SUCCESS;
1568}
1569
1570/**
1571 * @brief Update operations on a diff node when the new operation is DELETE.
1572 *
1573 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001574 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001575 * @param[in] src_diff Current source diff node.
1576 * @return LY_ERR value.
1577 */
1578static LY_ERR
1579lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1580{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001581 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001582 struct lyd_meta *meta;
1583 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001584 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001585
1586 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001587 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 +02001588
1589 switch (cur_op) {
1590 case LYD_DIFF_OP_CREATE:
1591 /* it was created, but then deleted -> set NONE operation */
1592 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1593
1594 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1595 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001596 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1597 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001598 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001599 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001600 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001601 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1602 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001603 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001604 break;
1605 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001606 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001607 if (lysc_is_userordered(diff_match->schema)) {
1608 if (lysc_is_dup_inst_list(diff_match->schema)) {
1609 meta_name = "position";
1610 } else if (diff_match->schema->nodetype == LYS_LIST) {
1611 meta_name = "key";
1612 } else {
1613 meta_name = "value";
1614 }
1615 } else {
1616 assert(diff_match->schema->nodetype == LYS_LEAF);
1617
1618 /* switch value for the original one */
1619 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001620 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001621 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001622 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1623 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001624 }
1625
1626 /* switch default for the original one, then remove the meta */
1627 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001628 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001629 diff_match->flags &= ~LYD_DEFAULT;
1630 if (meta->value.boolean) {
1631 diff_match->flags |= LYD_DEFAULT;
1632 }
1633 lyd_free_meta_single(meta);
1634
1635 meta_name = "orig-value";
1636 }
1637 lyd_diff_del_meta(diff_match, meta_name);
1638
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001639 /* it was being changed, but should be deleted instead -> set DELETE operation */
1640 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1641 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001642 case LYD_DIFF_OP_NONE:
1643 /* it was not modified, but should be deleted -> set DELETE operation */
1644 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001645 break;
1646 default:
1647 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001648 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1649 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001650 }
1651
1652 return LY_SUCCESS;
1653}
1654
1655/**
1656 * @brief Check whether this diff node is redundant (does not change data).
1657 *
1658 * @param[in] diff Diff node.
1659 * @return 0 if not, non-zero if it is.
1660 */
1661static int
1662lyd_diff_is_redundant(struct lyd_node *diff)
1663{
1664 enum lyd_diff_op op;
1665 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1666 struct lyd_node *child;
1667 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001668 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001669
1670 assert(diff);
1671
Michal Vaskoe78faec2021-04-08 17:24:43 +02001672 if (lysc_is_dup_inst_list(diff->schema)) {
1673 /* all descendants are keys */
1674 child = NULL;
1675 } else {
1676 child = lyd_child_no_keys(diff);
1677 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001678 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001679 assert(mod);
1680
1681 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001682 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001683
1684 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001685 /* get metadata names */
1686 if (lysc_is_dup_inst_list(diff->schema)) {
1687 meta_name = "position";
1688 orig_meta_name = "orig-position";
1689 } else if (diff->schema->nodetype == LYS_LIST) {
1690 meta_name = "key";
1691 orig_meta_name = "orig-key";
1692 } else {
1693 meta_name = "value";
1694 orig_meta_name = "orig-value";
1695 }
1696
Michal Vaskoe6323f62020-07-09 15:49:02 +02001697 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001698 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1699 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001700 assert(orig_val_meta && val_meta);
1701
1702 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1703 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001704 lyd_free_meta_single(orig_val_meta);
1705 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001706 if (child) {
1707 /* change operation to NONE, we have siblings */
1708 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1709 return 0;
1710 }
1711
1712 /* redundant node, BUT !!
1713 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1714 * because the data that this is applied on should not change for the diff lifetime.
1715 * However, when we are merging 2 diffs, this conversion is actually lossy because
1716 * if the data change, the move operation can also change its meaning. In this specific
1717 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1718 */
1719 return 1;
1720 }
1721 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1722 /* check whether at least the default flags are different */
1723 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1724 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001725 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001726
1727 /* if previous and current dflt flags are the same, this node is redundant */
1728 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1729 return 1;
1730 }
1731 return 0;
1732 }
1733
1734 if (!child && (op == LYD_DIFF_OP_NONE)) {
1735 return 1;
1736 }
1737
1738 return 0;
1739}
1740
1741/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001742 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001743 *
1744 * @param[in] src_diff Source diff node.
1745 * @param[in] diff_parent Current sysrepo diff parent.
1746 * @param[in] diff_cb Optional diff callback.
1747 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001748 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001749 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001750 * @param[in,out] diff Diff root node.
1751 * @return LY_ERR value.
1752 */
1753static LY_ERR
1754lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vaskod7c048c2021-05-18 16:12:55 +02001755 struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001756{
1757 LY_ERR ret = LY_SUCCESS;
1758 struct lyd_node *child, *diff_node = NULL;
1759 enum lyd_diff_op src_op, cur_op;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001760 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001761
1762 /* get source node operation */
1763 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1764
1765 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001766 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 +02001767
1768 if (diff_node) {
1769 /* get target (current) operation */
1770 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1771
1772 /* merge operations */
1773 switch (src_op) {
1774 case LYD_DIFF_OP_REPLACE:
1775 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1776 break;
1777 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001778 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001779 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001780 goto add_diff;
1781 }
1782
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001783 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001784 break;
1785 case LYD_DIFF_OP_DELETE:
1786 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1787 break;
1788 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001789 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1790 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001791 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1792 break;
1793 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001794 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001795 }
1796 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001797 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001798 return ret;
1799 }
1800
1801 if (diff_cb) {
1802 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001803 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001804 }
1805
1806 /* update diff parent */
1807 diff_parent = diff_node;
1808
Michal Vaskoe78faec2021-04-08 17:24:43 +02001809 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1810 * so there is nothing to merge for these "keys" */
1811 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1812 /* merge src_diff recursively */
1813 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1814 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1815 if (ret) {
1816 break;
1817 }
1818 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001819 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001820 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001821 }
1822 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001823add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001824 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001825 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1826 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
1827 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1828 } else {
1829 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
1830 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1831 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001832
1833 /* insert node into diff if not already */
1834 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001835 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001836 }
1837
1838 /* update operation */
1839 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1840
1841 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001842 /* call callback with no source diff node since it was duplicated and just added */
1843 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001844 }
1845
1846 /* update diff parent */
1847 diff_parent = diff_node;
1848 }
1849
1850 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001851 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001852 if (diff_parent == *diff) {
1853 *diff = (*diff)->next;
1854 }
1855 lyd_free_tree(diff_parent);
1856 }
1857
1858 return LY_SUCCESS;
1859}
1860
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001861LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001862lyd_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 +01001863 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001864{
1865 const struct lyd_node *src_root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001866 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001867 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001868
1869 LY_LIST_FOR(src_diff, src_root) {
1870 if (mod && (lyd_owner_module(src_root) != mod)) {
1871 /* skip data nodes from different modules */
1872 continue;
1873 }
1874
1875 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001876 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 +02001877 }
1878
Michal Vaskoe78faec2021-04-08 17:24:43 +02001879cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001880 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001881 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001882}
1883
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001884LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001885lyd_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 +01001886 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001887{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001888 LY_ERR ret;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001889 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001890
Michal Vasko04f85912020-08-07 12:14:58 +02001891 if (!src_sibling) {
1892 return LY_SUCCESS;
1893 }
1894
Michal Vaskoe78faec2021-04-08 17:24:43 +02001895 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 +02001896 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001897 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001898}
1899
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001900LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001901lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001902{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001903 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001904}
Michal Vasko4231fb62020-07-13 13:54:47 +02001905
1906static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001907lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001908{
1909 LY_ERR ret = LY_SUCCESS;
1910 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001911 const char *val1 = NULL;
1912 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001913 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001914
Michal Vaskobaba84e2021-02-05 16:33:30 +01001915 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1916
1917 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001918 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001919
1920 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001921 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001922
1923 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001924 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001925 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001926 } else {
1927 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1928 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001929
1930 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001931 flags = node->flags;
1932 if (node->schema->nodetype == LYS_LEAF) {
1933 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1934 } else {
1935 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02001936
Michal Vaskobaba84e2021-02-05 16:33:30 +01001937 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1938 }
1939 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001940 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1941
1942cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001943 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001944 return ret;
1945}
1946
1947static LY_ERR
1948lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1949{
1950 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001951 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001952
1953 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001954 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001955
1956 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001957 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001958 flag1 = LYD_DEFAULT;
1959 } else {
1960 flag1 = 0;
1961 }
1962
1963 /* current default */
1964 flag2 = node->flags & LYD_DEFAULT;
1965
Michal Vasko610e93b2020-11-09 20:58:32 +01001966 if (flag1 == flag2) {
1967 /* no default state change so nothing to reverse */
1968 return LY_SUCCESS;
1969 }
1970
Michal Vasko4231fb62020-07-13 13:54:47 +02001971 /* switch defaults */
1972 node->flags &= ~LYD_DEFAULT;
1973 node->flags |= flag1;
1974 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1975
1976 return LY_SUCCESS;
1977}
1978
1979static LY_ERR
1980lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1981{
1982 LY_ERR ret = LY_SUCCESS;
1983 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001984 const char *val1 = NULL;
1985 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001986
1987 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001988 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001989
1990 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001991 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001992
1993 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001994 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02001995
1996 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001997 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02001998
1999 /* switch values */
2000 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2001 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2002
2003cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002004 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002005 return ret;
2006}
2007
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002008/**
2009 * @brief Remove specific operation from all the nodes in a subtree.
2010 *
2011 * @param[in] diff Diff subtree to process.
2012 * @param[in] op Only expected operation.
2013 * @return LY_ERR value.
2014 */
2015static LY_ERR
2016lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2017{
2018 struct lyd_node *elem;
2019 struct lyd_meta *meta;
2020
2021 LYD_TREE_DFS_BEGIN(diff, elem) {
2022 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2023 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002024 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 +01002025 lyd_free_meta_single(meta);
2026 }
2027
2028 LYD_TREE_DFS_END(diff, elem);
2029 }
2030
2031 return LY_SUCCESS;
2032}
2033
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002034LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002035lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002036{
2037 LY_ERR ret = LY_SUCCESS;
2038 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002039 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002040 enum lyd_diff_op op;
2041
2042 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2043
2044 if (!src_diff) {
2045 *diff = NULL;
2046 return LY_SUCCESS;
2047 }
2048
2049 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002050 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002051
2052 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002053 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2054 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002055
2056 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002057 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002058 /* skip all keys */
2059 if (!lysc_is_key(elem->schema)) {
2060 /* find operation attribute, if any */
2061 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002062
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002063 switch (op) {
2064 case LYD_DIFF_OP_CREATE:
2065 /* reverse create to delete */
2066 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002067
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002068 /* check all the children for the same operation, nothing else is expected */
2069 LY_LIST_FOR(lyd_child(elem), iter) {
2070 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2071 }
2072
Michal Vasko9e070522021-03-05 14:00:14 +01002073 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002074 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002075 case LYD_DIFF_OP_DELETE:
2076 /* reverse delete to create */
2077 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002078
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002079 /* check all the children for the same operation, nothing else is expected */
2080 LY_LIST_FOR(lyd_child(elem), iter) {
2081 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2082 }
2083
Michal Vasko9e070522021-03-05 14:00:14 +01002084 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002085 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002086 case LYD_DIFF_OP_REPLACE:
2087 switch (elem->schema->nodetype) {
2088 case LYS_LEAF:
2089 /* leaf value change */
2090 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2091 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2092 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002093 case LYS_ANYXML:
2094 case LYS_ANYDATA:
2095 /* any value change */
2096 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2097 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002098 case LYS_LEAFLIST:
2099 /* leaf-list move */
2100 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002101 if (lysc_is_dup_inst_list(elem->schema)) {
2102 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2103 } else {
2104 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2105 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002106 break;
2107 case LYS_LIST:
2108 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002109 if (lysc_is_dup_inst_list(elem->schema)) {
2110 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2111 } else {
2112 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2113 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002114 break;
2115 default:
2116 LOGINT(LYD_CTX(src_diff));
2117 ret = LY_EINT;
2118 goto cleanup;
2119 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002120 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002121 case LYD_DIFF_OP_NONE:
2122 switch (elem->schema->nodetype) {
2123 case LYS_LEAF:
2124 case LYS_LEAFLIST:
2125 /* default flag change */
2126 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2127 break;
2128 default:
2129 /* nothing to do */
2130 break;
2131 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002132 break;
2133 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002134 }
2135
Michal Vasko56daf732020-08-10 10:57:18 +02002136 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002137 }
2138 }
2139
2140cleanup:
2141 if (ret) {
2142 lyd_free_siblings(*diff);
2143 *diff = NULL;
2144 }
2145 return ret;
2146}