blob: 94b75081455389a91cfc50d8175a38e56ef8047b [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;
170 const struct lys_module *yang_mod;
171
172 assert(diff);
173
Michal Vasko53d48422020-11-13 18:02:29 +0100174 /* replace leaf always needs orig-default and orig-value */
175 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
176
177 /* create on userord needs key/value */
178 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200179 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100180 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200181 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100182
183 /* move on userord needs both key and orig-key/value and orig-value */
184 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200185 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100186 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200187 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
188 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100189
Michal Vaskod59035b2020-07-08 12:00:06 +0200190 /* find the first existing parent */
191 siblings = *diff;
192 while (1) {
193 /* find next node parent */
194 parent = node;
195 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100196 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200197 }
198 if (parent == node) {
199 /* no more parents to find */
200 break;
201 }
202
203 /* check whether it exists in the diff */
204 if (lyd_find_sibling_first(siblings, parent, &match)) {
205 break;
206 }
207
208 /* another parent found */
209 diff_parent = match;
210
211 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200212 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200213 }
214
215 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200216 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100217 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200218
219 /* find the first duplicated parent */
220 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100221 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200222 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100223 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200224 }
225 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100226 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200227 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100228 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200229 }
230 }
231
232 /* no parent existed, must be manually connected */
233 if (!diff_parent) {
234 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200235 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200236 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200237 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200238 }
239
240 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200241 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200242 assert(!strcmp(yang_mod->name, "yang"));
243
244 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200245 if (diff_parent && (diff_parent != dup)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100246 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200247 }
248
249 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100250 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200251
Michal Vaskocffc3f92022-06-15 07:57:24 +0200252 if (op == LYD_DIFF_OP_CREATE) {
253 /* all nested user-ordered (leaf-)lists need special metadata for create op */
254 LYD_TREE_DFS_BEGIN(dup, elem) {
255 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
256 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
257 }
258 LYD_TREE_DFS_END(dup, elem);
259 }
260 }
261
Michal Vaskod59035b2020-07-08 12:00:06 +0200262 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200263 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100264 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200265 }
266
267 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200268 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100269 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200270 }
271
272 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200273 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100274 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200275 }
276
277 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200278 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100279 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200280 }
281
Michal Vaskoe78faec2021-04-08 17:24:43 +0200282 /* position */
283 if (position) {
284 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "position", position, 0, NULL));
285 }
286
Michal Vaskod59035b2020-07-08 12:00:06 +0200287 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200288 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100289 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200290 }
291
Michal Vaskoe78faec2021-04-08 17:24:43 +0200292 /* orig-position */
293 if (orig_position) {
294 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-position", orig_position, 0, NULL));
295 }
296
Michal Vaskod59035b2020-07-08 12:00:06 +0200297 return LY_SUCCESS;
298}
299
300/**
301 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
302 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100303 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200304 * @param[in] schema Schema node of the list/leaf-list.
305 * @param[in,out] userord Sized array of userord items.
306 * @return Userord item for all the user-ordered list/leaf-list instances.
307 */
308static struct lyd_diff_userord *
309lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
310{
311 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200312 struct lyd_node *iter;
313 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200314 LY_ARRAY_COUNT_TYPE u;
315
316 LY_ARRAY_FOR(*userord, u) {
317 if ((*userord)[u].schema == schema) {
318 return &(*userord)[u];
319 }
320 }
321
322 /* it was not added yet, add it now */
323 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
324
325 item->schema = schema;
326 item->pos = 0;
327 item->inst = NULL;
328
329 /* store all the instance pointers in the current order */
330 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200331 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
332 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
333 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200334 }
335 }
336
337 return item;
338}
339
340/**
341 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
342 * lists/leaf-lists.
343 *
344 * @param[in] first Node from the first tree, can be NULL (on create).
345 * @param[in] second Node from the second tree, can be NULL (on delete).
346 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100347 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200348 * @param[out] op Operation.
349 * @param[out] orig_default Original default metadata.
350 * @param[out] value Value metadata.
351 * @param[out] orig_value Original value metadata
352 * @param[out] key Key metadata.
353 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200354 * @param[out] position Position metadata.
355 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200356 * @return LY_SUCCESS on success,
357 * @return LY_ENOT if there is no change to be added into diff,
358 * @return LY_ERR value on other errors.
359 */
360static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200361lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100362 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200363 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200364{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200365 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200366 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200367 size_t buflen, bufused;
368 uint32_t first_pos, second_pos;
Michal Vaskod59035b2020-07-08 12:00:06 +0200369
370 assert(first || second);
371
372 *orig_default = NULL;
373 *value = NULL;
374 *orig_value = NULL;
375 *key = NULL;
376 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200377 *position = NULL;
378 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200379
380 schema = first ? first->schema : second->schema;
381 assert(lysc_is_userordered(schema));
382
Michal Vaskod59035b2020-07-08 12:00:06 +0200383 /* find user-ordered first position */
384 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200385 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200386 if (userord_item->inst[first_pos] == first) {
387 break;
388 }
389 }
390 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
391 } else {
392 first_pos = 0;
393 }
394
Michal Vaskoe78faec2021-04-08 17:24:43 +0200395 /* prepare position of the next instance */
396 second_pos = userord_item->pos++;
397
Michal Vaskod59035b2020-07-08 12:00:06 +0200398 /* learn operation first */
399 if (!second) {
400 *op = LYD_DIFF_OP_DELETE;
401 } else if (!first) {
402 *op = LYD_DIFF_OP_CREATE;
403 } else {
Michal Vasko8f359bf2020-07-28 10:41:15 +0200404 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200405 /* in first, there is a different instance on the second position, we are going to move 'first' node */
406 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200407 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200408 /* default flag change */
409 *op = LYD_DIFF_OP_NONE;
410 } else {
411 /* no changes */
412 return LY_ENOT;
413 }
414 }
415
416 /*
417 * set each attribute correctly based on the operation and node type
418 */
419
420 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100421 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200422 if (first->flags & LYD_DEFAULT) {
423 *orig_default = "true";
424 } else {
425 *orig_default = "false";
426 }
427 }
428
429 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200430 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
431 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200432 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200433 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200434 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200435 } else {
436 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200437 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200438 }
439 }
440
441 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200442 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
443 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200444 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200445 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200446 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200447 } else {
448 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200449 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200450 }
451 }
452
453 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200454 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
455 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200456 if (second_pos) {
457 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200458 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 +0200459 } else {
460 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200461 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200462 }
463 }
464
465 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200466 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
467 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200468 if (first_pos) {
469 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200470 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 +0200471 } else {
472 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200473 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200474 }
475 }
476
Michal Vaskoe78faec2021-04-08 17:24:43 +0200477 /* position */
478 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
479 if (second_pos) {
480 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
481 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200482 rc = LY_EMEM;
483 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200484 }
485 } else {
486 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200487 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200488 }
489 }
490
491 /* orig-position */
492 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
493 if (first_pos) {
494 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
495 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200496 rc = LY_EMEM;
497 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200498 }
499 } else {
500 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200501 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200502 }
503 }
504
Michal Vaskod59035b2020-07-08 12:00:06 +0200505 /*
506 * update our instances - apply the change
507 */
508 if (*op == LYD_DIFF_OP_CREATE) {
509 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200510 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200511 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
512 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
513 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
514 }
515 LY_ARRAY_INCREMENT(userord_item->inst);
516 userord_item->inst[second_pos] = second;
517
518 } else if (*op == LYD_DIFF_OP_DELETE) {
519 /* remove the instance */
520 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
521 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
522 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
523 }
524 LY_ARRAY_DECREMENT(userord_item->inst);
525
526 } else if (*op == LYD_DIFF_OP_REPLACE) {
527 /* move the instances */
528 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
529 (first_pos - second_pos) * sizeof *userord_item->inst);
530 userord_item->inst[second_pos] = first;
531 }
532
Michal Vaskof9b052a2022-06-08 10:26:53 +0200533cleanup:
534 if (rc) {
535 free(*value);
536 *value = NULL;
537 free(*orig_value);
538 *orig_value = NULL;
539 free(*key);
540 *key = NULL;
541 free(*orig_key);
542 *orig_key = NULL;
543 free(*position);
544 *position = NULL;
545 free(*orig_position);
546 *orig_position = NULL;
547 }
548 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200549}
550
551/**
552 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
553 * lists/leaf-lists.
554 *
555 * @param[in] first Node from the first tree, can be NULL (on create).
556 * @param[in] second Node from the second tree, can be NULL (on delete).
557 * @param[in] options Diff options.
558 * @param[out] op Operation.
559 * @param[out] orig_default Original default metadata.
560 * @param[out] orig_value Original value metadata.
561 * @return LY_SUCCESS on success,
562 * @return LY_ENOT if there is no change to be added into diff,
563 * @return LY_ERR value on other errors.
564 */
565static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200566lyd_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 +0200567 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200568{
569 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200570 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200571
572 assert(first || second);
573
574 *orig_default = NULL;
575 *orig_value = NULL;
576
577 schema = first ? first->schema : second->schema;
578 assert(!lysc_is_userordered(schema));
579
580 /* learn operation first */
581 if (!second) {
582 *op = LYD_DIFF_OP_DELETE;
583 } else if (!first) {
584 *op = LYD_DIFF_OP_CREATE;
585 } else {
586 switch (schema->nodetype) {
587 case LYS_CONTAINER:
588 case LYS_RPC:
589 case LYS_ACTION:
590 case LYS_NOTIF:
591 /* no changes */
592 return LY_ENOT;
593 case LYS_LIST:
594 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200595 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200596 /* default flag change */
597 *op = LYD_DIFF_OP_NONE;
598 } else {
599 /* no changes */
600 return LY_ENOT;
601 }
602 break;
603 case LYS_LEAF:
604 case LYS_ANYXML:
605 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200606 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200607 /* different values */
608 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200609 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200610 /* default flag change */
611 *op = LYD_DIFF_OP_NONE;
612 } else {
613 /* no changes */
614 return LY_ENOT;
615 }
616 break;
617 default:
618 LOGINT_RET(schema->module->ctx);
619 }
620 }
621
622 /*
623 * set each attribute correctly based on the operation and node type
624 */
625
626 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100627 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200628 if (first->flags & LYD_DEFAULT) {
629 *orig_default = "true";
630 } else {
631 *orig_default = "false";
632 }
633 }
634
635 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100636 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
637 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200638 str_val = lyd_get_value(first);
639 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100640 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
641 } else {
642 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
643 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200644 }
645
646 return LY_SUCCESS;
647}
648
649/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200650 * @brief Find a matching instance of a node in a data tree.
651 *
652 * @param[in] siblings Siblings to search in.
653 * @param[in] target Target node to search for.
654 * @param[in] defaults Whether to consider (or ignore) default values.
655 * @param[in,out] dup_inst_cache Duplicate instance cache.
656 * @param[out] match Found match, NULL if no matching node found.
657 * @return LY_ERR value.
658 */
659static LY_ERR
660lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200661 struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200662{
Michal Vaskoe78faec2021-04-08 17:24:43 +0200663 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
664 /* try to find the exact instance */
665 lyd_find_sibling_first(siblings, target, match);
666 } else {
667 /* try to simply find the node, there cannot be more instances */
668 lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
669 }
670
Michal Vaskod7c048c2021-05-18 16:12:55 +0200671 /* update match as needed */
672 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200673
674 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
675 /* ignore default nodes */
676 *match = NULL;
677 }
678 return LY_SUCCESS;
679}
680
681/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200682 * @brief Perform diff for all siblings at certain depth, recursively.
683 *
684 * For user-ordered lists/leaf-lists a specific structure is used for storing
685 * the current order. The idea is to apply all the generated diff changes
686 * virtually on the first tree so that we can continue to generate correct
687 * changes after some were already generated.
688 *
689 * The algorithm then uses second tree position-based changes with a before
690 * (preceding) item anchor.
691 *
692 * Example:
693 *
694 * Virtual first tree leaf-list order:
695 * 1 2 [3] 4 5
696 *
697 * Second tree leaf-list order:
698 * 1 2 [5] 3 4
699 *
700 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
701 * match - they do not - move nodes so that the 3rd position node is final ->
702 * -> move node 5 to the 3rd position -> move node 5 after node 2.
703 *
704 * Required properties:
705 * Stored operations (move) should not be affected by later operations -
706 * - would cause a redundantly long list of operations, possibly inifinite.
707 *
708 * Implemenation justification:
709 * First, all delete operations and only then move/create operations are stored.
710 * Also, preceding anchor is used and after each iteration another node is
711 * at its final position. That results in the invariant that all preceding
712 * nodes are final and will not be changed by the later operations, meaning
713 * they can safely be used as anchors for the later operations.
714 *
715 * @param[in] first First tree first sibling.
716 * @param[in] second Second tree first sibling.
717 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200718 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200719 * @param[in,out] diff Diff to append to.
720 * @return LY_ERR value.
721 */
722static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200723lyd_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 +0200724 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200725{
726 LY_ERR ret = LY_SUCCESS;
727 const struct lyd_node *iter_first, *iter_second;
728 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100729 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200730 struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200731 LY_ARRAY_COUNT_TYPE u;
732 enum lyd_diff_op op;
733 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200734 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200735
Michal Vaskod59035b2020-07-08 12:00:06 +0200736 /* compare first tree to the second tree - delete, replace, none */
737 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200738 if (!iter_first->schema) {
739 continue;
740 }
741
Michal Vaskod59035b2020-07-08 12:00:06 +0200742 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200743 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200744 /* skip default nodes */
745 continue;
746 }
747
Michal Vaskoe78faec2021-04-08 17:24:43 +0200748 /* find a match in the second tree */
749 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
750 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200751
752 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100753 /* get (create) userord entry */
754 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
755 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
756
Michal Vaskoe78faec2021-04-08 17:24:43 +0200757 /* we are handling only user-ordered node delete now */
758 if (!match_second) {
759 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100760 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
761 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200762
763 /* there must be changes, it is deleted */
764 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100765 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
766 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200767
768 free(orig_value);
769 free(key);
770 free(value);
771 free(position);
772 free(orig_key);
773 free(orig_position);
774 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200775 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200776 } else {
777 /* get all the attributes */
778 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
779
780 /* add into diff if there are any changes */
781 if (!ret) {
782 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200783 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200784 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100785 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200786 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200787 }
788
789 free(orig_value);
790 LY_CHECK_GOTO(ret, cleanup);
791 } else if (ret == LY_ENOT) {
792 ret = LY_SUCCESS;
793 } else {
794 goto cleanup;
795 }
796 }
797
798 /* check descendants, if any, recursively */
799 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200800 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
801 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200802 }
803
804 if (nosiblings) {
805 break;
806 }
807 }
808
809 /* reset all cached positions */
810 LY_ARRAY_FOR(userord, u) {
811 userord[u].pos = 0;
812 }
813
814 /* compare second tree to the first tree - create, user-ordered move */
815 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200816 if (!iter_second->schema) {
817 continue;
818 }
819
Michal Vaskod59035b2020-07-08 12:00:06 +0200820 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200821 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200822 /* skip default nodes */
823 continue;
824 }
825
Michal Vaskoe78faec2021-04-08 17:24:43 +0200826 /* find a match in the first tree */
827 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
828 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200829
830 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100831 /* get userord entry */
832 userord_item = lyd_diff_userord_get(NULL, iter_second->schema, &userord);
833 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
834
Michal Vaskod59035b2020-07-08 12:00:06 +0200835 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100836 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200837 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200838
839 /* add into diff if there are any changes */
840 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200841 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
842 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200843
844 free(orig_value);
845 free(key);
846 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200847 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200848 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200849 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200850 LY_CHECK_GOTO(ret, cleanup);
851 } else if (ret == LY_ENOT) {
852 ret = LY_SUCCESS;
853 } else {
854 goto cleanup;
855 }
856 } else if (!match_first) {
857 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200858 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 +0200859
860 /* there must be changes, it is created */
861 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200862 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200863
864 free(orig_value);
865 LY_CHECK_GOTO(ret, cleanup);
866 } /* else was handled */
867
868 if (nosiblings) {
869 break;
870 }
871 }
872
873cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200874 lyd_dup_inst_free(dup_inst_first);
875 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200876 LY_ARRAY_FOR(userord, u) {
877 LY_ARRAY_FREE(userord[u].inst);
878 }
879 LY_ARRAY_FREE(userord);
880 return ret;
881}
882
Michal Vasko3a41dff2020-07-15 14:30:28 +0200883static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +0100884lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
885 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200886{
887 const struct ly_ctx *ctx;
888
889 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
890
891 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200892 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200893 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200894 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200895 } else {
896 ctx = NULL;
897 }
898
899 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
900 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
901 return LY_EINVAL;
902 }
903
904 *diff = NULL;
905
Michal Vasko3a41dff2020-07-15 14:30:28 +0200906 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
907}
908
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100909LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200910lyd_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 +0200911{
912 return lyd_diff(first, second, options, 1, diff);
913}
914
Jan Kundrátc53a7ec2021-12-09 16:01:19 +0100915LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200916lyd_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 +0200917{
918 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200919}
920
921/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200922 * @brief Learn operation of a diff node.
923 *
924 * @param[in] diff_node Diff node.
925 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200926 * @return LY_ERR value.
927 */
928static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200929lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200930{
931 struct lyd_meta *meta = NULL;
932 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200933 const char *str;
Michal Vasko52afd7d2022-01-18 14:08:34 +0100934 char *path;
Michal Vaskod59035b2020-07-08 12:00:06 +0200935
Michal Vasko9e685082021-01-29 14:49:09 +0100936 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200937 LY_LIST_FOR(diff_parent->meta, meta) {
938 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200939 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200940 if ((str[0] == 'r') && (diff_parent != diff_node)) {
941 /* we do not care about this operation if it's in our parent */
942 continue;
943 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200944 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200945 break;
946 }
947 }
948 if (meta) {
949 break;
950 }
951 }
Michal Vasko52afd7d2022-01-18 14:08:34 +0100952
953 if (!meta) {
954 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
955 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
956 free(path);
957 return LY_EINT;
958 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200959
Michal Vaskod59035b2020-07-08 12:00:06 +0200960 return LY_SUCCESS;
961}
962
963/**
964 * @brief Insert a diff node into a data tree.
965 *
966 * @param[in,out] first_node First sibling of the data tree.
967 * @param[in] parent_node Data tree sibling parent node.
968 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200969 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
970 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200971 * @return err_info, NULL on success.
972 */
973static LY_ERR
974lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200975 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200976{
977 LY_ERR ret;
978 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200979 uint32_t pos, anchor_pos;
980 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200981
982 assert(new_node);
983
984 if (!*first_node) {
985 if (!parent_node) {
986 /* no parent or siblings */
987 *first_node = new_node;
988 return LY_SUCCESS;
989 }
990
991 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200992 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200993 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200994 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200995 return LY_EINVAL;
996 }
Michal Vaskob104f112020-07-17 09:54:54 +0200997 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200998 }
999
Michal Vasko9e685082021-01-29 14:49:09 +01001000 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001001
Michal Vaskod59035b2020-07-08 12:00:06 +02001002 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +02001003 /* simple insert */
1004 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001005 }
1006
Michal Vaskoe78faec2021-04-08 17:24:43 +02001007 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001008 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001009 if (lysc_is_dup_inst_list(new_node->schema)) {
1010 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001011 if (!anchor_pos) {
1012 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1013 return LY_EINVAL;
1014 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001015
1016 found = 0;
1017 pos = 1;
1018 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1019 if (pos == anchor_pos) {
1020 found = 1;
1021 break;
1022 }
1023 ++pos;
1024 }
1025 if (!found) {
1026 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1027 new_node->schema->name);
1028 return LY_EINVAL;
1029 }
1030 } else {
1031 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1032 if (ret == LY_ENOTFOUND) {
1033 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1034 new_node->schema->name);
1035 return LY_EINVAL;
1036 } else if (ret) {
1037 return ret;
1038 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001039 }
1040
1041 /* insert after */
1042 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1043 assert(new_node->prev == anchor);
1044 if (*first_node == new_node) {
1045 *first_node = anchor;
1046 }
1047 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001048 /* find the first instance */
1049 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1050 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001051
Michal Vaskoea7d3232022-04-19 12:01:36 +02001052 if (anchor) {
1053 /* insert before the first instance */
1054 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1055 if ((*first_node)->prev->next) {
1056 assert(!new_node->prev->next);
1057 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001058 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001059 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001060 /* insert anywhere */
1061 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001062 }
1063 }
1064
1065 return LY_SUCCESS;
1066}
1067
1068/**
1069 * @brief Apply diff subtree on data tree nodes, recursively.
1070 *
1071 * @param[in,out] first_node First sibling of the data tree.
1072 * @param[in] parent_node Parent of the first sibling.
1073 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001074 * @param[in] diff_cb Optional diff callback.
1075 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001076 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001077 * @return LY_ERR value.
1078 */
1079static LY_ERR
1080lyd_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 +02001081 lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001082{
1083 LY_ERR ret;
1084 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001085 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001086 enum lyd_diff_op op;
1087 struct lyd_meta *meta;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001088 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001089 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001090
1091 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001092 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001093
Michal Vaskoe6323f62020-07-09 15:49:02 +02001094 /* handle specific user-ordered (leaf-)lists operations separately */
1095 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1096 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001097 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001098 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001099 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001100 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001101 /* duplicate the node */
1102 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001103 }
1104
Michal Vaskoe78faec2021-04-08 17:24:43 +02001105 /* get "key", "value", or "position" metadata string value */
1106 if (lysc_is_dup_inst_list(diff_node->schema)) {
1107 meta_str = "yang:position";
1108 } else if (diff_node->schema->nodetype == LYS_LIST) {
1109 meta_str = "yang:key";
1110 } else {
1111 meta_str = "yang:value";
1112 }
1113 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001114 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001115 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001116
Michal Vaskod59035b2020-07-08 12:00:06 +02001117 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001118 if (str_val[0]) {
1119 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001120 } else {
1121 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1122 }
1123 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001124 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001125 lyd_free_tree(match);
1126 }
1127 return ret;
1128 }
1129
1130 goto next_iter_r;
1131 }
1132
1133 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001134 switch (op) {
1135 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001136 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001137 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001138 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001139
1140 if (match->schema->nodetype & LYD_NODE_TERM) {
1141 /* special case of only dflt flag change */
1142 if (diff_node->flags & LYD_DEFAULT) {
1143 match->flags |= LYD_DEFAULT;
1144 } else {
1145 match->flags &= ~LYD_DEFAULT;
1146 }
1147 } else {
1148 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001149 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001150 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1151 LYD_NAME(diff_node));
1152 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001153 }
1154 }
1155 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001156 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001157 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001158 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001159
1160 /* insert it at the end */
1161 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001162 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001163 if (match->flags & LYD_EXT) {
1164 ret = lyd_insert_ext(parent_node, match);
1165 } else {
1166 ret = lyd_insert_child(parent_node, match);
1167 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001168 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001169 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001170 }
1171 if (ret) {
1172 lyd_free_tree(match);
1173 return ret;
1174 }
1175
1176 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001177 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001178 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001179 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001180 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001181
1182 /* remove it */
1183 if ((match == *first_node) && !match->parent) {
1184 assert(!parent_node);
1185 /* we have removed the top-level node */
1186 *first_node = (*first_node)->next;
1187 }
1188 lyd_free_tree(match);
1189
1190 /* we are not going recursively in this case, the whole subtree was already deleted */
1191 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001192 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001193 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1194 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1195 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1196 return LY_EINVAL;
1197 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001198
1199 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001200 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001201 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001202
Michal Vaskobaba84e2021-02-05 16:33:30 +01001203 /* update the value */
1204 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001205 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001206 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001207 } else {
1208 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001209 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001210 }
1211
1212 /* with flags */
1213 match->flags = diff_node->flags;
1214 break;
1215 default:
1216 LOGINT_RET(ctx);
1217 }
1218
1219next_iter_r:
1220 if (diff_cb) {
1221 /* call callback */
1222 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1223 }
1224
1225 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001226 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001227 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001228 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1229 if (ret) {
1230 break;
1231 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001232 }
1233
Michal Vaskod7c048c2021-05-18 16:12:55 +02001234 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001235 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001236}
1237
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001238LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001239lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001240 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001241{
1242 const struct lyd_node *root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001243 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001244 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001245
1246 LY_LIST_FOR(diff, root) {
1247 if (mod && (lyd_owner_module(root) != mod)) {
1248 /* skip data nodes from different modules */
1249 continue;
1250 }
1251
1252 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001253 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1254 if (ret) {
1255 break;
1256 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001257 }
1258
Michal Vaskod7c048c2021-05-18 16:12:55 +02001259 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001260 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001261}
1262
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001263LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001264lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001265{
1266 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1267}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001268
1269/**
1270 * @brief Update operations on a diff node when the new operation is NONE.
1271 *
1272 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001273 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001274 * @param[in] src_diff Current source diff node.
1275 * @return LY_ERR value.
1276 */
1277static LY_ERR
1278lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1279{
1280 switch (cur_op) {
1281 case LYD_DIFF_OP_NONE:
1282 case LYD_DIFF_OP_CREATE:
1283 case LYD_DIFF_OP_REPLACE:
1284 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1285 /* NONE on a term means only its dflt flag was changed */
1286 diff_match->flags &= ~LYD_DEFAULT;
1287 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1288 }
1289 break;
1290 default:
1291 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001292 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1293 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001294 }
1295
1296 return LY_SUCCESS;
1297}
1298
1299/**
1300 * @brief Remove an attribute from a node.
1301 *
1302 * @param[in] node Node with the metadata.
1303 * @param[in] name Metadata name.
1304 */
1305static void
1306lyd_diff_del_meta(struct lyd_node *node, const char *name)
1307{
1308 struct lyd_meta *meta;
1309
1310 LY_LIST_FOR(node->meta, meta) {
1311 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001312 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001313 return;
1314 }
1315 }
1316
1317 assert(0);
1318}
1319
1320/**
1321 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001322 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001323 *
1324 * @param[in] node Node to change.
1325 * @param[in] op Operation to set.
1326 * @return LY_ERR value.
1327 */
1328static LY_ERR
1329lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1330{
1331 struct lyd_meta *meta;
1332
1333 LY_LIST_FOR(node->meta, meta) {
1334 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001335 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001336 break;
1337 }
1338 }
1339
Michal Vasko871a0252020-11-11 18:35:24 +01001340 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001341}
1342
1343/**
1344 * @brief Update operations on a diff node when the new operation is REPLACE.
1345 *
1346 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001347 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001348 * @param[in] src_diff Current source diff node.
1349 * @return LY_ERR value.
1350 */
1351static LY_ERR
1352lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1353{
1354 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001355 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001356 struct lyd_meta *meta;
1357 const struct lys_module *mod;
1358 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001359 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001360
1361 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001362 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001363 assert(mod);
1364
1365 switch (cur_op) {
1366 case LYD_DIFF_OP_REPLACE:
1367 case LYD_DIFF_OP_CREATE:
1368 switch (diff_match->schema->nodetype) {
1369 case LYS_LIST:
1370 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001371 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001372 * keep orig_key/orig_value (only replace oper) and replace key/value */
1373 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001374 if (lysc_is_dup_inst_list(diff_match->schema)) {
1375 meta_name = "position";
1376 } else if (diff_match->schema->nodetype == LYS_LIST) {
1377 meta_name = "key";
1378 } else {
1379 meta_name = "value";
1380 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001381
1382 lyd_diff_del_meta(diff_match, meta_name);
1383 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001384 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001385 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001386 break;
1387 case LYS_LEAF:
1388 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001389 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001390 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1391 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001392 }
1393
Michal Vaskoe6323f62020-07-09 15:49:02 +02001394 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001395 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001396 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001397 }
1398
Michal Vasko8caadab2020-11-05 17:38:15 +01001399 if (cur_op == LYD_DIFF_OP_REPLACE) {
1400 /* compare values whether there is any change at all */
1401 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001402 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001403 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001404 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1405 if (!ret) {
1406 /* values are the same, remove orig-value meta and set oper to NONE */
1407 lyd_free_meta_single(meta);
1408 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1409 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001410 }
1411
1412 /* modify the default flag */
1413 diff_match->flags &= ~LYD_DEFAULT;
1414 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1415 break;
1416 case LYS_ANYXML:
1417 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001418 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001419 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1420 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001421 }
1422
1423 /* modify the node value */
1424 any = (struct lyd_node_any *)src_diff;
1425 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1426 break;
1427 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001428 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001429 }
1430 break;
1431 case LYD_DIFF_OP_NONE:
1432 /* it is moved now */
1433 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1434
1435 /* change the operation */
1436 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1437
Michal Vaskoe78faec2021-04-08 17:24:43 +02001438 /* set orig-meta and meta */
1439 if (lysc_is_dup_inst_list(diff_match->schema)) {
1440 meta_name = "position";
1441 orig_meta_name = "orig-position";
1442 } else {
1443 meta_name = "key";
1444 orig_meta_name = "orig-key";
1445 }
1446
1447 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001448 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_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
Michal Vaskoe78faec2021-04-08 17:24:43 +02001451 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001452 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001453 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001454 break;
1455 default:
1456 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001457 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1458 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001459 }
1460
1461 return LY_SUCCESS;
1462}
1463
1464/**
1465 * @brief Update operations in a diff node when the new operation is CREATE.
1466 *
1467 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001468 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001469 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001470 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001471 * @return LY_ERR value.
1472 */
1473static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001474lyd_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 +02001475{
1476 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001477 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001478 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001479 const char *meta_name, *orig_meta_name;
1480 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001481 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001482
1483 switch (cur_op) {
1484 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001485 /* remember current flags */
1486 trg_flags = diff_match->flags;
1487
Michal Vaskoe78faec2021-04-08 17:24:43 +02001488 if (lysc_is_userordered(diff_match->schema)) {
1489 /* get anchor metadata */
1490 if (lysc_is_dup_inst_list(diff_match->schema)) {
1491 meta_name = "yang:position";
1492 orig_meta_name = "yang:orig-position";
1493 } else if (diff_match->schema->nodetype == LYS_LIST) {
1494 meta_name = "yang:key";
1495 orig_meta_name = "yang:orig-key";
1496 } else {
1497 meta_name = "yang:value";
1498 orig_meta_name = "yang:orig-value";
1499 }
1500 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001501 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001502 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001503 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001504
1505 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1506 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001507 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001508 /* deleted + created at another position -> operation REPLACE */
1509 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1510
1511 /* add anchor metadata */
1512 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1513 } else {
1514 /* deleted + created at the same position -> operation NONE */
1515 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1516
1517 /* delete anchor metadata */
1518 lyd_free_meta_single(orig_meta);
1519 }
1520 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1521 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1522 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1523 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1524 }
1525
Radek Krejci55c4bd22021-04-26 08:09:04 +02001526 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1527 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001528 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1529 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1530 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1531 /* deleted + created -> operation NONE */
1532 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1533 } else {
1534 /* we deleted it, but it was created with a different value -> operation REPLACE */
1535 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1536
1537 /* current value is the previous one (meta) */
1538 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001539 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001540
1541 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001542 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001543 }
1544 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001545 /* deleted + created -> operation NONE */
1546 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001547 }
1548
1549 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001550 /* add orig-dflt metadata */
1551 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1552 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1553
Michal Vaskoe6323f62020-07-09 15:49:02 +02001554 /* update dflt flag itself */
1555 diff_match->flags &= ~LYD_DEFAULT;
1556 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001557 }
1558
1559 /* but the operation of its children should remain DELETE */
1560 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1561 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001562 }
1563 break;
1564 default:
1565 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001566 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1567 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001568 }
1569
1570 return LY_SUCCESS;
1571}
1572
1573/**
1574 * @brief Update operations on a diff node when the new operation is DELETE.
1575 *
1576 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001577 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001578 * @param[in] src_diff Current source diff node.
1579 * @return LY_ERR value.
1580 */
1581static LY_ERR
1582lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1583{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001584 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001585 struct lyd_meta *meta;
1586 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001587 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001588
1589 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001590 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 +02001591
1592 switch (cur_op) {
1593 case LYD_DIFF_OP_CREATE:
1594 /* it was created, but then deleted -> set NONE operation */
1595 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1596
1597 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1598 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001599 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1600 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001601 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001602 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001603 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001604 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1605 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001606 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001607 break;
1608 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001609 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001610 if (lysc_is_userordered(diff_match->schema)) {
1611 if (lysc_is_dup_inst_list(diff_match->schema)) {
1612 meta_name = "position";
1613 } else if (diff_match->schema->nodetype == LYS_LIST) {
1614 meta_name = "key";
1615 } else {
1616 meta_name = "value";
1617 }
1618 } else {
1619 assert(diff_match->schema->nodetype == LYS_LEAF);
1620
1621 /* switch value for the original one */
1622 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001623 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001624 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001625 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1626 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001627 }
1628
1629 /* switch default for the original one, then remove the meta */
1630 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001631 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001632 diff_match->flags &= ~LYD_DEFAULT;
1633 if (meta->value.boolean) {
1634 diff_match->flags |= LYD_DEFAULT;
1635 }
1636 lyd_free_meta_single(meta);
1637
1638 meta_name = "orig-value";
1639 }
1640 lyd_diff_del_meta(diff_match, meta_name);
1641
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001642 /* it was being changed, but should be deleted instead -> set DELETE operation */
1643 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1644 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001645 case LYD_DIFF_OP_NONE:
1646 /* it was not modified, but should be deleted -> set DELETE operation */
1647 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001648 break;
1649 default:
1650 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001651 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1652 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001653 }
1654
1655 return LY_SUCCESS;
1656}
1657
1658/**
1659 * @brief Check whether this diff node is redundant (does not change data).
1660 *
1661 * @param[in] diff Diff node.
1662 * @return 0 if not, non-zero if it is.
1663 */
1664static int
1665lyd_diff_is_redundant(struct lyd_node *diff)
1666{
1667 enum lyd_diff_op op;
1668 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1669 struct lyd_node *child;
1670 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001671 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001672
1673 assert(diff);
1674
Michal Vaskoe78faec2021-04-08 17:24:43 +02001675 if (lysc_is_dup_inst_list(diff->schema)) {
1676 /* all descendants are keys */
1677 child = NULL;
1678 } else {
1679 child = lyd_child_no_keys(diff);
1680 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001681 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001682 assert(mod);
1683
1684 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001685 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001686
1687 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001688 /* get metadata names */
1689 if (lysc_is_dup_inst_list(diff->schema)) {
1690 meta_name = "position";
1691 orig_meta_name = "orig-position";
1692 } else if (diff->schema->nodetype == LYS_LIST) {
1693 meta_name = "key";
1694 orig_meta_name = "orig-key";
1695 } else {
1696 meta_name = "value";
1697 orig_meta_name = "orig-value";
1698 }
1699
Michal Vaskoe6323f62020-07-09 15:49:02 +02001700 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001701 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1702 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001703 assert(orig_val_meta && val_meta);
1704
1705 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1706 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001707 lyd_free_meta_single(orig_val_meta);
1708 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001709 if (child) {
1710 /* change operation to NONE, we have siblings */
1711 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1712 return 0;
1713 }
1714
1715 /* redundant node, BUT !!
1716 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1717 * because the data that this is applied on should not change for the diff lifetime.
1718 * However, when we are merging 2 diffs, this conversion is actually lossy because
1719 * if the data change, the move operation can also change its meaning. In this specific
1720 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1721 */
1722 return 1;
1723 }
1724 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1725 /* check whether at least the default flags are different */
1726 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1727 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001728 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001729
1730 /* if previous and current dflt flags are the same, this node is redundant */
1731 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1732 return 1;
1733 }
1734 return 0;
1735 }
1736
1737 if (!child && (op == LYD_DIFF_OP_NONE)) {
1738 return 1;
1739 }
1740
1741 return 0;
1742}
1743
1744/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001745 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001746 *
1747 * @param[in] src_diff Source diff node.
1748 * @param[in] diff_parent Current sysrepo diff parent.
1749 * @param[in] diff_cb Optional diff callback.
1750 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001751 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001752 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001753 * @param[in,out] diff Diff root node.
1754 * @return LY_ERR value.
1755 */
1756static LY_ERR
1757lyd_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 +02001758 struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001759{
1760 LY_ERR ret = LY_SUCCESS;
1761 struct lyd_node *child, *diff_node = NULL;
1762 enum lyd_diff_op src_op, cur_op;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001763 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001764
1765 /* get source node operation */
1766 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1767
1768 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001769 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 +02001770
1771 if (diff_node) {
1772 /* get target (current) operation */
1773 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1774
1775 /* merge operations */
1776 switch (src_op) {
1777 case LYD_DIFF_OP_REPLACE:
1778 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1779 break;
1780 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001781 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001782 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001783 goto add_diff;
1784 }
1785
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001786 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001787 break;
1788 case LYD_DIFF_OP_DELETE:
1789 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1790 break;
1791 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001792 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1793 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001794 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1795 break;
1796 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001797 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001798 }
1799 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001800 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001801 return ret;
1802 }
1803
1804 if (diff_cb) {
1805 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001806 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001807 }
1808
1809 /* update diff parent */
1810 diff_parent = diff_node;
1811
Michal Vaskoe78faec2021-04-08 17:24:43 +02001812 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1813 * so there is nothing to merge for these "keys" */
1814 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1815 /* merge src_diff recursively */
1816 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1817 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1818 if (ret) {
1819 break;
1820 }
1821 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001822 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001823 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001824 }
1825 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001826add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001827 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001828 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1829 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
1830 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1831 } else {
1832 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
1833 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1834 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001835
1836 /* insert node into diff if not already */
1837 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001838 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001839 }
1840
1841 /* update operation */
1842 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1843
1844 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001845 /* call callback with no source diff node since it was duplicated and just added */
1846 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001847 }
1848
1849 /* update diff parent */
1850 diff_parent = diff_node;
1851 }
1852
1853 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001854 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001855 if (diff_parent == *diff) {
1856 *diff = (*diff)->next;
1857 }
1858 lyd_free_tree(diff_parent);
1859 }
1860
1861 return LY_SUCCESS;
1862}
1863
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001864LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001865lyd_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 +01001866 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001867{
1868 const struct lyd_node *src_root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001869 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001870 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001871
1872 LY_LIST_FOR(src_diff, src_root) {
1873 if (mod && (lyd_owner_module(src_root) != mod)) {
1874 /* skip data nodes from different modules */
1875 continue;
1876 }
1877
1878 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001879 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 +02001880 }
1881
Michal Vaskoe78faec2021-04-08 17:24:43 +02001882cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001883 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001884 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001885}
1886
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001887LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001888lyd_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 +01001889 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001890{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001891 LY_ERR ret;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001892 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001893
Michal Vasko04f85912020-08-07 12:14:58 +02001894 if (!src_sibling) {
1895 return LY_SUCCESS;
1896 }
1897
Michal Vaskoe78faec2021-04-08 17:24:43 +02001898 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 +02001899 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001900 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001901}
1902
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001903LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001904lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001905{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001906 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001907}
Michal Vasko4231fb62020-07-13 13:54:47 +02001908
1909static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001910lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001911{
1912 LY_ERR ret = LY_SUCCESS;
1913 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001914 const char *val1 = NULL;
1915 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001916 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001917
Michal Vaskobaba84e2021-02-05 16:33:30 +01001918 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1919
1920 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001921 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001922
1923 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001924 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001925
1926 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001927 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001928 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001929 } else {
1930 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1931 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001932
1933 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001934 flags = node->flags;
1935 if (node->schema->nodetype == LYS_LEAF) {
1936 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1937 } else {
1938 union lyd_any_value anyval = {.str = val1};
1939 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1940 }
1941 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001942 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1943
1944cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001945 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001946 return ret;
1947}
1948
1949static LY_ERR
1950lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1951{
1952 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001953 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001954
1955 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001956 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001957
1958 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001959 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001960 flag1 = LYD_DEFAULT;
1961 } else {
1962 flag1 = 0;
1963 }
1964
1965 /* current default */
1966 flag2 = node->flags & LYD_DEFAULT;
1967
Michal Vasko610e93b2020-11-09 20:58:32 +01001968 if (flag1 == flag2) {
1969 /* no default state change so nothing to reverse */
1970 return LY_SUCCESS;
1971 }
1972
Michal Vasko4231fb62020-07-13 13:54:47 +02001973 /* switch defaults */
1974 node->flags &= ~LYD_DEFAULT;
1975 node->flags |= flag1;
1976 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1977
1978 return LY_SUCCESS;
1979}
1980
1981static LY_ERR
1982lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1983{
1984 LY_ERR ret = LY_SUCCESS;
1985 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001986 const char *val1 = NULL;
1987 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001988
1989 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001990 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001991
1992 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001993 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02001994
1995 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001996 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02001997
1998 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001999 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02002000
2001 /* switch values */
2002 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2003 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2004
2005cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002006 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002007 return ret;
2008}
2009
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002010/**
2011 * @brief Remove specific operation from all the nodes in a subtree.
2012 *
2013 * @param[in] diff Diff subtree to process.
2014 * @param[in] op Only expected operation.
2015 * @return LY_ERR value.
2016 */
2017static LY_ERR
2018lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2019{
2020 struct lyd_node *elem;
2021 struct lyd_meta *meta;
2022
2023 LYD_TREE_DFS_BEGIN(diff, elem) {
2024 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2025 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002026 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 +01002027 lyd_free_meta_single(meta);
2028 }
2029
2030 LYD_TREE_DFS_END(diff, elem);
2031 }
2032
2033 return LY_SUCCESS;
2034}
2035
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002036LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002037lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002038{
2039 LY_ERR ret = LY_SUCCESS;
2040 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002041 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002042 enum lyd_diff_op op;
2043
2044 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2045
2046 if (!src_diff) {
2047 *diff = NULL;
2048 return LY_SUCCESS;
2049 }
2050
2051 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002052 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002053
2054 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002055 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2056 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002057
2058 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002059 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002060 /* skip all keys */
2061 if (!lysc_is_key(elem->schema)) {
2062 /* find operation attribute, if any */
2063 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002064
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002065 switch (op) {
2066 case LYD_DIFF_OP_CREATE:
2067 /* reverse create to delete */
2068 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002069
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002070 /* check all the children for the same operation, nothing else is expected */
2071 LY_LIST_FOR(lyd_child(elem), iter) {
2072 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2073 }
2074
Michal Vasko9e070522021-03-05 14:00:14 +01002075 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002076 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002077 case LYD_DIFF_OP_DELETE:
2078 /* reverse delete to create */
2079 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002080
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002081 /* check all the children for the same operation, nothing else is expected */
2082 LY_LIST_FOR(lyd_child(elem), iter) {
2083 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2084 }
2085
Michal Vasko9e070522021-03-05 14:00:14 +01002086 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002087 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002088 case LYD_DIFF_OP_REPLACE:
2089 switch (elem->schema->nodetype) {
2090 case LYS_LEAF:
2091 /* leaf value change */
2092 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2093 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2094 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002095 case LYS_ANYXML:
2096 case LYS_ANYDATA:
2097 /* any value change */
2098 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2099 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002100 case LYS_LEAFLIST:
2101 /* leaf-list move */
2102 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002103 if (lysc_is_dup_inst_list(elem->schema)) {
2104 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2105 } else {
2106 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2107 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002108 break;
2109 case LYS_LIST:
2110 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002111 if (lysc_is_dup_inst_list(elem->schema)) {
2112 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2113 } else {
2114 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2115 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002116 break;
2117 default:
2118 LOGINT(LYD_CTX(src_diff));
2119 ret = LY_EINT;
2120 goto cleanup;
2121 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002122 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002123 case LYD_DIFF_OP_NONE:
2124 switch (elem->schema->nodetype) {
2125 case LYS_LEAF:
2126 case LYS_LEAFLIST:
2127 /* default flag change */
2128 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2129 break;
2130 default:
2131 /* nothing to do */
2132 break;
2133 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002134 break;
2135 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002136 }
2137
Michal Vasko56daf732020-08-10 10:57:18 +02002138 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002139 }
2140 }
2141
2142cleanup:
2143 if (ret) {
2144 lyd_free_siblings(*diff);
2145 *diff = NULL;
2146 }
2147 return ret;
2148}