blob: b01acbc8a95a5e3e2b6ca93b91f00a7807f6e960 [file] [log] [blame]
Michal Vaskod59035b2020-07-08 12:00:06 +02001/**
2 * @file diff.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief diff functions
5 *
Michal Vaskoe78faec2021-04-08 17:24:43 +02006 * Copyright (c) 2020 - 2021 CESNET, z.s.p.o.
Michal Vaskod59035b2020-07-08 12:00:06 +02007 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
Christian Hopps32874e12021-05-01 09:43:54 -040014#define _GNU_SOURCE /* asprintf, strdup */
Michal Vaskod59035b2020-07-08 12:00:06 +020015
16#include "diff.h"
17
18#include <assert.h>
19#include <stddef.h>
Michal Vaskoe78faec2021-04-08 17:24:43 +020020#include <stdint.h>
21#include <stdio.h>
Radek Krejci47fab892020-11-05 17:02:41 +010022#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020023#include <string.h>
24
25#include "common.h"
Michal Vaskoe78faec2021-04-08 17:24:43 +020026#include "compat.h"
Radek Krejci47fab892020-11-05 17:02:41 +010027#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020028#include "log.h"
Michal Vasko19175b62022-04-01 09:17:07 +020029#include "plugins_exts.h"
Radek Krejci47fab892020-11-05 17:02:41 +010030#include "plugins_types.h"
31#include "set.h"
32#include "tree.h"
33#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020034#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010035#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020036#include "tree_schema.h"
37#include "tree_schema_internal.h"
38
Michal Vasko52afd7d2022-01-18 14:08:34 +010039#define LOGERR_META(ctx, meta_name, node) \
40 { \
41 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
42 LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \
43 free(__path); \
44 }
45
46#define LOGERR_NOINST(ctx, node) \
47 { \
48 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
49 LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \
50 free(__path); \
51 }
52
53#define LOGERR_UNEXPVAL(ctx, node, data_source) \
54 { \
55 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
56 LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \
57 free(__path); \
58 }
59
60#define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \
61 { \
62 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
63 LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \
64 lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \
65 free(__path); \
66 }
67
Michal Vaskod59035b2020-07-08 12:00:06 +020068static const char *
69lyd_diff_op2str(enum lyd_diff_op op)
70{
71 switch (op) {
72 case LYD_DIFF_OP_CREATE:
73 return "create";
74 case LYD_DIFF_OP_DELETE:
75 return "delete";
76 case LYD_DIFF_OP_REPLACE:
77 return "replace";
78 case LYD_DIFF_OP_NONE:
79 return "none";
80 }
81
82 LOGINT(NULL);
83 return NULL;
84}
85
Michal Vaskoe6323f62020-07-09 15:49:02 +020086static enum lyd_diff_op
87lyd_diff_str2op(const char *str)
88{
89 switch (str[0]) {
90 case 'c':
91 assert(!strcmp(str, "create"));
92 return LYD_DIFF_OP_CREATE;
93 case 'd':
94 assert(!strcmp(str, "delete"));
95 return LYD_DIFF_OP_DELETE;
96 case 'r':
97 assert(!strcmp(str, "replace"));
98 return LYD_DIFF_OP_REPLACE;
99 case 'n':
100 assert(!strcmp(str, "none"));
101 return LYD_DIFF_OP_NONE;
102 }
103
104 LOGINT(NULL);
105 return 0;
106}
107
Michal Vaskocffc3f92022-06-15 07:57:24 +0200108/**
109 * @brief Create diff metadata for a nested user-ordered node with the effective operation "create".
110 *
111 * @param[in] node User-rodered node to update.
112 * @return LY_ERR value.
113 */
114static LY_ERR
115lyd_diff_add_create_nested_userord(struct lyd_node *node)
116{
117 LY_ERR rc = LY_SUCCESS;
118 const char *meta_name, *meta_val;
119 size_t buflen = 0, bufused = 0;
120 uint32_t pos;
121 char *dyn = NULL;
122
123 assert(lysc_is_userordered(node->schema));
124
125 /* get correct metadata name and value */
126 if (lysc_is_dup_inst_list(node->schema)) {
127 meta_name = "yang:position";
128
129 pos = lyd_list_pos(node);
130 if (asprintf(&dyn, "%" PRIu32, pos) == -1) {
131 LOGMEM(LYD_CTX(node));
132 rc = LY_EMEM;
133 goto cleanup;
134 }
135 meta_val = dyn;
136 } else if (node->schema->nodetype == LYS_LIST) {
137 meta_name = "yang:key";
138
139 if (node->prev->next && (node->prev->schema == node->schema)) {
140 LY_CHECK_GOTO(rc = lyd_path_list_predicate(node->prev, &dyn, &buflen, &bufused, 0), cleanup);
141 meta_val = dyn;
142 } else {
143 meta_val = "";
144 }
145 } else {
146 meta_name = "yang:value";
147
148 if (node->prev->next && (node->prev->schema == node->schema)) {
149 meta_val = lyd_get_value(node->prev);
150 } else {
151 meta_val = "";
152 }
153 }
154
155 /* create the metadata */
156 LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_val, 0, NULL), cleanup);
157
158cleanup:
159 free(dyn);
160 return rc;
161}
162
Michal Vaskod59035b2020-07-08 12:00:06 +0200163LY_ERR
164lyd_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 +0200165 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
166 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200167{
Michal Vaskocffc3f92022-06-15 07:57:24 +0200168 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
Michal Vaskod59035b2020-07-08 12:00:06 +0200169 const struct lyd_node *parent = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200170
171 assert(diff);
172
Michal Vasko53d48422020-11-13 18:02:29 +0100173 /* replace leaf always needs orig-default and orig-value */
174 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
175
176 /* create on userord needs key/value */
177 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200178 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100179 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200180 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100181
182 /* move on userord needs both key and orig-key/value and orig-value */
183 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200184 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100185 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200186 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
187 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100188
Michal Vaskod59035b2020-07-08 12:00:06 +0200189 /* find the first existing parent */
190 siblings = *diff;
191 while (1) {
192 /* find next node parent */
193 parent = node;
194 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100195 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200196 }
197 if (parent == node) {
198 /* no more parents to find */
199 break;
200 }
201
202 /* check whether it exists in the diff */
203 if (lyd_find_sibling_first(siblings, parent, &match)) {
204 break;
205 }
206
207 /* another parent found */
208 diff_parent = match;
209
210 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200211 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200212 }
213
214 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200215 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100216 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200217
218 /* find the first duplicated parent */
219 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100220 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200221 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100222 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200223 }
224 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100225 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200226 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100227 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200228 }
229 }
230
231 /* no parent existed, must be manually connected */
232 if (!diff_parent) {
233 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200234 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200235 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200236 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200237 }
238
Michal Vaskod59035b2020-07-08 12:00:06 +0200239 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200240 if (diff_parent && (diff_parent != dup)) {
Michal Vasko2e552792022-11-02 12:15:31 +0100241 LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200242 }
243
244 /* add subtree operation */
Michal Vasko2e552792022-11-02 12:15:31 +0100245 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200246
Michal Vaskocffc3f92022-06-15 07:57:24 +0200247 if (op == LYD_DIFF_OP_CREATE) {
248 /* all nested user-ordered (leaf-)lists need special metadata for create op */
249 LYD_TREE_DFS_BEGIN(dup, elem) {
250 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
251 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
252 }
253 LYD_TREE_DFS_END(dup, elem);
254 }
255 }
256
Michal Vaskod59035b2020-07-08 12:00:06 +0200257 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200258 if (orig_default) {
Michal Vasko2e552792022-11-02 12:15:31 +0100259 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200260 }
261
262 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200263 if (orig_value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100264 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200265 }
266
267 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200268 if (key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100269 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200270 }
271
272 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200273 if (value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100274 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200275 }
276
Michal Vaskoe78faec2021-04-08 17:24:43 +0200277 /* position */
278 if (position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100279 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200280 }
281
Michal Vaskod59035b2020-07-08 12:00:06 +0200282 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200283 if (orig_key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100284 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200285 }
286
Michal Vaskoe78faec2021-04-08 17:24:43 +0200287 /* orig-position */
288 if (orig_position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100289 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200290 }
291
Michal Vaskod59035b2020-07-08 12:00:06 +0200292 return LY_SUCCESS;
293}
294
295/**
296 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
297 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100298 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200299 * @param[in] schema Schema node of the list/leaf-list.
300 * @param[in,out] userord Sized array of userord items.
301 * @return Userord item for all the user-ordered list/leaf-list instances.
302 */
303static struct lyd_diff_userord *
304lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
305{
306 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200307 struct lyd_node *iter;
308 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200309 LY_ARRAY_COUNT_TYPE u;
310
311 LY_ARRAY_FOR(*userord, u) {
312 if ((*userord)[u].schema == schema) {
313 return &(*userord)[u];
314 }
315 }
316
317 /* it was not added yet, add it now */
318 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
319
320 item->schema = schema;
321 item->pos = 0;
322 item->inst = NULL;
323
324 /* store all the instance pointers in the current order */
325 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200326 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
327 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
328 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200329 }
330 }
331
332 return item;
333}
334
335/**
336 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
337 * lists/leaf-lists.
338 *
339 * @param[in] first Node from the first tree, can be NULL (on create).
340 * @param[in] second Node from the second tree, can be NULL (on delete).
341 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100342 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200343 * @param[out] op Operation.
344 * @param[out] orig_default Original default metadata.
345 * @param[out] value Value metadata.
346 * @param[out] orig_value Original value metadata
347 * @param[out] key Key metadata.
348 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200349 * @param[out] position Position metadata.
350 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200351 * @return LY_SUCCESS on success,
352 * @return LY_ENOT if there is no change to be added into diff,
353 * @return LY_ERR value on other errors.
354 */
355static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200356lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100357 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200358 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200359{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200360 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200361 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200362 size_t buflen, bufused;
363 uint32_t first_pos, second_pos;
Michal Vaskod59035b2020-07-08 12:00:06 +0200364
365 assert(first || second);
366
367 *orig_default = NULL;
368 *value = NULL;
369 *orig_value = NULL;
370 *key = NULL;
371 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200372 *position = NULL;
373 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200374
375 schema = first ? first->schema : second->schema;
376 assert(lysc_is_userordered(schema));
377
Michal Vaskod59035b2020-07-08 12:00:06 +0200378 /* find user-ordered first position */
379 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200380 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200381 if (userord_item->inst[first_pos] == first) {
382 break;
383 }
384 }
385 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
386 } else {
387 first_pos = 0;
388 }
389
Michal Vaskoe78faec2021-04-08 17:24:43 +0200390 /* prepare position of the next instance */
391 second_pos = userord_item->pos++;
392
Michal Vaskod59035b2020-07-08 12:00:06 +0200393 /* learn operation first */
394 if (!second) {
395 *op = LYD_DIFF_OP_DELETE;
396 } else if (!first) {
397 *op = LYD_DIFF_OP_CREATE;
398 } else {
Michal Vasko8f359bf2020-07-28 10:41:15 +0200399 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200400 /* in first, there is a different instance on the second position, we are going to move 'first' node */
401 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200402 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200403 /* default flag change */
404 *op = LYD_DIFF_OP_NONE;
405 } else {
406 /* no changes */
407 return LY_ENOT;
408 }
409 }
410
411 /*
412 * set each attribute correctly based on the operation and node type
413 */
414
415 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100416 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200417 if (first->flags & LYD_DEFAULT) {
418 *orig_default = "true";
419 } else {
420 *orig_default = "false";
421 }
422 }
423
424 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200425 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
426 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200427 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200428 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200429 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200430 } else {
431 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200432 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200433 }
434 }
435
436 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200437 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
438 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200439 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200440 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200441 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200442 } else {
443 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200444 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200445 }
446 }
447
448 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200449 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
450 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200451 if (second_pos) {
452 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200453 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 +0200454 } else {
455 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200456 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200457 }
458 }
459
460 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200461 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
462 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200463 if (first_pos) {
464 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200465 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 +0200466 } else {
467 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200468 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200469 }
470 }
471
Michal Vaskoe78faec2021-04-08 17:24:43 +0200472 /* position */
473 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
474 if (second_pos) {
475 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
476 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200477 rc = LY_EMEM;
478 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200479 }
480 } else {
481 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200482 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200483 }
484 }
485
486 /* orig-position */
487 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
488 if (first_pos) {
489 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
490 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200491 rc = LY_EMEM;
492 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200493 }
494 } else {
495 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200496 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200497 }
498 }
499
Michal Vaskod59035b2020-07-08 12:00:06 +0200500 /*
501 * update our instances - apply the change
502 */
503 if (*op == LYD_DIFF_OP_CREATE) {
504 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200505 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200506 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
507 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
508 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
509 }
510 LY_ARRAY_INCREMENT(userord_item->inst);
511 userord_item->inst[second_pos] = second;
512
513 } else if (*op == LYD_DIFF_OP_DELETE) {
514 /* remove the instance */
515 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
516 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
517 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
518 }
519 LY_ARRAY_DECREMENT(userord_item->inst);
520
521 } else if (*op == LYD_DIFF_OP_REPLACE) {
522 /* move the instances */
523 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
524 (first_pos - second_pos) * sizeof *userord_item->inst);
525 userord_item->inst[second_pos] = first;
526 }
527
Michal Vaskof9b052a2022-06-08 10:26:53 +0200528cleanup:
529 if (rc) {
530 free(*value);
531 *value = NULL;
532 free(*orig_value);
533 *orig_value = NULL;
534 free(*key);
535 *key = NULL;
536 free(*orig_key);
537 *orig_key = NULL;
538 free(*position);
539 *position = NULL;
540 free(*orig_position);
541 *orig_position = NULL;
542 }
543 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200544}
545
546/**
547 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
548 * lists/leaf-lists.
549 *
550 * @param[in] first Node from the first tree, can be NULL (on create).
551 * @param[in] second Node from the second tree, can be NULL (on delete).
552 * @param[in] options Diff options.
553 * @param[out] op Operation.
554 * @param[out] orig_default Original default metadata.
555 * @param[out] orig_value Original value metadata.
556 * @return LY_SUCCESS on success,
557 * @return LY_ENOT if there is no change to be added into diff,
558 * @return LY_ERR value on other errors.
559 */
560static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200561lyd_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 +0200562 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200563{
564 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200565 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200566
567 assert(first || second);
568
569 *orig_default = NULL;
570 *orig_value = NULL;
571
572 schema = first ? first->schema : second->schema;
573 assert(!lysc_is_userordered(schema));
574
575 /* learn operation first */
576 if (!second) {
577 *op = LYD_DIFF_OP_DELETE;
578 } else if (!first) {
579 *op = LYD_DIFF_OP_CREATE;
580 } else {
581 switch (schema->nodetype) {
582 case LYS_CONTAINER:
583 case LYS_RPC:
584 case LYS_ACTION:
585 case LYS_NOTIF:
586 /* no changes */
587 return LY_ENOT;
588 case LYS_LIST:
589 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200590 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200591 /* default flag change */
592 *op = LYD_DIFF_OP_NONE;
593 } else {
594 /* no changes */
595 return LY_ENOT;
596 }
597 break;
598 case LYS_LEAF:
599 case LYS_ANYXML:
600 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200601 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200602 /* different values */
603 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200604 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200605 /* default flag change */
606 *op = LYD_DIFF_OP_NONE;
607 } else {
608 /* no changes */
609 return LY_ENOT;
610 }
611 break;
612 default:
613 LOGINT_RET(schema->module->ctx);
614 }
615 }
616
617 /*
618 * set each attribute correctly based on the operation and node type
619 */
620
621 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100622 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200623 if (first->flags & LYD_DEFAULT) {
624 *orig_default = "true";
625 } else {
626 *orig_default = "false";
627 }
628 }
629
630 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100631 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
632 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200633 str_val = lyd_get_value(first);
634 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100635 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
636 } else {
637 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
638 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200639 }
640
641 return LY_SUCCESS;
642}
643
644/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200645 * @brief Find a matching instance of a node in a data tree.
646 *
647 * @param[in] siblings Siblings to search in.
648 * @param[in] target Target node to search for.
649 * @param[in] defaults Whether to consider (or ignore) default values.
650 * @param[in,out] dup_inst_cache Duplicate instance cache.
651 * @param[out] match Found match, NULL if no matching node found.
652 * @return LY_ERR value.
653 */
654static LY_ERR
655lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200656 struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200657{
Michal Vaskoe78faec2021-04-08 17:24:43 +0200658 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
659 /* try to find the exact instance */
660 lyd_find_sibling_first(siblings, target, match);
661 } else {
662 /* try to simply find the node, there cannot be more instances */
663 lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
664 }
665
Michal Vaskod7c048c2021-05-18 16:12:55 +0200666 /* update match as needed */
667 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200668
669 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
670 /* ignore default nodes */
671 *match = NULL;
672 }
673 return LY_SUCCESS;
674}
675
676/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200677 * @brief Perform diff for all siblings at certain depth, recursively.
678 *
679 * For user-ordered lists/leaf-lists a specific structure is used for storing
680 * the current order. The idea is to apply all the generated diff changes
681 * virtually on the first tree so that we can continue to generate correct
682 * changes after some were already generated.
683 *
684 * The algorithm then uses second tree position-based changes with a before
685 * (preceding) item anchor.
686 *
687 * Example:
688 *
689 * Virtual first tree leaf-list order:
690 * 1 2 [3] 4 5
691 *
692 * Second tree leaf-list order:
693 * 1 2 [5] 3 4
694 *
695 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
696 * match - they do not - move nodes so that the 3rd position node is final ->
697 * -> move node 5 to the 3rd position -> move node 5 after node 2.
698 *
699 * Required properties:
700 * Stored operations (move) should not be affected by later operations -
701 * - would cause a redundantly long list of operations, possibly inifinite.
702 *
703 * Implemenation justification:
704 * First, all delete operations and only then move/create operations are stored.
705 * Also, preceding anchor is used and after each iteration another node is
706 * at its final position. That results in the invariant that all preceding
707 * nodes are final and will not be changed by the later operations, meaning
708 * they can safely be used as anchors for the later operations.
709 *
710 * @param[in] first First tree first sibling.
711 * @param[in] second Second tree first sibling.
712 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200713 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200714 * @param[in,out] diff Diff to append to.
715 * @return LY_ERR value.
716 */
717static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200718lyd_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 +0200719 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200720{
721 LY_ERR ret = LY_SUCCESS;
722 const struct lyd_node *iter_first, *iter_second;
723 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100724 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200725 struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200726 LY_ARRAY_COUNT_TYPE u;
727 enum lyd_diff_op op;
728 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200729 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200730
Michal Vaskod59035b2020-07-08 12:00:06 +0200731 /* compare first tree to the second tree - delete, replace, none */
732 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200733 if (!iter_first->schema) {
734 continue;
735 }
736
Michal Vaskod59035b2020-07-08 12:00:06 +0200737 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200738 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200739 /* skip default nodes */
740 continue;
741 }
742
Michal Vaskoe78faec2021-04-08 17:24:43 +0200743 /* find a match in the second tree */
744 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
745 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200746
747 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100748 /* get (create) userord entry */
749 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
750 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
751
Michal Vaskoe78faec2021-04-08 17:24:43 +0200752 /* we are handling only user-ordered node delete now */
753 if (!match_second) {
754 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100755 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
756 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200757
758 /* there must be changes, it is deleted */
759 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100760 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
761 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200762
763 free(orig_value);
764 free(key);
765 free(value);
766 free(position);
767 free(orig_key);
768 free(orig_position);
769 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200770 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200771 } else {
772 /* get all the attributes */
773 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
774
775 /* add into diff if there are any changes */
776 if (!ret) {
777 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200778 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200779 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100780 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200781 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200782 }
783
784 free(orig_value);
785 LY_CHECK_GOTO(ret, cleanup);
786 } else if (ret == LY_ENOT) {
787 ret = LY_SUCCESS;
788 } else {
789 goto cleanup;
790 }
791 }
792
793 /* check descendants, if any, recursively */
794 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200795 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
796 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200797 }
798
799 if (nosiblings) {
800 break;
801 }
802 }
803
804 /* reset all cached positions */
805 LY_ARRAY_FOR(userord, u) {
806 userord[u].pos = 0;
807 }
808
809 /* compare second tree to the first tree - create, user-ordered move */
810 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200811 if (!iter_second->schema) {
812 continue;
813 }
814
Michal Vaskod59035b2020-07-08 12:00:06 +0200815 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200816 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200817 /* skip default nodes */
818 continue;
819 }
820
Michal Vaskoe78faec2021-04-08 17:24:43 +0200821 /* find a match in the first tree */
822 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
823 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200824
825 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100826 /* get userord entry */
827 userord_item = lyd_diff_userord_get(NULL, iter_second->schema, &userord);
828 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
829
Michal Vaskod59035b2020-07-08 12:00:06 +0200830 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100831 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200832 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200833
834 /* add into diff if there are any changes */
835 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200836 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
837 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200838
839 free(orig_value);
840 free(key);
841 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200842 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200843 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200844 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200845 LY_CHECK_GOTO(ret, cleanup);
846 } else if (ret == LY_ENOT) {
847 ret = LY_SUCCESS;
848 } else {
849 goto cleanup;
850 }
851 } else if (!match_first) {
852 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200853 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 +0200854
855 /* there must be changes, it is created */
856 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200857 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200858
859 free(orig_value);
860 LY_CHECK_GOTO(ret, cleanup);
861 } /* else was handled */
862
863 if (nosiblings) {
864 break;
865 }
866 }
867
868cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200869 lyd_dup_inst_free(dup_inst_first);
870 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200871 LY_ARRAY_FOR(userord, u) {
872 LY_ARRAY_FREE(userord[u].inst);
873 }
874 LY_ARRAY_FREE(userord);
875 return ret;
876}
877
Michal Vasko3a41dff2020-07-15 14:30:28 +0200878static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100879lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
880 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200881{
882 const struct ly_ctx *ctx;
883
884 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
885
886 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200887 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200888 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200889 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200890 } else {
891 ctx = NULL;
892 }
893
894 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
895 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
896 return LY_EINVAL;
897 }
898
899 *diff = NULL;
900
Michal Vasko3a41dff2020-07-15 14:30:28 +0200901 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
902}
903
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100904LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200905lyd_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 +0200906{
907 return lyd_diff(first, second, options, 1, diff);
908}
909
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100910LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200911lyd_diff_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 +0200912{
913 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200914}
915
916/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200917 * @brief Learn operation of a diff node.
918 *
919 * @param[in] diff_node Diff node.
920 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200921 * @return LY_ERR value.
922 */
923static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200924lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200925{
926 struct lyd_meta *meta = NULL;
927 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200928 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100929 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +0200930
Michal Vasko9e685082021-01-29 14:49:09 +0100931 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200932 LY_LIST_FOR(diff_parent->meta, meta) {
933 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200934 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200935 if ((str[0] == 'r') && (diff_parent != diff_node)) {
936 /* we do not care about this operation if it's in our parent */
937 continue;
938 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200939 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200940 break;
941 }
942 }
943 if (meta) {
944 break;
945 }
946 }
Michal Vasko52afd7d2022-01-18 14:08:34 +0100947
948 if (!meta) {
949 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
950 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
951 free(path);
952 return LY_EINT;
953 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200954
Michal Vaskod59035b2020-07-08 12:00:06 +0200955 return LY_SUCCESS;
956}
957
958/**
959 * @brief Insert a diff node into a data tree.
960 *
961 * @param[in,out] first_node First sibling of the data tree.
962 * @param[in] parent_node Data tree sibling parent node.
963 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200964 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
965 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200966 * @return err_info, NULL on success.
967 */
968static LY_ERR
969lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200970 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200971{
972 LY_ERR ret;
973 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200974 uint32_t pos, anchor_pos;
975 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200976
977 assert(new_node);
978
979 if (!*first_node) {
980 if (!parent_node) {
981 /* no parent or siblings */
982 *first_node = new_node;
983 return LY_SUCCESS;
984 }
985
986 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200987 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200988 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200989 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200990 return LY_EINVAL;
991 }
Michal Vaskob104f112020-07-17 09:54:54 +0200992 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200993 }
994
Michal Vasko9e685082021-01-29 14:49:09 +0100995 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200996
Michal Vaskod59035b2020-07-08 12:00:06 +0200997 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200998 /* simple insert */
999 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001000 }
1001
Michal Vaskoe78faec2021-04-08 17:24:43 +02001002 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001003 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001004 if (lysc_is_dup_inst_list(new_node->schema)) {
1005 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001006 if (!anchor_pos) {
1007 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1008 return LY_EINVAL;
1009 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001010
1011 found = 0;
1012 pos = 1;
1013 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1014 if (pos == anchor_pos) {
1015 found = 1;
1016 break;
1017 }
1018 ++pos;
1019 }
1020 if (!found) {
1021 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1022 new_node->schema->name);
1023 return LY_EINVAL;
1024 }
1025 } else {
1026 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1027 if (ret == LY_ENOTFOUND) {
1028 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1029 new_node->schema->name);
1030 return LY_EINVAL;
1031 } else if (ret) {
1032 return ret;
1033 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001034 }
1035
1036 /* insert after */
1037 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1038 assert(new_node->prev == anchor);
1039 if (*first_node == new_node) {
1040 *first_node = anchor;
1041 }
1042 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001043 /* find the first instance */
1044 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1045 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001046
Michal Vaskoea7d3232022-04-19 12:01:36 +02001047 if (anchor) {
1048 /* insert before the first instance */
1049 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1050 if ((*first_node)->prev->next) {
1051 assert(!new_node->prev->next);
1052 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001053 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001054 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001055 /* insert anywhere */
1056 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001057 }
1058 }
1059
1060 return LY_SUCCESS;
1061}
1062
1063/**
1064 * @brief Apply diff subtree on data tree nodes, recursively.
1065 *
1066 * @param[in,out] first_node First sibling of the data tree.
1067 * @param[in] parent_node Parent of the first sibling.
1068 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001069 * @param[in] diff_cb Optional diff callback.
1070 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001071 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001072 * @return LY_ERR value.
1073 */
1074static LY_ERR
1075lyd_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 +02001076 lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001077{
1078 LY_ERR ret;
1079 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001080 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001081 enum lyd_diff_op op;
1082 struct lyd_meta *meta;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001083 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001084 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001085
1086 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001087 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001088
Michal Vaskoe6323f62020-07-09 15:49:02 +02001089 /* handle specific user-ordered (leaf-)lists operations separately */
1090 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1091 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001092 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001093 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001094 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001095 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001096 /* duplicate the node */
1097 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001098 }
1099
Michal Vaskoe78faec2021-04-08 17:24:43 +02001100 /* get "key", "value", or "position" metadata string value */
1101 if (lysc_is_dup_inst_list(diff_node->schema)) {
1102 meta_str = "yang:position";
1103 } else if (diff_node->schema->nodetype == LYS_LIST) {
1104 meta_str = "yang:key";
1105 } else {
1106 meta_str = "yang:value";
1107 }
1108 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001109 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001110 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001111
Michal Vaskod59035b2020-07-08 12:00:06 +02001112 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001113 if (str_val[0]) {
1114 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001115 } else {
1116 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1117 }
1118 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001119 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001120 lyd_free_tree(match);
1121 }
1122 return ret;
1123 }
1124
1125 goto next_iter_r;
1126 }
1127
1128 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001129 switch (op) {
1130 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001131 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001132 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001133 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001134
1135 if (match->schema->nodetype & LYD_NODE_TERM) {
1136 /* special case of only dflt flag change */
1137 if (diff_node->flags & LYD_DEFAULT) {
1138 match->flags |= LYD_DEFAULT;
1139 } else {
1140 match->flags &= ~LYD_DEFAULT;
1141 }
1142 } else {
1143 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001144 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001145 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1146 LYD_NAME(diff_node));
1147 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001148 }
1149 }
1150 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001151 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001152 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001153 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001154
1155 /* insert it at the end */
1156 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001157 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001158 if (match->flags & LYD_EXT) {
1159 ret = lyd_insert_ext(parent_node, match);
1160 } else {
1161 ret = lyd_insert_child(parent_node, match);
1162 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001163 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001164 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001165 }
1166 if (ret) {
1167 lyd_free_tree(match);
1168 return ret;
1169 }
1170
1171 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001172 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001173 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001174 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001175 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001176
1177 /* remove it */
1178 if ((match == *first_node) && !match->parent) {
1179 assert(!parent_node);
1180 /* we have removed the top-level node */
1181 *first_node = (*first_node)->next;
1182 }
1183 lyd_free_tree(match);
1184
1185 /* we are not going recursively in this case, the whole subtree was already deleted */
1186 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001187 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001188 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1189 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1190 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1191 return LY_EINVAL;
1192 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001193
1194 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001195 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001196 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001197
Michal Vaskobaba84e2021-02-05 16:33:30 +01001198 /* update the value */
1199 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001200 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001201 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001202 } else {
1203 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001204
Michal Vaskoe78faec2021-04-08 17:24:43 +02001205 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001206 }
1207
1208 /* with flags */
1209 match->flags = diff_node->flags;
1210 break;
1211 default:
1212 LOGINT_RET(ctx);
1213 }
1214
1215next_iter_r:
1216 if (diff_cb) {
1217 /* call callback */
1218 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1219 }
1220
1221 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001222 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001223 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001224 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1225 if (ret) {
1226 break;
1227 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001228 }
1229
Michal Vaskod7c048c2021-05-18 16:12:55 +02001230 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001231 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001232}
1233
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001234LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001235lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001236 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001237{
1238 const struct lyd_node *root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001239 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001240 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001241
1242 LY_LIST_FOR(diff, root) {
1243 if (mod && (lyd_owner_module(root) != mod)) {
1244 /* skip data nodes from different modules */
1245 continue;
1246 }
1247
1248 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001249 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1250 if (ret) {
1251 break;
1252 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001253 }
1254
Michal Vaskod7c048c2021-05-18 16:12:55 +02001255 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001256 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001257}
1258
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001259LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001260lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001261{
1262 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1263}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001264
1265/**
1266 * @brief Update operations on a diff node when the new operation is NONE.
1267 *
1268 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001269 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001270 * @param[in] src_diff Current source diff node.
1271 * @return LY_ERR value.
1272 */
1273static LY_ERR
1274lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1275{
1276 switch (cur_op) {
1277 case LYD_DIFF_OP_NONE:
1278 case LYD_DIFF_OP_CREATE:
1279 case LYD_DIFF_OP_REPLACE:
1280 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1281 /* NONE on a term means only its dflt flag was changed */
1282 diff_match->flags &= ~LYD_DEFAULT;
1283 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1284 }
1285 break;
1286 default:
1287 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001288 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1289 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001290 }
1291
1292 return LY_SUCCESS;
1293}
1294
1295/**
1296 * @brief Remove an attribute from a node.
1297 *
1298 * @param[in] node Node with the metadata.
1299 * @param[in] name Metadata name.
1300 */
1301static void
1302lyd_diff_del_meta(struct lyd_node *node, const char *name)
1303{
1304 struct lyd_meta *meta;
1305
1306 LY_LIST_FOR(node->meta, meta) {
1307 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001308 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001309 return;
1310 }
1311 }
1312
1313 assert(0);
1314}
1315
1316/**
1317 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001318 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001319 *
1320 * @param[in] node Node to change.
1321 * @param[in] op Operation to set.
1322 * @return LY_ERR value.
1323 */
1324static LY_ERR
1325lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1326{
1327 struct lyd_meta *meta;
1328
1329 LY_LIST_FOR(node->meta, meta) {
1330 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001331 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001332 break;
1333 }
1334 }
1335
Michal Vasko871a0252020-11-11 18:35:24 +01001336 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001337}
1338
1339/**
1340 * @brief Update operations on a diff node when the new operation is REPLACE.
1341 *
1342 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001343 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001344 * @param[in] src_diff Current source diff node.
1345 * @return LY_ERR value.
1346 */
1347static LY_ERR
1348lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1349{
1350 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001351 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001352 struct lyd_meta *meta;
1353 const struct lys_module *mod;
1354 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001355 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001356
1357 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001358 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001359 assert(mod);
1360
1361 switch (cur_op) {
1362 case LYD_DIFF_OP_REPLACE:
1363 case LYD_DIFF_OP_CREATE:
1364 switch (diff_match->schema->nodetype) {
1365 case LYS_LIST:
1366 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001367 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001368 * keep orig_key/orig_value (only replace oper) and replace key/value */
1369 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001370 if (lysc_is_dup_inst_list(diff_match->schema)) {
1371 meta_name = "position";
1372 } else if (diff_match->schema->nodetype == LYS_LIST) {
1373 meta_name = "key";
1374 } else {
1375 meta_name = "value";
1376 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001377
1378 lyd_diff_del_meta(diff_match, meta_name);
1379 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001380 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001381 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001382 break;
1383 case LYS_LEAF:
1384 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001385 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001386 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1387 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001388 }
1389
Michal Vaskoe6323f62020-07-09 15:49:02 +02001390 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001391 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001392 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001393 }
1394
Michal Vasko8caadab2020-11-05 17:38:15 +01001395 if (cur_op == LYD_DIFF_OP_REPLACE) {
1396 /* compare values whether there is any change at all */
1397 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001398 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001399 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001400 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1401 if (!ret) {
1402 /* values are the same, remove orig-value meta and set oper to NONE */
1403 lyd_free_meta_single(meta);
1404 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1405 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001406 }
1407
1408 /* modify the default flag */
1409 diff_match->flags &= ~LYD_DEFAULT;
1410 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1411 break;
1412 case LYS_ANYXML:
1413 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001414 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001415 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1416 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001417 }
1418
1419 /* modify the node value */
1420 any = (struct lyd_node_any *)src_diff;
1421 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1422 break;
1423 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001424 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001425 }
1426 break;
1427 case LYD_DIFF_OP_NONE:
1428 /* it is moved now */
1429 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1430
1431 /* change the operation */
1432 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1433
Michal Vaskoe78faec2021-04-08 17:24:43 +02001434 /* set orig-meta and meta */
1435 if (lysc_is_dup_inst_list(diff_match->schema)) {
1436 meta_name = "position";
1437 orig_meta_name = "orig-position";
1438 } else {
1439 meta_name = "key";
1440 orig_meta_name = "orig-key";
1441 }
1442
1443 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001444 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001445 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001446
Michal Vaskoe78faec2021-04-08 17:24:43 +02001447 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001448 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001449 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001450 break;
1451 default:
1452 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001453 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1454 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001455 }
1456
1457 return LY_SUCCESS;
1458}
1459
1460/**
1461 * @brief Update operations in a diff node when the new operation is CREATE.
1462 *
1463 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001464 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001465 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001466 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001467 * @return LY_ERR value.
1468 */
1469static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001470lyd_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 +02001471{
1472 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001473 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001474 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001475 const char *meta_name, *orig_meta_name;
1476 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001477 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001478
1479 switch (cur_op) {
1480 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001481 /* remember current flags */
1482 trg_flags = diff_match->flags;
1483
Michal Vaskoe78faec2021-04-08 17:24:43 +02001484 if (lysc_is_userordered(diff_match->schema)) {
1485 /* get anchor metadata */
1486 if (lysc_is_dup_inst_list(diff_match->schema)) {
1487 meta_name = "yang:position";
1488 orig_meta_name = "yang:orig-position";
1489 } else if (diff_match->schema->nodetype == LYS_LIST) {
1490 meta_name = "yang:key";
1491 orig_meta_name = "yang:orig-key";
1492 } else {
1493 meta_name = "yang:value";
1494 orig_meta_name = "yang:orig-value";
1495 }
1496 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001497 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001498 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001499 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001500
1501 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1502 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001503 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001504 /* deleted + created at another position -> operation REPLACE */
1505 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1506
1507 /* add anchor metadata */
1508 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1509 } else {
1510 /* deleted + created at the same position -> operation NONE */
1511 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1512
1513 /* delete anchor metadata */
1514 lyd_free_meta_single(orig_meta);
1515 }
1516 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1517 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1518 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1519 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1520 }
1521
Radek Krejci55c4bd22021-04-26 08:09:04 +02001522 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1523 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001524 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1525 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1526 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1527 /* deleted + created -> operation NONE */
1528 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1529 } else {
1530 /* we deleted it, but it was created with a different value -> operation REPLACE */
1531 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1532
1533 /* current value is the previous one (meta) */
1534 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001535 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001536
1537 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001538 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001539 }
1540 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001541 /* deleted + created -> operation NONE */
1542 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001543 }
1544
1545 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001546 /* add orig-dflt metadata */
1547 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1548 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1549
Michal Vaskoe6323f62020-07-09 15:49:02 +02001550 /* update dflt flag itself */
1551 diff_match->flags &= ~LYD_DEFAULT;
1552 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001553 }
1554
1555 /* but the operation of its children should remain DELETE */
1556 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1557 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001558 }
1559 break;
1560 default:
1561 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001562 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1563 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001564 }
1565
1566 return LY_SUCCESS;
1567}
1568
1569/**
1570 * @brief Update operations on a diff node when the new operation is DELETE.
1571 *
1572 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001573 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001574 * @param[in] src_diff Current source diff node.
1575 * @return LY_ERR value.
1576 */
1577static LY_ERR
1578lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1579{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001580 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001581 struct lyd_meta *meta;
1582 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001583 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001584
1585 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001586 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 +02001587
1588 switch (cur_op) {
1589 case LYD_DIFF_OP_CREATE:
1590 /* it was created, but then deleted -> set NONE operation */
1591 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1592
1593 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1594 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001595 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1596 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001597 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001598 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001599 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001600 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1601 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001602 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001603 break;
1604 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001605 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001606 if (lysc_is_userordered(diff_match->schema)) {
1607 if (lysc_is_dup_inst_list(diff_match->schema)) {
1608 meta_name = "position";
1609 } else if (diff_match->schema->nodetype == LYS_LIST) {
1610 meta_name = "key";
1611 } else {
1612 meta_name = "value";
1613 }
1614 } else {
1615 assert(diff_match->schema->nodetype == LYS_LEAF);
1616
1617 /* switch value for the original one */
1618 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001619 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001620 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001621 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1622 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001623 }
1624
1625 /* switch default for the original one, then remove the meta */
1626 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001627 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001628 diff_match->flags &= ~LYD_DEFAULT;
1629 if (meta->value.boolean) {
1630 diff_match->flags |= LYD_DEFAULT;
1631 }
1632 lyd_free_meta_single(meta);
1633
1634 meta_name = "orig-value";
1635 }
1636 lyd_diff_del_meta(diff_match, meta_name);
1637
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001638 /* it was being changed, but should be deleted instead -> set DELETE operation */
1639 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1640 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001641 case LYD_DIFF_OP_NONE:
1642 /* it was not modified, but should be deleted -> set DELETE operation */
1643 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001644 break;
1645 default:
1646 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001647 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1648 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001649 }
1650
1651 return LY_SUCCESS;
1652}
1653
1654/**
1655 * @brief Check whether this diff node is redundant (does not change data).
1656 *
1657 * @param[in] diff Diff node.
1658 * @return 0 if not, non-zero if it is.
1659 */
1660static int
1661lyd_diff_is_redundant(struct lyd_node *diff)
1662{
1663 enum lyd_diff_op op;
1664 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1665 struct lyd_node *child;
1666 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001667 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001668
1669 assert(diff);
1670
Michal Vaskoe78faec2021-04-08 17:24:43 +02001671 if (lysc_is_dup_inst_list(diff->schema)) {
1672 /* all descendants are keys */
1673 child = NULL;
1674 } else {
1675 child = lyd_child_no_keys(diff);
1676 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001677 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001678 assert(mod);
1679
1680 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001681 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001682
1683 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001684 /* get metadata names */
1685 if (lysc_is_dup_inst_list(diff->schema)) {
1686 meta_name = "position";
1687 orig_meta_name = "orig-position";
1688 } else if (diff->schema->nodetype == LYS_LIST) {
1689 meta_name = "key";
1690 orig_meta_name = "orig-key";
1691 } else {
1692 meta_name = "value";
1693 orig_meta_name = "orig-value";
1694 }
1695
Michal Vaskoe6323f62020-07-09 15:49:02 +02001696 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001697 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1698 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001699 assert(orig_val_meta && val_meta);
1700
1701 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1702 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001703 lyd_free_meta_single(orig_val_meta);
1704 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001705 if (child) {
1706 /* change operation to NONE, we have siblings */
1707 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1708 return 0;
1709 }
1710
1711 /* redundant node, BUT !!
1712 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1713 * because the data that this is applied on should not change for the diff lifetime.
1714 * However, when we are merging 2 diffs, this conversion is actually lossy because
1715 * if the data change, the move operation can also change its meaning. In this specific
1716 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1717 */
1718 return 1;
1719 }
1720 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1721 /* check whether at least the default flags are different */
1722 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1723 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001724 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001725
1726 /* if previous and current dflt flags are the same, this node is redundant */
1727 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1728 return 1;
1729 }
1730 return 0;
1731 }
1732
1733 if (!child && (op == LYD_DIFF_OP_NONE)) {
1734 return 1;
1735 }
1736
1737 return 0;
1738}
1739
1740/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001741 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001742 *
1743 * @param[in] src_diff Source diff node.
1744 * @param[in] diff_parent Current sysrepo diff parent.
1745 * @param[in] diff_cb Optional diff callback.
1746 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001747 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001748 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001749 * @param[in,out] diff Diff root node.
1750 * @return LY_ERR value.
1751 */
1752static LY_ERR
1753lyd_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 +02001754 struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001755{
1756 LY_ERR ret = LY_SUCCESS;
1757 struct lyd_node *child, *diff_node = NULL;
1758 enum lyd_diff_op src_op, cur_op;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001759 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001760
1761 /* get source node operation */
1762 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1763
1764 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001765 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 +02001766
1767 if (diff_node) {
1768 /* get target (current) operation */
1769 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1770
1771 /* merge operations */
1772 switch (src_op) {
1773 case LYD_DIFF_OP_REPLACE:
1774 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1775 break;
1776 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001777 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001778 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001779 goto add_diff;
1780 }
1781
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001782 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001783 break;
1784 case LYD_DIFF_OP_DELETE:
1785 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1786 break;
1787 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001788 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1789 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001790 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1791 break;
1792 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001793 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001794 }
1795 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001796 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001797 return ret;
1798 }
1799
1800 if (diff_cb) {
1801 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001802 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001803 }
1804
1805 /* update diff parent */
1806 diff_parent = diff_node;
1807
Michal Vaskoe78faec2021-04-08 17:24:43 +02001808 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1809 * so there is nothing to merge for these "keys" */
1810 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1811 /* merge src_diff recursively */
1812 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1813 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1814 if (ret) {
1815 break;
1816 }
1817 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001818 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001819 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001820 }
1821 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001822add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001823 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001824 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1825 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
1826 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1827 } else {
1828 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
1829 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1830 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001831
1832 /* insert node into diff if not already */
1833 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001834 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001835 }
1836
1837 /* update operation */
1838 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1839
1840 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001841 /* call callback with no source diff node since it was duplicated and just added */
1842 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001843 }
1844
1845 /* update diff parent */
1846 diff_parent = diff_node;
1847 }
1848
1849 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001850 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001851 if (diff_parent == *diff) {
1852 *diff = (*diff)->next;
1853 }
1854 lyd_free_tree(diff_parent);
1855 }
1856
1857 return LY_SUCCESS;
1858}
1859
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001860LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001861lyd_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 +01001862 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001863{
1864 const struct lyd_node *src_root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001865 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001866 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001867
1868 LY_LIST_FOR(src_diff, src_root) {
1869 if (mod && (lyd_owner_module(src_root) != mod)) {
1870 /* skip data nodes from different modules */
1871 continue;
1872 }
1873
1874 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001875 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 +02001876 }
1877
Michal Vaskoe78faec2021-04-08 17:24:43 +02001878cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001879 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001880 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001881}
1882
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001883LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001884lyd_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 +01001885 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001886{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001887 LY_ERR ret;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001888 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001889
Michal Vasko04f85912020-08-07 12:14:58 +02001890 if (!src_sibling) {
1891 return LY_SUCCESS;
1892 }
1893
Michal Vaskoe78faec2021-04-08 17:24:43 +02001894 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 +02001895 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001896 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001897}
1898
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001899LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001900lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001901{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001902 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001903}
Michal Vasko4231fb62020-07-13 13:54:47 +02001904
1905static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001906lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001907{
1908 LY_ERR ret = LY_SUCCESS;
1909 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001910 const char *val1 = NULL;
1911 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001912 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001913
Michal Vaskobaba84e2021-02-05 16:33:30 +01001914 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1915
1916 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001917 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001918
1919 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001920 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001921
1922 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001923 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001924 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001925 } else {
1926 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1927 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001928
1929 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001930 flags = node->flags;
1931 if (node->schema->nodetype == LYS_LEAF) {
1932 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1933 } else {
1934 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02001935
Michal Vaskobaba84e2021-02-05 16:33:30 +01001936 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1937 }
1938 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001939 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1940
1941cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001942 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001943 return ret;
1944}
1945
1946static LY_ERR
1947lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1948{
1949 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001950 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001951
1952 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001953 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001954
1955 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001956 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001957 flag1 = LYD_DEFAULT;
1958 } else {
1959 flag1 = 0;
1960 }
1961
1962 /* current default */
1963 flag2 = node->flags & LYD_DEFAULT;
1964
Michal Vasko610e93b2020-11-09 20:58:32 +01001965 if (flag1 == flag2) {
1966 /* no default state change so nothing to reverse */
1967 return LY_SUCCESS;
1968 }
1969
Michal Vasko4231fb62020-07-13 13:54:47 +02001970 /* switch defaults */
1971 node->flags &= ~LYD_DEFAULT;
1972 node->flags |= flag1;
1973 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1974
1975 return LY_SUCCESS;
1976}
1977
1978static LY_ERR
1979lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1980{
1981 LY_ERR ret = LY_SUCCESS;
1982 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001983 const char *val1 = NULL;
1984 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001985
1986 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001987 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001988
1989 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001990 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001991
1992 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001993 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02001994
1995 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001996 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02001997
1998 /* switch values */
1999 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2000 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2001
2002cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002003 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002004 return ret;
2005}
2006
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002007/**
2008 * @brief Remove specific operation from all the nodes in a subtree.
2009 *
2010 * @param[in] diff Diff subtree to process.
2011 * @param[in] op Only expected operation.
2012 * @return LY_ERR value.
2013 */
2014static LY_ERR
2015lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2016{
2017 struct lyd_node *elem;
2018 struct lyd_meta *meta;
2019
2020 LYD_TREE_DFS_BEGIN(diff, elem) {
2021 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2022 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002023 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 +01002024 lyd_free_meta_single(meta);
2025 }
2026
2027 LYD_TREE_DFS_END(diff, elem);
2028 }
2029
2030 return LY_SUCCESS;
2031}
2032
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002033LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002034lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002035{
2036 LY_ERR ret = LY_SUCCESS;
2037 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002038 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002039 enum lyd_diff_op op;
2040
2041 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2042
2043 if (!src_diff) {
2044 *diff = NULL;
2045 return LY_SUCCESS;
2046 }
2047
2048 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002049 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002050
2051 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002052 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2053 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002054
2055 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002056 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002057 /* skip all keys */
2058 if (!lysc_is_key(elem->schema)) {
2059 /* find operation attribute, if any */
2060 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002061
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002062 switch (op) {
2063 case LYD_DIFF_OP_CREATE:
2064 /* reverse create to delete */
2065 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002066
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002067 /* check all the children for the same operation, nothing else is expected */
2068 LY_LIST_FOR(lyd_child(elem), iter) {
2069 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2070 }
2071
Michal Vasko9e070522021-03-05 14:00:14 +01002072 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002073 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002074 case LYD_DIFF_OP_DELETE:
2075 /* reverse delete to create */
2076 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002077
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002078 /* check all the children for the same operation, nothing else is expected */
2079 LY_LIST_FOR(lyd_child(elem), iter) {
2080 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2081 }
2082
Michal Vasko9e070522021-03-05 14:00:14 +01002083 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002084 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002085 case LYD_DIFF_OP_REPLACE:
2086 switch (elem->schema->nodetype) {
2087 case LYS_LEAF:
2088 /* leaf value change */
2089 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2090 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2091 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002092 case LYS_ANYXML:
2093 case LYS_ANYDATA:
2094 /* any value change */
2095 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2096 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002097 case LYS_LEAFLIST:
2098 /* leaf-list move */
2099 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002100 if (lysc_is_dup_inst_list(elem->schema)) {
2101 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2102 } else {
2103 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2104 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002105 break;
2106 case LYS_LIST:
2107 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002108 if (lysc_is_dup_inst_list(elem->schema)) {
2109 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2110 } else {
2111 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2112 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002113 break;
2114 default:
2115 LOGINT(LYD_CTX(src_diff));
2116 ret = LY_EINT;
2117 goto cleanup;
2118 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002119 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002120 case LYD_DIFF_OP_NONE:
2121 switch (elem->schema->nodetype) {
2122 case LYS_LEAF:
2123 case LYS_LEAFLIST:
2124 /* default flag change */
2125 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2126 break;
2127 default:
2128 /* nothing to do */
2129 break;
2130 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002131 break;
2132 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002133 }
2134
Michal Vasko56daf732020-08-10 10:57:18 +02002135 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002136 }
2137 }
2138
2139cleanup:
2140 if (ret) {
2141 lyd_free_siblings(*diff);
2142 *diff = NULL;
2143 }
2144 return ret;
2145}