blob: fd72132b4e1da673470258479196a39f34ef3591 [file] [log] [blame]
Michal Vaskod59035b2020-07-08 12:00:06 +02001/**
2 * @file diff.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief diff functions
5 *
Michal Vaskoe78faec2021-04-08 17:24:43 +02006 * Copyright (c) 2020 - 2021 CESNET, z.s.p.o.
Michal Vaskod59035b2020-07-08 12:00:06 +02007 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
Christian Hopps32874e12021-05-01 09:43:54 -040014#define _GNU_SOURCE /* asprintf, strdup */
Michal Vaskod59035b2020-07-08 12:00:06 +020015
16#include "diff.h"
17
18#include <assert.h>
19#include <stddef.h>
Michal Vaskoe78faec2021-04-08 17:24:43 +020020#include <stdint.h>
21#include <stdio.h>
Radek Krejci47fab892020-11-05 17:02:41 +010022#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020023#include <string.h>
24
25#include "common.h"
Michal Vaskoe78faec2021-04-08 17:24:43 +020026#include "compat.h"
Radek Krejci47fab892020-11-05 17:02:41 +010027#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020028#include "log.h"
Michal Vasko19175b62022-04-01 09:17:07 +020029#include "plugins_exts.h"
Michal Vaskob4750962022-10-06 15:33:35 +020030#include "plugins_exts/metadata.h"
Radek Krejci47fab892020-11-05 17:02:41 +010031#include "plugins_types.h"
32#include "set.h"
33#include "tree.h"
34#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020035#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010036#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020037#include "tree_schema.h"
38#include "tree_schema_internal.h"
39
Michal Vasko52afd7d2022-01-18 14:08:34 +010040#define LOGERR_META(ctx, meta_name, node) \
41 { \
42 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
43 LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \
44 free(__path); \
45 }
46
47#define LOGERR_NOINST(ctx, node) \
48 { \
49 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
50 LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \
51 free(__path); \
52 }
53
54#define LOGERR_UNEXPVAL(ctx, node, data_source) \
55 { \
56 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
57 LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \
58 free(__path); \
59 }
60
61#define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \
62 { \
63 char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \
64 LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \
65 lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \
66 free(__path); \
67 }
68
Michal Vaskod59035b2020-07-08 12:00:06 +020069static const char *
70lyd_diff_op2str(enum lyd_diff_op op)
71{
72 switch (op) {
73 case LYD_DIFF_OP_CREATE:
74 return "create";
75 case LYD_DIFF_OP_DELETE:
76 return "delete";
77 case LYD_DIFF_OP_REPLACE:
78 return "replace";
79 case LYD_DIFF_OP_NONE:
80 return "none";
81 }
82
83 LOGINT(NULL);
84 return NULL;
85}
86
Michal Vaskoe6323f62020-07-09 15:49:02 +020087static enum lyd_diff_op
88lyd_diff_str2op(const char *str)
89{
90 switch (str[0]) {
91 case 'c':
92 assert(!strcmp(str, "create"));
93 return LYD_DIFF_OP_CREATE;
94 case 'd':
95 assert(!strcmp(str, "delete"));
96 return LYD_DIFF_OP_DELETE;
97 case 'r':
98 assert(!strcmp(str, "replace"));
99 return LYD_DIFF_OP_REPLACE;
100 case 'n':
101 assert(!strcmp(str, "none"));
102 return LYD_DIFF_OP_NONE;
103 }
104
105 LOGINT(NULL);
106 return 0;
107}
108
Michal Vaskocffc3f92022-06-15 07:57:24 +0200109/**
110 * @brief Create diff metadata for a nested user-ordered node with the effective operation "create".
111 *
112 * @param[in] node User-rodered node to update.
113 * @return LY_ERR value.
114 */
115static LY_ERR
116lyd_diff_add_create_nested_userord(struct lyd_node *node)
117{
118 LY_ERR rc = LY_SUCCESS;
119 const char *meta_name, *meta_val;
120 size_t buflen = 0, bufused = 0;
121 uint32_t pos;
122 char *dyn = NULL;
123
124 assert(lysc_is_userordered(node->schema));
125
126 /* get correct metadata name and value */
127 if (lysc_is_dup_inst_list(node->schema)) {
128 meta_name = "yang:position";
129
130 pos = lyd_list_pos(node);
131 if (asprintf(&dyn, "%" PRIu32, pos) == -1) {
132 LOGMEM(LYD_CTX(node));
133 rc = LY_EMEM;
134 goto cleanup;
135 }
136 meta_val = dyn;
137 } else if (node->schema->nodetype == LYS_LIST) {
138 meta_name = "yang:key";
139
140 if (node->prev->next && (node->prev->schema == node->schema)) {
141 LY_CHECK_GOTO(rc = lyd_path_list_predicate(node->prev, &dyn, &buflen, &bufused, 0), cleanup);
142 meta_val = dyn;
143 } else {
144 meta_val = "";
145 }
146 } else {
147 meta_name = "yang:value";
148
149 if (node->prev->next && (node->prev->schema == node->schema)) {
150 meta_val = lyd_get_value(node->prev);
151 } else {
152 meta_val = "";
153 }
154 }
155
156 /* create the metadata */
157 LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_val, 0, NULL), cleanup);
158
159cleanup:
160 free(dyn);
161 return rc;
162}
163
Michal Vaskofb51a842023-06-20 08:50:24 +0200164/**
165 * @brief Find metadata/an attribute of a node.
166 *
167 * @param[in] node Node to search.
168 * @param[in] name Metadata/attribute name.
169 * @param[out] meta Metadata found, NULL if not found.
170 * @param[out] attr Attribute found, NULL if not found.
171 */
172static void
173lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
174{
175 struct lyd_meta *m;
176 struct lyd_attr *a;
177
178 if (meta) {
179 *meta = NULL;
180 }
181 if (attr) {
182 *attr = NULL;
183 }
184
185 if (node->schema) {
186 assert(meta);
187
188 LY_LIST_FOR(node->meta, m) {
189 if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
190 *meta = m;
191 break;
192 }
193 }
194 } else {
195 assert(attr);
196
197 LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
198 /* name */
199 if (strcmp(a->name.name, name)) {
200 continue;
201 }
202
203 /* module */
204 switch (a->format) {
205 case LY_VALUE_JSON:
206 if (strcmp(a->name.module_name, "yang")) {
207 continue;
208 }
209 break;
210 case LY_VALUE_XML:
211 if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
212 continue;
213 }
214 break;
215 default:
216 LOGINT(LYD_CTX(node));
217 return;
218 }
219
220 *attr = a;
221 break;
222 }
223 }
224}
225
226/**
227 * @brief Learn operation of a diff node.
228 *
229 * @param[in] diff_node Diff node.
230 * @param[out] op Operation.
231 * @return LY_ERR value.
232 */
233static LY_ERR
234lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
235{
236 struct lyd_meta *meta = NULL;
237 struct lyd_attr *attr = NULL;
238 const struct lyd_node *diff_parent;
239 const char *str;
240 char *path;
241
242 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
243 lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
244 if (!meta && !attr) {
245 continue;
246 }
247
248 str = meta ? lyd_get_meta_value(meta) : attr->value;
249 if ((str[0] == 'r') && (diff_parent != diff_node)) {
250 /* we do not care about this operation if it's in our parent */
251 continue;
252 }
253 *op = lyd_diff_str2op(str);
254 return LY_SUCCESS;
255 }
256
257 /* operation not found */
258 path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
259 LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
260 free(path);
261 return LY_EINT;
262}
263
264/**
265 * @brief Remove metadata/an attribute from a node.
266 *
267 * @param[in] node Node to update.
268 * @param[in] name Metadata/attribute name.
269 */
270static void
271lyd_diff_del_meta(struct lyd_node *node, const char *name)
272{
273 struct lyd_meta *meta;
274 struct lyd_attr *attr;
275
276 lyd_diff_find_meta(node, name, &meta, &attr);
277
278 if (meta) {
279 lyd_free_meta_single(meta);
280 } else if (attr) {
281 lyd_free_attr_single(LYD_CTX(node), attr);
282 }
283}
284
Michal Vaskod59035b2020-07-08 12:00:06 +0200285LY_ERR
286lyd_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 +0200287 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
288 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200289{
Michal Vaskocffc3f92022-06-15 07:57:24 +0200290 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
Michal Vaskod59035b2020-07-08 12:00:06 +0200291 const struct lyd_node *parent = NULL;
Michal Vaskofb51a842023-06-20 08:50:24 +0200292 enum lyd_diff_op cur_op;
Michal Vaskod4525672023-06-20 09:15:42 +0200293 struct lyd_meta *meta;
Michal Vasko9af40912023-06-21 08:03:43 +0200294 uint32_t diff_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200295
296 assert(diff);
297
Michal Vasko53d48422020-11-13 18:02:29 +0100298 /* replace leaf always needs orig-default and orig-value */
299 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
300
301 /* create on userord needs key/value */
302 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200303 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +0100304 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200305 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +0100306
307 /* move on userord needs both key and orig-key/value and orig-value */
308 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200309 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100310 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200311 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
312 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100313
Michal Vaskod59035b2020-07-08 12:00:06 +0200314 /* find the first existing parent */
315 siblings = *diff;
Michal Vaskofb51a842023-06-20 08:50:24 +0200316 do {
Michal Vaskod59035b2020-07-08 12:00:06 +0200317 /* find next node parent */
318 parent = node;
319 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100320 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200321 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200322
323 if (lysc_is_dup_inst_list(parent->schema)) {
324 /* assume it never exists, we are not able to distinguish whether it does or not */
325 match = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200326 break;
327 }
328
329 /* check whether it exists in the diff */
330 if (lyd_find_sibling_first(siblings, parent, &match)) {
331 break;
332 }
333
334 /* another parent found */
335 diff_parent = match;
336
337 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200338 siblings = lyd_child_no_keys(match);
Michal Vaskofb51a842023-06-20 08:50:24 +0200339 } while (parent != node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200340
Michal Vaskofb51a842023-06-20 08:50:24 +0200341 if (match && (parent == node)) {
342 /* special case when there is already an operation on our descendant */
343 assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
344 (void)cur_op;
345
Michal Vasko9af40912023-06-21 08:03:43 +0200346 /* move it to the end where it is expected (matters for user-ordered lists) */
347 if (lysc_is_userordered(diff_parent->schema)) {
348 for (elem = diff_parent; elem->next && (elem->next->schema == elem->schema); elem = elem->next) {}
349 if (elem != diff_parent) {
350 LY_CHECK_RET(lyd_insert_after(elem, diff_parent));
351 }
352 }
353
Michal Vaskod4525672023-06-20 09:15:42 +0200354 /* will be replaced by the new operation but keep the current op for descendants */
Michal Vaskofb51a842023-06-20 08:50:24 +0200355 lyd_diff_del_meta(diff_parent, "operation");
Michal Vaskod4525672023-06-20 09:15:42 +0200356 LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) {
357 lyd_diff_find_meta(elem, "operation", &meta, NULL);
358 if (meta) {
359 /* explicit operation, fine */
360 continue;
361 }
362
363 /* set the none operation */
364 LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", 0, NULL));
365 }
366
Michal Vaskofb51a842023-06-20 08:50:24 +0200367 dup = diff_parent;
Michal Vasko695a7f22023-02-14 10:02:10 +0100368 } else {
Michal Vaskofb51a842023-06-20 08:50:24 +0200369 /* duplicate the subtree (and connect to the diff if possible) */
370 if (diff_parent) {
371 LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
372 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
373 } else {
374 LY_CHECK_RET(lyd_dup_single(node, NULL,
375 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200376 }
Michal Vaskofb51a842023-06-20 08:50:24 +0200377
378 /* find the first duplicated parent */
379 if (!diff_parent) {
380 diff_parent = lyd_parent(dup);
381 while (diff_parent && diff_parent->parent) {
382 diff_parent = lyd_parent(diff_parent);
383 }
384 } else {
385 diff_parent = dup;
386 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
387 diff_parent = lyd_parent(diff_parent);
388 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200389 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200390
Michal Vaskofb51a842023-06-20 08:50:24 +0200391 /* no parent existed, must be manually connected */
392 if (!diff_parent) {
393 /* there actually was no parent to duplicate */
394 lyd_insert_sibling(*diff, dup, diff);
395 } else if (!diff_parent->parent) {
396 lyd_insert_sibling(*diff, diff_parent, diff);
397 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200398
Michal Vaskofb51a842023-06-20 08:50:24 +0200399 /* add parent operation, if any */
400 if (diff_parent && (diff_parent != dup)) {
401 LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
402 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200403 }
404
405 /* add subtree operation */
Michal Vasko2e552792022-11-02 12:15:31 +0100406 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200407
Michal Vaskocffc3f92022-06-15 07:57:24 +0200408 if (op == LYD_DIFF_OP_CREATE) {
409 /* all nested user-ordered (leaf-)lists need special metadata for create op */
410 LYD_TREE_DFS_BEGIN(dup, elem) {
411 if ((elem != dup) && lysc_is_userordered(elem->schema)) {
412 LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem));
413 }
414 LYD_TREE_DFS_END(dup, elem);
415 }
416 }
417
Michal Vaskod59035b2020-07-08 12:00:06 +0200418 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200419 if (orig_default) {
Michal Vasko2e552792022-11-02 12:15:31 +0100420 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200421 }
422
423 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200424 if (orig_value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100425 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200426 }
427
428 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200429 if (key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100430 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200431 }
432
433 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200434 if (value) {
Michal Vasko2e552792022-11-02 12:15:31 +0100435 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200436 }
437
Michal Vaskoe78faec2021-04-08 17:24:43 +0200438 /* position */
439 if (position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100440 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200441 }
442
Michal Vaskod59035b2020-07-08 12:00:06 +0200443 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200444 if (orig_key) {
Michal Vasko2e552792022-11-02 12:15:31 +0100445 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200446 }
447
Michal Vaskoe78faec2021-04-08 17:24:43 +0200448 /* orig-position */
449 if (orig_position) {
Michal Vasko2e552792022-11-02 12:15:31 +0100450 LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200451 }
452
Michal Vaskod59035b2020-07-08 12:00:06 +0200453 return LY_SUCCESS;
454}
455
456/**
457 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
458 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100459 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200460 * @param[in] schema Schema node of the list/leaf-list.
461 * @param[in,out] userord Sized array of userord items.
462 * @return Userord item for all the user-ordered list/leaf-list instances.
463 */
464static struct lyd_diff_userord *
465lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
466{
467 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200468 struct lyd_node *iter;
469 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200470 LY_ARRAY_COUNT_TYPE u;
471
472 LY_ARRAY_FOR(*userord, u) {
473 if ((*userord)[u].schema == schema) {
474 return &(*userord)[u];
475 }
476 }
477
478 /* it was not added yet, add it now */
479 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
480
481 item->schema = schema;
482 item->pos = 0;
483 item->inst = NULL;
484
485 /* store all the instance pointers in the current order */
486 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200487 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
488 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
489 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200490 }
491 }
492
493 return item;
494}
495
496/**
497 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
498 * lists/leaf-lists.
499 *
500 * @param[in] first Node from the first tree, can be NULL (on create).
501 * @param[in] second Node from the second tree, can be NULL (on delete).
502 * @param[in] options Diff options.
Michal Vasko5da938a2022-03-01 09:19:02 +0100503 * @param[in] userord_item Userord item of @p first and/or @p second node.
Michal Vaskod59035b2020-07-08 12:00:06 +0200504 * @param[out] op Operation.
505 * @param[out] orig_default Original default metadata.
506 * @param[out] value Value metadata.
507 * @param[out] orig_value Original value metadata
508 * @param[out] key Key metadata.
509 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200510 * @param[out] position Position metadata.
511 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200512 * @return LY_SUCCESS on success,
513 * @return LY_ENOT if there is no change to be added into diff,
514 * @return LY_ERR value on other errors.
515 */
516static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200517lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Michal Vasko5da938a2022-03-01 09:19:02 +0100518 struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200519 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200520{
Michal Vaskof9b052a2022-06-08 10:26:53 +0200521 LY_ERR rc = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +0200522 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200523 size_t buflen, bufused;
Michal Vaskoee9b9482023-06-19 13:17:48 +0200524 uint32_t first_pos, second_pos, comp_opts;
Michal Vaskod59035b2020-07-08 12:00:06 +0200525
526 assert(first || second);
527
528 *orig_default = NULL;
529 *value = NULL;
530 *orig_value = NULL;
531 *key = NULL;
532 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200533 *position = NULL;
534 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200535
536 schema = first ? first->schema : second->schema;
537 assert(lysc_is_userordered(schema));
538
Michal Vaskod59035b2020-07-08 12:00:06 +0200539 /* find user-ordered first position */
540 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200541 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200542 if (userord_item->inst[first_pos] == first) {
543 break;
544 }
545 }
546 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
547 } else {
548 first_pos = 0;
549 }
550
Michal Vaskoe78faec2021-04-08 17:24:43 +0200551 /* prepare position of the next instance */
552 second_pos = userord_item->pos++;
553
Michal Vaskod59035b2020-07-08 12:00:06 +0200554 /* learn operation first */
555 if (!second) {
556 *op = LYD_DIFF_OP_DELETE;
557 } else if (!first) {
558 *op = LYD_DIFF_OP_CREATE;
559 } else {
Michal Vaskoee9b9482023-06-19 13:17:48 +0200560 comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
561 if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200562 /* in first, there is a different instance on the second position, we are going to move 'first' node */
563 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200564 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200565 /* default flag change */
566 *op = LYD_DIFF_OP_NONE;
567 } else {
568 /* no changes */
569 return LY_ENOT;
570 }
571 }
572
573 /*
574 * set each attribute correctly based on the operation and node type
575 */
576
577 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100578 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200579 if (first->flags & LYD_DEFAULT) {
580 *orig_default = "true";
581 } else {
582 *orig_default = "false";
583 }
584 }
585
586 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200587 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
588 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200589 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200590 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200591 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200592 } else {
593 *value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200594 LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200595 }
596 }
597
598 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200599 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
600 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200601 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200602 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskof9b052a2022-06-08 10:26:53 +0200603 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200604 } else {
605 *orig_value = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200606 LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200607 }
608 }
609
610 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200611 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
612 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200613 if (second_pos) {
614 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200615 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 +0200616 } else {
617 *key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200618 LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200619 }
620 }
621
622 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200623 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
624 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200625 if (first_pos) {
626 buflen = bufused = 0;
Michal Vaskof9b052a2022-06-08 10:26:53 +0200627 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 +0200628 } else {
629 *orig_key = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200630 LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200631 }
632 }
633
Michal Vaskoe78faec2021-04-08 17:24:43 +0200634 /* position */
635 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
636 if (second_pos) {
637 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
638 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200639 rc = LY_EMEM;
640 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200641 }
642 } else {
643 *position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200644 LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200645 }
646 }
647
648 /* orig-position */
649 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
650 if (first_pos) {
651 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
652 LOGMEM(schema->module->ctx);
Michal Vaskof9b052a2022-06-08 10:26:53 +0200653 rc = LY_EMEM;
654 goto cleanup;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200655 }
656 } else {
657 *orig_position = strdup("");
Michal Vaskof9b052a2022-06-08 10:26:53 +0200658 LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200659 }
660 }
661
Michal Vaskod59035b2020-07-08 12:00:06 +0200662 /*
663 * update our instances - apply the change
664 */
665 if (*op == LYD_DIFF_OP_CREATE) {
666 /* insert the instance */
Michal Vaskof9b052a2022-06-08 10:26:53 +0200667 LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200668 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
669 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
670 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
671 }
672 LY_ARRAY_INCREMENT(userord_item->inst);
673 userord_item->inst[second_pos] = second;
674
675 } else if (*op == LYD_DIFF_OP_DELETE) {
676 /* remove the instance */
677 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
678 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
679 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
680 }
681 LY_ARRAY_DECREMENT(userord_item->inst);
682
683 } else if (*op == LYD_DIFF_OP_REPLACE) {
684 /* move the instances */
685 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
686 (first_pos - second_pos) * sizeof *userord_item->inst);
687 userord_item->inst[second_pos] = first;
688 }
689
Michal Vaskof9b052a2022-06-08 10:26:53 +0200690cleanup:
691 if (rc) {
692 free(*value);
693 *value = NULL;
694 free(*orig_value);
695 *orig_value = NULL;
696 free(*key);
697 *key = NULL;
698 free(*orig_key);
699 *orig_key = NULL;
700 free(*position);
701 *position = NULL;
702 free(*orig_position);
703 *orig_position = NULL;
704 }
705 return rc;
Michal Vaskod59035b2020-07-08 12:00:06 +0200706}
707
708/**
709 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
710 * lists/leaf-lists.
711 *
712 * @param[in] first Node from the first tree, can be NULL (on create).
713 * @param[in] second Node from the second tree, can be NULL (on delete).
714 * @param[in] options Diff options.
715 * @param[out] op Operation.
716 * @param[out] orig_default Original default metadata.
717 * @param[out] orig_value Original value metadata.
718 * @return LY_SUCCESS on success,
719 * @return LY_ENOT if there is no change to be added into diff,
720 * @return LY_ERR value on other errors.
721 */
722static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200723lyd_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 +0200724 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200725{
726 const struct lysc_node *schema;
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200727 const char *str_val;
Michal Vaskod59035b2020-07-08 12:00:06 +0200728
729 assert(first || second);
730
731 *orig_default = NULL;
732 *orig_value = NULL;
733
734 schema = first ? first->schema : second->schema;
735 assert(!lysc_is_userordered(schema));
736
737 /* learn operation first */
738 if (!second) {
739 *op = LYD_DIFF_OP_DELETE;
740 } else if (!first) {
741 *op = LYD_DIFF_OP_CREATE;
742 } else {
743 switch (schema->nodetype) {
744 case LYS_CONTAINER:
745 case LYS_RPC:
746 case LYS_ACTION:
747 case LYS_NOTIF:
748 /* no changes */
749 return LY_ENOT;
750 case LYS_LIST:
751 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200752 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200753 /* default flag change */
754 *op = LYD_DIFF_OP_NONE;
755 } else {
756 /* no changes */
757 return LY_ENOT;
758 }
759 break;
760 case LYS_LEAF:
761 case LYS_ANYXML:
762 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200763 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200764 /* different values */
765 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200766 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200767 /* default flag change */
768 *op = LYD_DIFF_OP_NONE;
769 } else {
770 /* no changes */
771 return LY_ENOT;
772 }
773 break;
774 default:
775 LOGINT_RET(schema->module->ctx);
776 }
777 }
778
779 /*
780 * set each attribute correctly based on the operation and node type
781 */
782
783 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100784 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200785 if (first->flags & LYD_DEFAULT) {
786 *orig_default = "true";
787 } else {
788 *orig_default = "false";
789 }
790 }
791
792 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100793 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
794 if (schema->nodetype == LYS_LEAF) {
Michal Vasko6ea6fe22021-10-08 09:57:01 +0200795 str_val = lyd_get_value(first);
796 *orig_value = strdup(str_val ? str_val : "");
Michal Vaskobaba84e2021-02-05 16:33:30 +0100797 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
798 } else {
799 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
800 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200801 }
802
803 return LY_SUCCESS;
804}
805
806/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200807 * @brief Find a matching instance of a node in a data tree.
808 *
809 * @param[in] siblings Siblings to search in.
810 * @param[in] target Target node to search for.
811 * @param[in] defaults Whether to consider (or ignore) default values.
Michal Vasko271d2e32023-01-31 15:43:19 +0100812 * @param[in,out] dup_inst_ht Duplicate instance cache.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200813 * @param[out] match Found match, NULL if no matching node found.
814 * @return LY_ERR value.
815 */
816static LY_ERR
817lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vasko8efac242023-03-30 08:24:56 +0200818 struct ly_ht **dup_inst_ht, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200819{
Michal Vasko2bd856f2022-12-02 14:03:29 +0100820 LY_ERR r;
821
Michal Vaskodb91fc32023-05-02 14:39:40 +0200822 if (!target->schema) {
823 /* try to find the same opaque node */
824 r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
825 } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200826 /* try to find the exact instance */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100827 r = lyd_find_sibling_first(siblings, target, match);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200828 } else {
829 /* try to simply find the node, there cannot be more instances */
Michal Vasko2bd856f2022-12-02 14:03:29 +0100830 r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
831 }
832 if (r && (r != LY_ENOTFOUND)) {
833 return r;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200834 }
835
Michal Vaskod7c048c2021-05-18 16:12:55 +0200836 /* update match as needed */
Michal Vasko271d2e32023-01-31 15:43:19 +0100837 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200838
839 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
840 /* ignore default nodes */
841 *match = NULL;
842 }
843 return LY_SUCCESS;
844}
845
846/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200847 * @brief Perform diff for all siblings at certain depth, recursively.
848 *
849 * For user-ordered lists/leaf-lists a specific structure is used for storing
850 * the current order. The idea is to apply all the generated diff changes
851 * virtually on the first tree so that we can continue to generate correct
852 * changes after some were already generated.
853 *
854 * The algorithm then uses second tree position-based changes with a before
855 * (preceding) item anchor.
856 *
857 * Example:
858 *
859 * Virtual first tree leaf-list order:
860 * 1 2 [3] 4 5
861 *
862 * Second tree leaf-list order:
863 * 1 2 [5] 3 4
864 *
865 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
866 * match - they do not - move nodes so that the 3rd position node is final ->
867 * -> move node 5 to the 3rd position -> move node 5 after node 2.
868 *
869 * Required properties:
870 * Stored operations (move) should not be affected by later operations -
871 * - would cause a redundantly long list of operations, possibly inifinite.
872 *
873 * Implemenation justification:
874 * First, all delete operations and only then move/create operations are stored.
875 * Also, preceding anchor is used and after each iteration another node is
876 * at its final position. That results in the invariant that all preceding
877 * nodes are final and will not be changed by the later operations, meaning
878 * they can safely be used as anchors for the later operations.
879 *
880 * @param[in] first First tree first sibling.
881 * @param[in] second Second tree first sibling.
882 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200883 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200884 * @param[in,out] diff Diff to append to.
885 * @return LY_ERR value.
886 */
887static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200888lyd_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 +0200889 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200890{
891 LY_ERR ret = LY_SUCCESS;
892 const struct lyd_node *iter_first, *iter_second;
893 struct lyd_node *match_second, *match_first;
Michal Vasko5da938a2022-03-01 09:19:02 +0100894 struct lyd_diff_userord *userord = NULL, *userord_item;
Michal Vasko8efac242023-03-30 08:24:56 +0200895 struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200896 LY_ARRAY_COUNT_TYPE u;
897 enum lyd_diff_op op;
898 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200899 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200900
Michal Vaskod59035b2020-07-08 12:00:06 +0200901 /* compare first tree to the second tree - delete, replace, none */
902 LY_LIST_FOR(first, iter_first) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200903 if (!iter_first->schema) {
904 continue;
905 }
906
Michal Vaskod59035b2020-07-08 12:00:06 +0200907 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200908 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200909 /* skip default nodes */
910 continue;
911 }
912
Michal Vaskoe78faec2021-04-08 17:24:43 +0200913 /* find a match in the second tree */
914 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
915 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200916
917 if (lysc_is_userordered(iter_first->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100918 /* get (create) userord entry */
919 userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord);
920 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup);
921
Michal Vaskoe78faec2021-04-08 17:24:43 +0200922 /* we are handling only user-ordered node delete now */
923 if (!match_second) {
924 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +0100925 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op,
926 &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200927
928 /* there must be changes, it is deleted */
929 assert(op == LYD_DIFF_OP_DELETE);
Michal Vasko5da938a2022-03-01 09:19:02 +0100930 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key,
931 orig_position, diff);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200932
933 free(orig_value);
934 free(key);
935 free(value);
936 free(position);
937 free(orig_key);
938 free(orig_position);
939 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200940 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200941 } else {
942 /* get all the attributes */
943 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
944
945 /* add into diff if there are any changes */
946 if (!ret) {
947 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200948 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200949 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100950 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200951 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200952 }
953
954 free(orig_value);
955 LY_CHECK_GOTO(ret, cleanup);
956 } else if (ret == LY_ENOT) {
957 ret = LY_SUCCESS;
958 } else {
959 goto cleanup;
960 }
961 }
962
963 /* check descendants, if any, recursively */
964 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200965 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
966 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200967 }
968
969 if (nosiblings) {
970 break;
971 }
972 }
973
974 /* reset all cached positions */
975 LY_ARRAY_FOR(userord, u) {
976 userord[u].pos = 0;
977 }
978
979 /* compare second tree to the first tree - create, user-ordered move */
980 LY_LIST_FOR(second, iter_second) {
Michal Vaskoc825ed72021-07-21 16:05:59 +0200981 if (!iter_second->schema) {
982 continue;
983 }
984
Michal Vaskod59035b2020-07-08 12:00:06 +0200985 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200986 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200987 /* skip default nodes */
988 continue;
989 }
990
Michal Vaskoe78faec2021-04-08 17:24:43 +0200991 /* find a match in the first tree */
992 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
993 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200994
995 if (lysc_is_userordered(iter_second->schema)) {
Michal Vasko5da938a2022-03-01 09:19:02 +0100996 /* get userord entry */
Michal Vaskoaa51cc52022-12-06 09:57:11 +0100997 userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord);
Michal Vasko5da938a2022-03-01 09:19:02 +0100998 LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup);
999
Michal Vaskod59035b2020-07-08 12:00:06 +02001000 /* get all the attributes */
Michal Vasko5da938a2022-03-01 09:19:02 +01001001 ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001002 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001003
1004 /* add into diff if there are any changes */
1005 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001006 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
1007 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001008
1009 free(orig_value);
1010 free(key);
1011 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001012 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001013 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001014 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +02001015 LY_CHECK_GOTO(ret, cleanup);
1016 } else if (ret == LY_ENOT) {
1017 ret = LY_SUCCESS;
1018 } else {
1019 goto cleanup;
1020 }
1021 } else if (!match_first) {
1022 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001023 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 +02001024
1025 /* there must be changes, it is created */
1026 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001027 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001028
1029 free(orig_value);
1030 LY_CHECK_GOTO(ret, cleanup);
1031 } /* else was handled */
1032
1033 if (nosiblings) {
1034 break;
1035 }
1036 }
1037
1038cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001039 lyd_dup_inst_free(dup_inst_first);
1040 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001041 LY_ARRAY_FOR(userord, u) {
1042 LY_ARRAY_FREE(userord[u].inst);
1043 }
1044 LY_ARRAY_FREE(userord);
1045 return ret;
1046}
1047
Michal Vasko3a41dff2020-07-15 14:30:28 +02001048static LY_ERR
Michal Vasko55896172022-02-17 10:47:21 +01001049lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings,
1050 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001051{
1052 const struct ly_ctx *ctx;
1053
1054 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1055
1056 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001057 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +02001058 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001059 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +02001060 } else {
1061 ctx = NULL;
1062 }
1063
1064 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
1065 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
1066 return LY_EINVAL;
1067 }
1068
1069 *diff = NULL;
1070
Michal Vasko3a41dff2020-07-15 14:30:28 +02001071 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
1072}
1073
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001074LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001075lyd_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 +02001076{
1077 return lyd_diff(first, second, options, 1, diff);
1078}
1079
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001080LIBYANG_API_DEF LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +02001081lyd_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 +02001082{
1083 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +02001084}
1085
1086/**
Michal Vaskod59035b2020-07-08 12:00:06 +02001087 * @brief Insert a diff node into a data tree.
1088 *
1089 * @param[in,out] first_node First sibling of the data tree.
1090 * @param[in] parent_node Data tree sibling parent node.
1091 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001092 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
1093 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +02001094 * @return err_info, NULL on success.
1095 */
1096static LY_ERR
1097lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +02001098 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +02001099{
1100 LY_ERR ret;
1101 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001102 uint32_t pos, anchor_pos;
1103 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +02001104
1105 assert(new_node);
1106
1107 if (!*first_node) {
1108 if (!parent_node) {
1109 /* no parent or siblings */
1110 *first_node = new_node;
1111 return LY_SUCCESS;
1112 }
1113
1114 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001115 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001116 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +02001117 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +02001118 return LY_EINVAL;
1119 }
Michal Vaskob104f112020-07-17 09:54:54 +02001120 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001121 }
1122
Michal Vasko9e685082021-01-29 14:49:09 +01001123 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001124
Michal Vaskod59035b2020-07-08 12:00:06 +02001125 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +02001126 /* simple insert */
1127 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001128 }
1129
Michal Vaskoe78faec2021-04-08 17:24:43 +02001130 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001131 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001132 if (lysc_is_dup_inst_list(new_node->schema)) {
1133 anchor_pos = atoi(userord_anchor);
Michal Vasko0ff97752022-01-18 16:35:41 +01001134 if (!anchor_pos) {
1135 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor);
1136 return LY_EINVAL;
1137 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001138
1139 found = 0;
1140 pos = 1;
1141 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
1142 if (pos == anchor_pos) {
1143 found = 1;
1144 break;
1145 }
1146 ++pos;
1147 }
1148 if (!found) {
1149 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1150 new_node->schema->name);
1151 return LY_EINVAL;
1152 }
1153 } else {
1154 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
1155 if (ret == LY_ENOTFOUND) {
1156 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
1157 new_node->schema->name);
1158 return LY_EINVAL;
1159 } else if (ret) {
1160 return ret;
1161 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001162 }
1163
1164 /* insert after */
1165 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
1166 assert(new_node->prev == anchor);
1167 if (*first_node == new_node) {
1168 *first_node = anchor;
1169 }
1170 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001171 /* find the first instance */
1172 ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor);
1173 LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret);
Michal Vaskod59035b2020-07-08 12:00:06 +02001174
Michal Vaskoea7d3232022-04-19 12:01:36 +02001175 if (anchor) {
1176 /* insert before the first instance */
1177 LY_CHECK_RET(lyd_insert_before(anchor, new_node));
1178 if ((*first_node)->prev->next) {
1179 assert(!new_node->prev->next);
1180 *first_node = new_node;
Michal Vaskod59035b2020-07-08 12:00:06 +02001181 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001182 } else {
Michal Vaskoea7d3232022-04-19 12:01:36 +02001183 /* insert anywhere */
1184 LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node));
Michal Vaskod59035b2020-07-08 12:00:06 +02001185 }
1186 }
1187
1188 return LY_SUCCESS;
1189}
1190
1191/**
1192 * @brief Apply diff subtree on data tree nodes, recursively.
1193 *
1194 * @param[in,out] first_node First sibling of the data tree.
1195 * @param[in] parent_node Parent of the first sibling.
1196 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001197 * @param[in] diff_cb Optional diff callback.
1198 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001199 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +02001200 * @return LY_ERR value.
1201 */
1202static LY_ERR
1203lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Michal Vasko8efac242023-03-30 08:24:56 +02001204 lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +02001205{
1206 LY_ERR ret;
1207 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001208 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001209 enum lyd_diff_op op;
1210 struct lyd_meta *meta;
Michal Vasko8efac242023-03-30 08:24:56 +02001211 struct ly_ht *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +02001212 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001213
1214 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001215 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +02001216
Michal Vaskoe6323f62020-07-09 15:49:02 +02001217 /* handle specific user-ordered (leaf-)lists operations separately */
1218 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
1219 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001220 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001221 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001222 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001223 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001224 /* duplicate the node */
1225 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001226 }
1227
Michal Vaskoe78faec2021-04-08 17:24:43 +02001228 /* get "key", "value", or "position" metadata string value */
1229 if (lysc_is_dup_inst_list(diff_node->schema)) {
1230 meta_str = "yang:position";
1231 } else if (diff_node->schema->nodetype == LYS_LIST) {
1232 meta_str = "yang:key";
1233 } else {
1234 meta_str = "yang:value";
1235 }
1236 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001237 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001238 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001239
Michal Vaskod59035b2020-07-08 12:00:06 +02001240 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001241 if (str_val[0]) {
1242 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +02001243 } else {
1244 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
1245 }
1246 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001247 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001248 lyd_free_tree(match);
1249 }
1250 return ret;
1251 }
1252
1253 goto next_iter_r;
1254 }
1255
1256 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001257 switch (op) {
1258 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001259 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001260 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001261 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001262
1263 if (match->schema->nodetype & LYD_NODE_TERM) {
1264 /* special case of only dflt flag change */
1265 if (diff_node->flags & LYD_DEFAULT) {
1266 match->flags |= LYD_DEFAULT;
1267 } else {
1268 match->flags &= ~LYD_DEFAULT;
1269 }
1270 } else {
1271 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001272 if (!lyd_child_no_keys(diff_node)) {
Michal Vasko0ff97752022-01-18 16:35:41 +01001273 LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.",
1274 LYD_NAME(diff_node));
1275 return LY_EINVAL;
Michal Vaskod59035b2020-07-08 12:00:06 +02001276 }
1277 }
1278 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001279 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001280 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001281 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001282
1283 /* insert it at the end */
1284 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001285 if (parent_node) {
Michal Vasko19175b62022-04-01 09:17:07 +02001286 if (match->flags & LYD_EXT) {
Michal Vasko193dacd2022-10-13 08:43:05 +02001287 ret = lyplg_ext_insert(parent_node, match);
Michal Vasko19175b62022-04-01 09:17:07 +02001288 } else {
1289 ret = lyd_insert_child(parent_node, match);
1290 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001291 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001292 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001293 }
1294 if (ret) {
1295 lyd_free_tree(match);
1296 return ret;
1297 }
1298
1299 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001300 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001301 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001302 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001303 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001304
1305 /* remove it */
1306 if ((match == *first_node) && !match->parent) {
1307 assert(!parent_node);
1308 /* we have removed the top-level node */
1309 *first_node = (*first_node)->next;
1310 }
1311 lyd_free_tree(match);
1312
1313 /* we are not going recursively in this case, the whole subtree was already deleted */
1314 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001315 case LYD_DIFF_OP_REPLACE:
Michal Vasko0ff97752022-01-18 16:35:41 +01001316 if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) {
1317 LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".",
1318 lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node));
1319 return LY_EINVAL;
1320 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001321
1322 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001323 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001324 LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL);
Michal Vaskod59035b2020-07-08 12:00:06 +02001325
Michal Vaskobaba84e2021-02-05 16:33:30 +01001326 /* update the value */
1327 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001328 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vasko52afd7d2022-01-18 14:08:34 +01001329 LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL);
Michal Vaskobaba84e2021-02-05 16:33:30 +01001330 } else {
1331 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vasko26bbb272022-08-02 14:54:33 +02001332
Michal Vaskoe78faec2021-04-08 17:24:43 +02001333 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001334 }
1335
1336 /* with flags */
1337 match->flags = diff_node->flags;
1338 break;
1339 default:
1340 LOGINT_RET(ctx);
1341 }
1342
1343next_iter_r:
1344 if (diff_cb) {
1345 /* call callback */
1346 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1347 }
1348
1349 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001350 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001351 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001352 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1353 if (ret) {
1354 break;
1355 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001356 }
1357
Michal Vaskod7c048c2021-05-18 16:12:55 +02001358 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001359 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001360}
1361
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001362LIBYANG_API_DEF LY_ERR
Michal Vaskod59035b2020-07-08 12:00:06 +02001363lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001364 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001365{
1366 const struct lyd_node *root;
Michal Vasko8efac242023-03-30 08:24:56 +02001367 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001368 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001369
1370 LY_LIST_FOR(diff, root) {
1371 if (mod && (lyd_owner_module(root) != mod)) {
1372 /* skip data nodes from different modules */
1373 continue;
1374 }
1375
1376 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001377 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1378 if (ret) {
1379 break;
1380 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001381 }
1382
Michal Vaskod7c048c2021-05-18 16:12:55 +02001383 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001384 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001385}
1386
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001387LIBYANG_API_DEF LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001388lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001389{
1390 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1391}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001392
1393/**
1394 * @brief Update operations on a diff node when the new operation is NONE.
1395 *
1396 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001397 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001398 * @param[in] src_diff Current source diff node.
1399 * @return LY_ERR value.
1400 */
1401static LY_ERR
1402lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1403{
1404 switch (cur_op) {
1405 case LYD_DIFF_OP_NONE:
1406 case LYD_DIFF_OP_CREATE:
1407 case LYD_DIFF_OP_REPLACE:
1408 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1409 /* NONE on a term means only its dflt flag was changed */
1410 diff_match->flags &= ~LYD_DEFAULT;
1411 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1412 }
1413 break;
1414 default:
1415 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001416 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE);
1417 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001418 }
1419
1420 return LY_SUCCESS;
1421}
1422
1423/**
Michal Vaskoe6323f62020-07-09 15:49:02 +02001424 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001425 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001426 *
1427 * @param[in] node Node to change.
1428 * @param[in] op Operation to set.
1429 * @return LY_ERR value.
1430 */
1431static LY_ERR
1432lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1433{
Michal Vaskodb91fc32023-05-02 14:39:40 +02001434 lyd_diff_del_meta(node, "operation");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001435
Michal Vaskodb91fc32023-05-02 14:39:40 +02001436 if (node->schema) {
1437 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
1438 } else {
1439 return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001440 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001441}
1442
1443/**
1444 * @brief Update operations on a diff node when the new operation is REPLACE.
1445 *
1446 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001447 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001448 * @param[in] src_diff Current source diff node.
1449 * @return LY_ERR value.
1450 */
1451static LY_ERR
1452lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1453{
1454 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001455 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001456 struct lyd_meta *meta;
1457 const struct lys_module *mod;
1458 const struct lyd_node_any *any;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001459 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001460
1461 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001462 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001463 assert(mod);
1464
1465 switch (cur_op) {
1466 case LYD_DIFF_OP_REPLACE:
1467 case LYD_DIFF_OP_CREATE:
1468 switch (diff_match->schema->nodetype) {
1469 case LYS_LIST:
1470 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001471 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001472 * keep orig_key/orig_value (only replace oper) and replace key/value */
1473 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001474 if (lysc_is_dup_inst_list(diff_match->schema)) {
1475 meta_name = "position";
1476 } else if (diff_match->schema->nodetype == LYS_LIST) {
1477 meta_name = "key";
1478 } else {
1479 meta_name = "value";
1480 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001481
1482 lyd_diff_del_meta(diff_match, meta_name);
1483 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001484 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001485 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001486 break;
1487 case LYS_LEAF:
1488 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001489 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001490 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1491 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001492 }
1493
Michal Vaskoe6323f62020-07-09 15:49:02 +02001494 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001495 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001496 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001497 }
1498
Michal Vasko8caadab2020-11-05 17:38:15 +01001499 if (cur_op == LYD_DIFF_OP_REPLACE) {
1500 /* compare values whether there is any change at all */
1501 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001502 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001503 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001504 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1505 if (!ret) {
1506 /* values are the same, remove orig-value meta and set oper to NONE */
1507 lyd_free_meta_single(meta);
1508 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1509 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001510 }
1511
1512 /* modify the default flag */
1513 diff_match->flags &= ~LYD_DEFAULT;
1514 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1515 break;
1516 case LYS_ANYXML:
1517 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001518 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001519 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1520 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001521 }
1522
1523 /* modify the node value */
1524 any = (struct lyd_node_any *)src_diff;
1525 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1526 break;
1527 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001528 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001529 }
1530 break;
1531 case LYD_DIFF_OP_NONE:
1532 /* it is moved now */
1533 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1534
1535 /* change the operation */
1536 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1537
Michal Vaskoe78faec2021-04-08 17:24:43 +02001538 /* set orig-meta and meta */
1539 if (lysc_is_dup_inst_list(diff_match->schema)) {
1540 meta_name = "position";
1541 orig_meta_name = "orig-position";
1542 } else {
1543 meta_name = "key";
1544 orig_meta_name = "orig-key";
1545 }
1546
1547 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001548 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001549 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001550
Michal Vaskoe78faec2021-04-08 17:24:43 +02001551 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001552 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001553 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001554 break;
1555 default:
1556 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001557 LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE);
1558 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001559 }
1560
1561 return LY_SUCCESS;
1562}
1563
1564/**
1565 * @brief Update operations in a diff node when the new operation is CREATE.
1566 *
1567 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001568 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001569 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001570 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001571 * @return LY_ERR value.
1572 */
1573static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001574lyd_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 +02001575{
1576 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001577 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001578 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001579 const char *meta_name, *orig_meta_name;
1580 struct lyd_meta *meta, *orig_meta;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001581 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001582
1583 switch (cur_op) {
1584 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001585 /* remember current flags */
1586 trg_flags = diff_match->flags;
1587
Michal Vaskoe78faec2021-04-08 17:24:43 +02001588 if (lysc_is_userordered(diff_match->schema)) {
1589 /* get anchor metadata */
1590 if (lysc_is_dup_inst_list(diff_match->schema)) {
1591 meta_name = "yang:position";
1592 orig_meta_name = "yang:orig-position";
1593 } else if (diff_match->schema->nodetype == LYS_LIST) {
1594 meta_name = "yang:key";
1595 orig_meta_name = "yang:orig-key";
1596 } else {
1597 meta_name = "yang:value";
1598 orig_meta_name = "yang:orig-value";
1599 }
1600 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001601 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001602 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
Michal Vasko52afd7d2022-01-18 14:08:34 +01001603 LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001604
1605 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1606 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001607 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001608 /* deleted + created at another position -> operation REPLACE */
1609 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1610
1611 /* add anchor metadata */
1612 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1613 } else {
1614 /* deleted + created at the same position -> operation NONE */
1615 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1616
1617 /* delete anchor metadata */
1618 lyd_free_meta_single(orig_meta);
1619 }
1620 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1621 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1622 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1623 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1624 }
1625
Radek Krejci55c4bd22021-04-26 08:09:04 +02001626 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1627 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001628 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1629 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1630 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1631 /* deleted + created -> operation NONE */
1632 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1633 } else {
1634 /* we deleted it, but it was created with a different value -> operation REPLACE */
1635 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1636
1637 /* current value is the previous one (meta) */
1638 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001639 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001640
1641 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001642 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001643 }
1644 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001645 /* deleted + created -> operation NONE */
1646 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001647 }
1648
1649 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001650 /* add orig-dflt metadata */
1651 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1652 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1653
Michal Vaskoe6323f62020-07-09 15:49:02 +02001654 /* update dflt flag itself */
1655 diff_match->flags &= ~LYD_DEFAULT;
1656 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001657 }
1658
1659 /* but the operation of its children should remain DELETE */
1660 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1661 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001662 }
1663 break;
1664 default:
1665 /* create and replace operations are not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001666 LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
1667 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001668 }
1669
1670 return LY_SUCCESS;
1671}
1672
1673/**
1674 * @brief Update operations on a diff node when the new operation is DELETE.
1675 *
1676 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001677 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001678 * @param[in] src_diff Current source diff node.
1679 * @return LY_ERR value.
1680 */
1681static LY_ERR
1682lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1683{
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001684 struct lyd_node *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001685 struct lyd_meta *meta;
1686 const char *meta_name;
Michal Vasko52afd7d2022-01-18 14:08:34 +01001687 const struct ly_ctx *ctx = LYD_CTX(diff_match);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001688
1689 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001690 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 +02001691
1692 switch (cur_op) {
1693 case LYD_DIFF_OP_CREATE:
1694 /* it was created, but then deleted -> set NONE operation */
1695 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1696
1697 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1698 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001699 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1700 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001701 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001702 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001703 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001704 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1705 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001706 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001707 break;
1708 case LYD_DIFF_OP_REPLACE:
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001709 /* remove the redundant metadata */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001710 if (lysc_is_userordered(diff_match->schema)) {
1711 if (lysc_is_dup_inst_list(diff_match->schema)) {
1712 meta_name = "position";
1713 } else if (diff_match->schema->nodetype == LYS_LIST) {
1714 meta_name = "key";
1715 } else {
1716 meta_name = "value";
1717 }
1718 } else {
1719 assert(diff_match->schema->nodetype == LYS_LEAF);
1720
1721 /* switch value for the original one */
1722 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001723 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001724 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vasko52afd7d2022-01-18 14:08:34 +01001725 LOGERR_UNEXPVAL(ctx, diff_match, "target diff");
1726 return LY_EINVAL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001727 }
1728
1729 /* switch default for the original one, then remove the meta */
1730 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
Michal Vasko52afd7d2022-01-18 14:08:34 +01001731 LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001732 diff_match->flags &= ~LYD_DEFAULT;
1733 if (meta->value.boolean) {
1734 diff_match->flags |= LYD_DEFAULT;
1735 }
1736 lyd_free_meta_single(meta);
1737
1738 meta_name = "orig-value";
1739 }
1740 lyd_diff_del_meta(diff_match, meta_name);
1741
Michal Vasko17d0c5c2021-11-01 11:31:11 +01001742 /* it was being changed, but should be deleted instead -> set DELETE operation */
1743 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1744 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001745 case LYD_DIFF_OP_NONE:
1746 /* it was not modified, but should be deleted -> set DELETE operation */
1747 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001748 break;
1749 default:
1750 /* delete operation is not valid */
Michal Vasko52afd7d2022-01-18 14:08:34 +01001751 LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE);
1752 return LY_EINVAL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001753 }
1754
1755 return LY_SUCCESS;
1756}
1757
1758/**
1759 * @brief Check whether this diff node is redundant (does not change data).
1760 *
1761 * @param[in] diff Diff node.
1762 * @return 0 if not, non-zero if it is.
1763 */
1764static int
1765lyd_diff_is_redundant(struct lyd_node *diff)
1766{
1767 enum lyd_diff_op op;
1768 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1769 struct lyd_node *child;
1770 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001771 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001772
1773 assert(diff);
1774
Michal Vaskoe78faec2021-04-08 17:24:43 +02001775 if (lysc_is_dup_inst_list(diff->schema)) {
1776 /* all descendants are keys */
1777 child = NULL;
1778 } else {
1779 child = lyd_child_no_keys(diff);
1780 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001781 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001782 assert(mod);
1783
1784 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001785 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001786
1787 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001788 /* get metadata names */
1789 if (lysc_is_dup_inst_list(diff->schema)) {
1790 meta_name = "position";
1791 orig_meta_name = "orig-position";
1792 } else if (diff->schema->nodetype == LYS_LIST) {
1793 meta_name = "key";
1794 orig_meta_name = "orig-key";
1795 } else {
1796 meta_name = "value";
1797 orig_meta_name = "orig-value";
1798 }
1799
Michal Vaskoe6323f62020-07-09 15:49:02 +02001800 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001801 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1802 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001803 assert(orig_val_meta && val_meta);
1804
1805 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1806 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001807 lyd_free_meta_single(orig_val_meta);
1808 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001809 if (child) {
1810 /* change operation to NONE, we have siblings */
1811 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1812 return 0;
1813 }
1814
1815 /* redundant node, BUT !!
1816 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1817 * because the data that this is applied on should not change for the diff lifetime.
1818 * However, when we are merging 2 diffs, this conversion is actually lossy because
1819 * if the data change, the move operation can also change its meaning. In this specific
1820 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1821 */
1822 return 1;
1823 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001824 } else if (op == LYD_DIFF_OP_NONE) {
1825 if (!diff->schema) {
1826 /* opaque node with none must be redundant */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001827 return 1;
1828 }
Michal Vaskodb91fc32023-05-02 14:39:40 +02001829
1830 if (diff->schema->nodetype & LYD_NODE_TERM) {
1831 /* check whether at least the default flags are different */
1832 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1833 assert(meta);
1834 str = lyd_get_meta_value(meta);
1835
1836 /* if previous and current dflt flags are the same, this node is redundant */
1837 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1838 return 1;
1839 }
1840 return 0;
1841 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001842 }
1843
1844 if (!child && (op == LYD_DIFF_OP_NONE)) {
1845 return 1;
1846 }
1847
1848 return 0;
1849}
1850
1851/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001852 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001853 *
1854 * @param[in] src_diff Source diff node.
1855 * @param[in] diff_parent Current sysrepo diff parent.
1856 * @param[in] diff_cb Optional diff callback.
1857 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001858 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001859 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001860 * @param[in,out] diff Diff root node.
1861 * @return LY_ERR value.
1862 */
1863static LY_ERR
1864lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vasko8efac242023-03-30 08:24:56 +02001865 struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001866{
1867 LY_ERR ret = LY_SUCCESS;
1868 struct lyd_node *child, *diff_node = NULL;
1869 enum lyd_diff_op src_op, cur_op;
Michal Vasko8efac242023-03-30 08:24:56 +02001870 struct ly_ht *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001871
1872 /* get source node operation */
1873 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1874
1875 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001876 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 +02001877
1878 if (diff_node) {
1879 /* get target (current) operation */
1880 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1881
1882 /* merge operations */
1883 switch (src_op) {
1884 case LYD_DIFF_OP_REPLACE:
1885 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1886 break;
1887 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001888 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001889 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001890 goto add_diff;
1891 }
1892
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001893 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001894 break;
1895 case LYD_DIFF_OP_DELETE:
1896 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1897 break;
1898 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001899 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1900 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001901 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1902 break;
1903 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001904 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001905 }
1906 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001907 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001908 return ret;
1909 }
1910
1911 if (diff_cb) {
1912 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001913 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001914 }
1915
1916 /* update diff parent */
1917 diff_parent = diff_node;
1918
Michal Vaskoe78faec2021-04-08 17:24:43 +02001919 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1920 * so there is nothing to merge for these "keys" */
1921 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1922 /* merge src_diff recursively */
1923 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1924 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1925 if (ret) {
1926 break;
1927 }
1928 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001929 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001930 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001931 }
1932 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001933add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001934 /* add new diff node with all descendants */
Michal Vasko9ad76852022-07-12 10:18:03 +02001935 if ((src_diff->flags & LYD_EXT) && diff_parent) {
1936 LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
1937 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1938 } else {
1939 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent,
1940 LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &diff_node));
1941 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001942
1943 /* insert node into diff if not already */
1944 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001945 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001946 }
1947
1948 /* update operation */
1949 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1950
1951 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001952 /* call callback with no source diff node since it was duplicated and just added */
1953 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001954 }
1955
1956 /* update diff parent */
1957 diff_parent = diff_node;
1958 }
1959
1960 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001961 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001962 if (diff_parent == *diff) {
1963 *diff = (*diff)->next;
1964 }
1965 lyd_free_tree(diff_parent);
1966 }
1967
1968 return LY_SUCCESS;
1969}
1970
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001971LIBYANG_API_DEF LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001972lyd_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 +01001973 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001974{
1975 const struct lyd_node *src_root;
Michal Vasko8efac242023-03-30 08:24:56 +02001976 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001977 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001978
1979 LY_LIST_FOR(src_diff, src_root) {
1980 if (mod && (lyd_owner_module(src_root) != mod)) {
1981 /* skip data nodes from different modules */
1982 continue;
1983 }
1984
1985 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001986 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 +02001987 }
1988
Michal Vaskoe78faec2021-04-08 17:24:43 +02001989cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001990 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001991 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001992}
1993
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01001994LIBYANG_API_DEF LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001995lyd_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 +01001996 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001997{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001998 LY_ERR ret;
Michal Vasko8efac242023-03-30 08:24:56 +02001999 struct ly_ht *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02002000
Michal Vasko04f85912020-08-07 12:14:58 +02002001 if (!src_sibling) {
2002 return LY_SUCCESS;
2003 }
2004
Michal Vaskoe78faec2021-04-08 17:24:43 +02002005 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 +02002006 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002007 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02002008}
2009
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002010LIBYANG_API_DEF LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002011lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02002012{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01002013 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02002014}
Michal Vasko4231fb62020-07-13 13:54:47 +02002015
2016static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01002017lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02002018{
2019 LY_ERR ret = LY_SUCCESS;
2020 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002021 const char *val1 = NULL;
2022 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002023 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002024
Michal Vaskobaba84e2021-02-05 16:33:30 +01002025 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
2026
2027 meta = lyd_find_meta(node->meta, mod, "orig-value");
Michal Vasko52afd7d2022-01-18 14:08:34 +01002028 LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002029
2030 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002031 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02002032
2033 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002034 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002035 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01002036 } else {
2037 LY_CHECK_RET(lyd_any_value_str(node, &val2));
2038 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002039
2040 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01002041 flags = node->flags;
2042 if (node->schema->nodetype == LYS_LEAF) {
2043 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
2044 } else {
2045 union lyd_any_value anyval = {.str = val1};
Michal Vasko26bbb272022-08-02 14:54:33 +02002046
Michal Vaskobaba84e2021-02-05 16:33:30 +01002047 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
2048 }
2049 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02002050 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
2051
2052cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002053 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002054 return ret;
2055}
2056
2057static LY_ERR
2058lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
2059{
2060 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02002061 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02002062
2063 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01002064 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02002065
2066 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002067 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02002068 flag1 = LYD_DEFAULT;
2069 } else {
2070 flag1 = 0;
2071 }
2072
2073 /* current default */
2074 flag2 = node->flags & LYD_DEFAULT;
2075
Michal Vasko610e93b2020-11-09 20:58:32 +01002076 if (flag1 == flag2) {
2077 /* no default state change so nothing to reverse */
2078 return LY_SUCCESS;
2079 }
2080
Michal Vasko4231fb62020-07-13 13:54:47 +02002081 /* switch defaults */
2082 node->flags &= ~LYD_DEFAULT;
2083 node->flags |= flag1;
2084 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
2085
2086 return LY_SUCCESS;
2087}
2088
2089static LY_ERR
2090lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
2091{
2092 LY_ERR ret = LY_SUCCESS;
2093 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002094 const char *val1 = NULL;
2095 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02002096
2097 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002098 LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002099
2100 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vasko52afd7d2022-01-18 14:08:34 +01002101 LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL);
Michal Vasko4231fb62020-07-13 13:54:47 +02002102
2103 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002104 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02002105
2106 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002107 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02002108
2109 /* switch values */
2110 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
2111 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
2112
2113cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02002114 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02002115 return ret;
2116}
2117
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002118/**
2119 * @brief Remove specific operation from all the nodes in a subtree.
2120 *
2121 * @param[in] diff Diff subtree to process.
2122 * @param[in] op Only expected operation.
2123 * @return LY_ERR value.
2124 */
2125static LY_ERR
2126lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
2127{
2128 struct lyd_node *elem;
2129 struct lyd_meta *meta;
2130
2131 LYD_TREE_DFS_BEGIN(diff, elem) {
2132 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
2133 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02002134 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 +01002135 lyd_free_meta_single(meta);
2136 }
2137
2138 LYD_TREE_DFS_END(diff, elem);
2139 }
2140
2141 return LY_SUCCESS;
2142}
2143
Jan Kundrátc53a7ec2021-12-09 16:01:19 +01002144LIBYANG_API_DEF LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02002145lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02002146{
2147 LY_ERR ret = LY_SUCCESS;
2148 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002149 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02002150 enum lyd_diff_op op;
2151
2152 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
2153
2154 if (!src_diff) {
2155 *diff = NULL;
2156 return LY_SUCCESS;
2157 }
2158
2159 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02002160 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02002161
2162 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02002163 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
2164 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002165
2166 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02002167 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002168 /* skip all keys */
2169 if (!lysc_is_key(elem->schema)) {
2170 /* find operation attribute, if any */
2171 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02002172
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002173 switch (op) {
2174 case LYD_DIFF_OP_CREATE:
2175 /* reverse create to delete */
2176 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002177
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002178 /* check all the children for the same operation, nothing else is expected */
2179 LY_LIST_FOR(lyd_child(elem), iter) {
2180 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
2181 }
2182
Michal Vasko9e070522021-03-05 14:00:14 +01002183 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002184 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002185 case LYD_DIFF_OP_DELETE:
2186 /* reverse delete to create */
2187 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01002188
Michal Vasko9a7e9d02021-03-09 13:52:25 +01002189 /* check all the children for the same operation, nothing else is expected */
2190 LY_LIST_FOR(lyd_child(elem), iter) {
2191 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
2192 }
2193
Michal Vasko9e070522021-03-05 14:00:14 +01002194 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02002195 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002196 case LYD_DIFF_OP_REPLACE:
2197 switch (elem->schema->nodetype) {
2198 case LYS_LEAF:
2199 /* leaf value change */
2200 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2201 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2202 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01002203 case LYS_ANYXML:
2204 case LYS_ANYDATA:
2205 /* any value change */
2206 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
2207 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002208 case LYS_LEAFLIST:
2209 /* leaf-list move */
2210 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02002211 if (lysc_is_dup_inst_list(elem->schema)) {
2212 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2213 } else {
2214 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
2215 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002216 break;
2217 case LYS_LIST:
2218 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02002219 if (lysc_is_dup_inst_list(elem->schema)) {
2220 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
2221 } else {
2222 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
2223 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002224 break;
2225 default:
2226 LOGINT(LYD_CTX(src_diff));
2227 ret = LY_EINT;
2228 goto cleanup;
2229 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002230 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01002231 case LYD_DIFF_OP_NONE:
2232 switch (elem->schema->nodetype) {
2233 case LYS_LEAF:
2234 case LYS_LEAFLIST:
2235 /* default flag change */
2236 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
2237 break;
2238 default:
2239 /* nothing to do */
2240 break;
2241 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002242 break;
2243 }
Michal Vasko4231fb62020-07-13 13:54:47 +02002244 }
2245
Michal Vasko56daf732020-08-10 10:57:18 +02002246 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02002247 }
2248 }
2249
2250cleanup:
2251 if (ret) {
2252 lyd_free_siblings(*diff);
2253 *diff = NULL;
2254 }
2255 return ret;
2256}