blob: b40dd73ad04773998ae3f875d8dd98f77706ba66 [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 */
15#include <sys/cdefs.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020016
17#include "diff.h"
18
19#include <assert.h>
20#include <stddef.h>
Michal Vaskoe78faec2021-04-08 17:24:43 +020021#include <stdint.h>
22#include <stdio.h>
Radek Krejci47fab892020-11-05 17:02:41 +010023#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020024#include <string.h>
25
26#include "common.h"
Michal Vaskoe78faec2021-04-08 17:24:43 +020027#include "compat.h"
Radek Krejci47fab892020-11-05 17:02:41 +010028#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020029#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010030#include "plugins_types.h"
31#include "set.h"
32#include "tree.h"
33#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020034#include "tree_data_internal.h"
Radek Krejci859a15a2021-03-05 20:56:59 +010035#include "tree_edit.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020036#include "tree_schema.h"
37#include "tree_schema_internal.h"
38
39static const char *
40lyd_diff_op2str(enum lyd_diff_op op)
41{
42 switch (op) {
43 case LYD_DIFF_OP_CREATE:
44 return "create";
45 case LYD_DIFF_OP_DELETE:
46 return "delete";
47 case LYD_DIFF_OP_REPLACE:
48 return "replace";
49 case LYD_DIFF_OP_NONE:
50 return "none";
51 }
52
53 LOGINT(NULL);
54 return NULL;
55}
56
Michal Vaskoe6323f62020-07-09 15:49:02 +020057static enum lyd_diff_op
58lyd_diff_str2op(const char *str)
59{
60 switch (str[0]) {
61 case 'c':
62 assert(!strcmp(str, "create"));
63 return LYD_DIFF_OP_CREATE;
64 case 'd':
65 assert(!strcmp(str, "delete"));
66 return LYD_DIFF_OP_DELETE;
67 case 'r':
68 assert(!strcmp(str, "replace"));
69 return LYD_DIFF_OP_REPLACE;
70 case 'n':
71 assert(!strcmp(str, "none"));
72 return LYD_DIFF_OP_NONE;
73 }
74
75 LOGINT(NULL);
76 return 0;
77}
78
Michal Vaskod59035b2020-07-08 12:00:06 +020079LY_ERR
80lyd_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 +020081 const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
82 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +020083{
84 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
85 const struct lyd_node *parent = NULL;
86 const struct lys_module *yang_mod;
87
88 assert(diff);
89
Michal Vasko53d48422020-11-13 18:02:29 +010090 /* replace leaf always needs orig-default and orig-value */
91 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
92
93 /* create on userord needs key/value */
94 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +020095 (lysc_is_dup_inst_list(node->schema) && position) || key);
Michal Vasko53d48422020-11-13 18:02:29 +010096 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +020097 (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value);
Michal Vasko53d48422020-11-13 18:02:29 +010098
99 /* move on userord needs both key and orig-key/value and orig-value */
100 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200101 (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key));
Michal Vasko53d48422020-11-13 18:02:29 +0100102 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
Michal Vaskoe78faec2021-04-08 17:24:43 +0200103 (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) ||
104 (value && orig_value));
Michal Vasko53d48422020-11-13 18:02:29 +0100105
Michal Vaskod59035b2020-07-08 12:00:06 +0200106 /* find the first existing parent */
107 siblings = *diff;
108 while (1) {
109 /* find next node parent */
110 parent = node;
111 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
Michal Vasko9e685082021-01-29 14:49:09 +0100112 parent = lyd_parent(parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200113 }
114 if (parent == node) {
115 /* no more parents to find */
116 break;
117 }
118
119 /* check whether it exists in the diff */
120 if (lyd_find_sibling_first(siblings, parent, &match)) {
121 break;
122 }
123
124 /* another parent found */
125 diff_parent = match;
126
127 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200128 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200129 }
130
131 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200132 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100133 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200134
135 /* find the first duplicated parent */
136 if (!diff_parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100137 diff_parent = lyd_parent(dup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200138 while (diff_parent && diff_parent->parent) {
Michal Vasko9e685082021-01-29 14:49:09 +0100139 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200140 }
141 } else {
Michal Vasko9e685082021-01-29 14:49:09 +0100142 diff_parent = dup;
Michal Vaskod59035b2020-07-08 12:00:06 +0200143 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100144 diff_parent = lyd_parent(diff_parent);
Michal Vaskod59035b2020-07-08 12:00:06 +0200145 }
146 }
147
148 /* no parent existed, must be manually connected */
149 if (!diff_parent) {
150 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200151 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200152 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200153 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200154 }
155
156 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200157 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200158 assert(!strcmp(yang_mod->name, "yang"));
159
160 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200161 if (diff_parent && (diff_parent != dup)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100162 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200163 }
164
165 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100166 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "operation", lyd_diff_op2str(op), 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200167
168 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200169 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100170 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-default", orig_default, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200171 }
172
173 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200174 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100175 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-value", orig_value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200176 }
177
178 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200179 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100180 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200181 }
182
183 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200184 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100185 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200186 }
187
Michal Vaskoe78faec2021-04-08 17:24:43 +0200188 /* position */
189 if (position) {
190 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "position", position, 0, NULL));
191 }
192
Michal Vaskod59035b2020-07-08 12:00:06 +0200193 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200194 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100195 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-key", orig_key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200196 }
197
Michal Vaskoe78faec2021-04-08 17:24:43 +0200198 /* orig-position */
199 if (orig_position) {
200 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-position", orig_position, 0, NULL));
201 }
202
Michal Vaskod59035b2020-07-08 12:00:06 +0200203 return LY_SUCCESS;
204}
205
206/**
207 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
208 *
Michal Vasko1dcd73b2020-12-08 10:04:33 +0100209 * @param[in] first Node from the first tree, can be NULL (on create).
Michal Vaskod59035b2020-07-08 12:00:06 +0200210 * @param[in] schema Schema node of the list/leaf-list.
211 * @param[in,out] userord Sized array of userord items.
212 * @return Userord item for all the user-ordered list/leaf-list instances.
213 */
214static struct lyd_diff_userord *
215lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
216{
217 struct lyd_diff_userord *item;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200218 struct lyd_node *iter;
219 const struct lyd_node **node;
Michal Vaskod59035b2020-07-08 12:00:06 +0200220 LY_ARRAY_COUNT_TYPE u;
221
222 LY_ARRAY_FOR(*userord, u) {
223 if ((*userord)[u].schema == schema) {
224 return &(*userord)[u];
225 }
226 }
227
228 /* it was not added yet, add it now */
229 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
230
231 item->schema = schema;
232 item->pos = 0;
233 item->inst = NULL;
234
235 /* store all the instance pointers in the current order */
236 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200237 LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) {
238 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
239 *node = iter;
Michal Vaskod59035b2020-07-08 12:00:06 +0200240 }
241 }
242
243 return item;
244}
245
246/**
247 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
248 * lists/leaf-lists.
249 *
250 * @param[in] first Node from the first tree, can be NULL (on create).
251 * @param[in] second Node from the second tree, can be NULL (on delete).
252 * @param[in] options Diff options.
253 * @param[in,out] userord Sized array of userord items for keeping the current node order.
254 * @param[out] op Operation.
255 * @param[out] orig_default Original default metadata.
256 * @param[out] value Value metadata.
257 * @param[out] orig_value Original value metadata
258 * @param[out] key Key metadata.
259 * @param[out] orig_key Original key metadata.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200260 * @param[out] position Position metadata.
261 * @param[out] orig_position Original position metadata.
Michal Vaskod59035b2020-07-08 12:00:06 +0200262 * @return LY_SUCCESS on success,
263 * @return LY_ENOT if there is no change to be added into diff,
264 * @return LY_ERR value on other errors.
265 */
266static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200267lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Radek Krejci0f969882020-08-21 16:56:47 +0200268 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200269 char **orig_value, char **key, char **orig_key, char **position, char **orig_position)
Michal Vaskod59035b2020-07-08 12:00:06 +0200270{
271 const struct lysc_node *schema;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200272 size_t buflen, bufused;
273 uint32_t first_pos, second_pos;
Michal Vaskod59035b2020-07-08 12:00:06 +0200274 struct lyd_diff_userord *userord_item;
275
276 assert(first || second);
277
278 *orig_default = NULL;
279 *value = NULL;
280 *orig_value = NULL;
281 *key = NULL;
282 *orig_key = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200283 *position = NULL;
284 *orig_position = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200285
286 schema = first ? first->schema : second->schema;
287 assert(lysc_is_userordered(schema));
288
289 /* get userord entry */
290 userord_item = lyd_diff_userord_get(first, schema, userord);
291 LY_CHECK_RET(!userord_item, LY_EMEM);
292
Michal Vaskod59035b2020-07-08 12:00:06 +0200293 /* find user-ordered first position */
294 if (first) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200295 for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200296 if (userord_item->inst[first_pos] == first) {
297 break;
298 }
299 }
300 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
301 } else {
302 first_pos = 0;
303 }
304
Michal Vaskoe78faec2021-04-08 17:24:43 +0200305 /* prepare position of the next instance */
306 second_pos = userord_item->pos++;
307
Michal Vaskod59035b2020-07-08 12:00:06 +0200308 /* learn operation first */
309 if (!second) {
310 *op = LYD_DIFF_OP_DELETE;
311 } else if (!first) {
312 *op = LYD_DIFF_OP_CREATE;
313 } else {
Michal Vasko8f359bf2020-07-28 10:41:15 +0200314 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200315 /* in first, there is a different instance on the second position, we are going to move 'first' node */
316 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200317 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200318 /* default flag change */
319 *op = LYD_DIFF_OP_NONE;
320 } else {
321 /* no changes */
322 return LY_ENOT;
323 }
324 }
325
326 /*
327 * set each attribute correctly based on the operation and node type
328 */
329
330 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100331 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200332 if (first->flags & LYD_DEFAULT) {
333 *orig_default = "true";
334 } else {
335 *orig_default = "false";
336 }
337 }
338
339 /* value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200340 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
341 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200342 if (second_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200343 *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200344 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200345 } else {
346 *value = strdup("");
347 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
348 }
349 }
350
351 /* orig-value */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200352 if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) &&
353 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200354 if (first_pos) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200355 *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200356 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200357 } else {
358 *orig_value = strdup("");
359 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
360 }
361 }
362
363 /* key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200364 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
365 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200366 if (second_pos) {
367 buflen = bufused = 0;
368 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
369 } else {
370 *key = strdup("");
371 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
372 }
373 }
374
375 /* orig-key */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200376 if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) &&
377 ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200378 if (first_pos) {
379 buflen = bufused = 0;
380 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
381 } else {
382 *orig_key = strdup("");
383 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
384 }
385 }
386
Michal Vaskoe78faec2021-04-08 17:24:43 +0200387 /* position */
388 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
389 if (second_pos) {
390 if (asprintf(position, "%" PRIu32, second_pos) == -1) {
391 LOGMEM(schema->module->ctx);
392 return LY_EMEM;
393 }
394 } else {
395 *position = strdup("");
396 LY_CHECK_ERR_RET(!*position, LOGMEM(schema->module->ctx), LY_EMEM);
397 }
398 }
399
400 /* orig-position */
401 if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
402 if (first_pos) {
403 if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) {
404 LOGMEM(schema->module->ctx);
405 return LY_EMEM;
406 }
407 } else {
408 *orig_position = strdup("");
409 LY_CHECK_ERR_RET(!*orig_position, LOGMEM(schema->module->ctx), LY_EMEM);
410 }
411 }
412
Michal Vaskod59035b2020-07-08 12:00:06 +0200413 /*
414 * update our instances - apply the change
415 */
416 if (*op == LYD_DIFF_OP_CREATE) {
417 /* insert the instance */
Michal Vasko5cde11b2020-12-08 10:04:48 +0100418 LY_ARRAY_CREATE_RET(schema->module->ctx, userord_item->inst, 1, LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200419 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
420 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
421 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
422 }
423 LY_ARRAY_INCREMENT(userord_item->inst);
424 userord_item->inst[second_pos] = second;
425
426 } else if (*op == LYD_DIFF_OP_DELETE) {
427 /* remove the instance */
428 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
429 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
430 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
431 }
432 LY_ARRAY_DECREMENT(userord_item->inst);
433
434 } else if (*op == LYD_DIFF_OP_REPLACE) {
435 /* move the instances */
436 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
437 (first_pos - second_pos) * sizeof *userord_item->inst);
438 userord_item->inst[second_pos] = first;
439 }
440
441 return LY_SUCCESS;
442}
443
444/**
445 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
446 * lists/leaf-lists.
447 *
448 * @param[in] first Node from the first tree, can be NULL (on create).
449 * @param[in] second Node from the second tree, can be NULL (on delete).
450 * @param[in] options Diff options.
451 * @param[out] op Operation.
452 * @param[out] orig_default Original default metadata.
453 * @param[out] orig_value Original value metadata.
454 * @return LY_SUCCESS on success,
455 * @return LY_ENOT if there is no change to be added into diff,
456 * @return LY_ERR value on other errors.
457 */
458static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200459lyd_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 +0200460 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200461{
462 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200463
464 assert(first || second);
465
466 *orig_default = NULL;
467 *orig_value = NULL;
468
469 schema = first ? first->schema : second->schema;
470 assert(!lysc_is_userordered(schema));
471
472 /* learn operation first */
473 if (!second) {
474 *op = LYD_DIFF_OP_DELETE;
475 } else if (!first) {
476 *op = LYD_DIFF_OP_CREATE;
477 } else {
478 switch (schema->nodetype) {
479 case LYS_CONTAINER:
480 case LYS_RPC:
481 case LYS_ACTION:
482 case LYS_NOTIF:
483 /* no changes */
484 return LY_ENOT;
485 case LYS_LIST:
486 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200487 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200488 /* default flag change */
489 *op = LYD_DIFF_OP_NONE;
490 } else {
491 /* no changes */
492 return LY_ENOT;
493 }
494 break;
495 case LYS_LEAF:
496 case LYS_ANYXML:
497 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200498 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200499 /* different values */
500 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200501 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200502 /* default flag change */
503 *op = LYD_DIFF_OP_NONE;
504 } else {
505 /* no changes */
506 return LY_ENOT;
507 }
508 break;
509 default:
510 LOGINT_RET(schema->module->ctx);
511 }
512 }
513
514 /*
515 * set each attribute correctly based on the operation and node type
516 */
517
518 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100519 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200520 if (first->flags & LYD_DEFAULT) {
521 *orig_default = "true";
522 } else {
523 *orig_default = "false";
524 }
525 }
526
527 /* orig-value */
Michal Vaskobaba84e2021-02-05 16:33:30 +0100528 if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) {
529 if (schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200530 *orig_value = strdup(lyd_get_value(first));
Michal Vaskobaba84e2021-02-05 16:33:30 +0100531 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
532 } else {
533 LY_CHECK_RET(lyd_any_value_str(first, orig_value));
534 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200535 }
536
537 return LY_SUCCESS;
538}
539
540/**
Michal Vaskoe78faec2021-04-08 17:24:43 +0200541 * @brief Find a matching instance of a node in a data tree.
542 *
543 * @param[in] siblings Siblings to search in.
544 * @param[in] target Target node to search for.
545 * @param[in] defaults Whether to consider (or ignore) default values.
546 * @param[in,out] dup_inst_cache Duplicate instance cache.
547 * @param[out] match Found match, NULL if no matching node found.
548 * @return LY_ERR value.
549 */
550static LY_ERR
551lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200552 struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
Michal Vaskoe78faec2021-04-08 17:24:43 +0200553{
Michal Vaskoe78faec2021-04-08 17:24:43 +0200554 if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
555 /* try to find the exact instance */
556 lyd_find_sibling_first(siblings, target, match);
557 } else {
558 /* try to simply find the node, there cannot be more instances */
559 lyd_find_sibling_val(siblings, target->schema, NULL, 0, match);
560 }
561
Michal Vaskod7c048c2021-05-18 16:12:55 +0200562 /* update match as needed */
563 LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
Michal Vaskoe78faec2021-04-08 17:24:43 +0200564
565 if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
566 /* ignore default nodes */
567 *match = NULL;
568 }
569 return LY_SUCCESS;
570}
571
572/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200573 * @brief Perform diff for all siblings at certain depth, recursively.
574 *
575 * For user-ordered lists/leaf-lists a specific structure is used for storing
576 * the current order. The idea is to apply all the generated diff changes
577 * virtually on the first tree so that we can continue to generate correct
578 * changes after some were already generated.
579 *
580 * The algorithm then uses second tree position-based changes with a before
581 * (preceding) item anchor.
582 *
583 * Example:
584 *
585 * Virtual first tree leaf-list order:
586 * 1 2 [3] 4 5
587 *
588 * Second tree leaf-list order:
589 * 1 2 [5] 3 4
590 *
591 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
592 * match - they do not - move nodes so that the 3rd position node is final ->
593 * -> move node 5 to the 3rd position -> move node 5 after node 2.
594 *
595 * Required properties:
596 * Stored operations (move) should not be affected by later operations -
597 * - would cause a redundantly long list of operations, possibly inifinite.
598 *
599 * Implemenation justification:
600 * First, all delete operations and only then move/create operations are stored.
601 * Also, preceding anchor is used and after each iteration another node is
602 * at its final position. That results in the invariant that all preceding
603 * nodes are final and will not be changed by the later operations, meaning
604 * they can safely be used as anchors for the later operations.
605 *
606 * @param[in] first First tree first sibling.
607 * @param[in] second Second tree first sibling.
608 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200609 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200610 * @param[in,out] diff Diff to append to.
611 * @return LY_ERR value.
612 */
613static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200614lyd_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 +0200615 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200616{
617 LY_ERR ret = LY_SUCCESS;
618 const struct lyd_node *iter_first, *iter_second;
619 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200620 struct lyd_diff_userord *userord = NULL;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200621 struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
Michal Vaskod59035b2020-07-08 12:00:06 +0200622 LY_ARRAY_COUNT_TYPE u;
623 enum lyd_diff_op op;
624 const char *orig_default;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200625 char *orig_value, *key, *value, *position, *orig_key, *orig_position;
Michal Vaskod59035b2020-07-08 12:00:06 +0200626
Michal Vaskod59035b2020-07-08 12:00:06 +0200627 /* compare first tree to the second tree - delete, replace, none */
628 LY_LIST_FOR(first, iter_first) {
629 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200630 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200631 /* skip default nodes */
632 continue;
633 }
634
Michal Vaskoe78faec2021-04-08 17:24:43 +0200635 /* find a match in the second tree */
636 LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second,
637 &match_second), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200638
639 if (lysc_is_userordered(iter_first->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200640 /* we are handling only user-ordered node delete now */
641 if (!match_second) {
642 /* get all the attributes */
643 LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
644 &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup);
645
646 /* there must be changes, it is deleted */
647 assert(op == LYD_DIFF_OP_DELETE);
648 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key, orig_position, diff);
649
650 free(orig_value);
651 free(key);
652 free(value);
653 free(position);
654 free(orig_key);
655 free(orig_position);
656 LY_CHECK_GOTO(ret, cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200657 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200658 } else {
659 /* get all the attributes */
660 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
661
662 /* add into diff if there are any changes */
663 if (!ret) {
664 if (op == LYD_DIFF_OP_DELETE) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200665 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200666 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100667 assert(match_second);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200668 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200669 }
670
671 free(orig_value);
672 LY_CHECK_GOTO(ret, cleanup);
673 } else if (ret == LY_ENOT) {
674 ret = LY_SUCCESS;
675 } else {
676 goto cleanup;
677 }
678 }
679
680 /* check descendants, if any, recursively */
681 if (match_second) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200682 LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second),
683 options, 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200684 }
685
686 if (nosiblings) {
687 break;
688 }
689 }
690
691 /* reset all cached positions */
692 LY_ARRAY_FOR(userord, u) {
693 userord[u].pos = 0;
694 }
695
696 /* compare second tree to the first tree - create, user-ordered move */
697 LY_LIST_FOR(second, iter_second) {
698 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200699 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200700 /* skip default nodes */
701 continue;
702 }
703
Michal Vaskoe78faec2021-04-08 17:24:43 +0200704 /* find a match in the first tree */
705 LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first,
706 &match_first), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200707
708 if (lysc_is_userordered(iter_second->schema)) {
709 /* get all the attributes */
710 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200711 &value, &orig_value, &key, &orig_key, &position, &orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200712
713 /* add into diff if there are any changes */
714 if (!ret) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200715 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key,
716 orig_position, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200717
718 free(orig_value);
719 free(key);
720 free(value);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200721 free(position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200722 free(orig_key);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200723 free(orig_position);
Michal Vaskod59035b2020-07-08 12:00:06 +0200724 LY_CHECK_GOTO(ret, cleanup);
725 } else if (ret == LY_ENOT) {
726 ret = LY_SUCCESS;
727 } else {
728 goto cleanup;
729 }
730 } else if (!match_first) {
731 /* get all the attributes */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200732 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 +0200733
734 /* there must be changes, it is created */
735 assert(op == LYD_DIFF_OP_CREATE);
Michal Vaskoe78faec2021-04-08 17:24:43 +0200736 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200737
738 free(orig_value);
739 LY_CHECK_GOTO(ret, cleanup);
740 } /* else was handled */
741
742 if (nosiblings) {
743 break;
744 }
745 }
746
747cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +0200748 lyd_dup_inst_free(dup_inst_first);
749 lyd_dup_inst_free(dup_inst_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200750 LY_ARRAY_FOR(userord, u) {
751 LY_ARRAY_FREE(userord[u].inst);
752 }
753 LY_ARRAY_FREE(userord);
754 return ret;
755}
756
Michal Vasko3a41dff2020-07-15 14:30:28 +0200757static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200758lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200759{
760 const struct ly_ctx *ctx;
761
762 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
763
764 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200765 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200766 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200767 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200768 } else {
769 ctx = NULL;
770 }
771
772 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
773 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
774 return LY_EINVAL;
775 }
776
777 *diff = NULL;
778
Michal Vasko3a41dff2020-07-15 14:30:28 +0200779 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
780}
781
782API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200783lyd_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 +0200784{
785 return lyd_diff(first, second, options, 1, diff);
786}
787
788API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200789lyd_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 +0200790{
791 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200792}
793
794/**
Michal Vaskod59035b2020-07-08 12:00:06 +0200795 * @brief Learn operation of a diff node.
796 *
797 * @param[in] diff_node Diff node.
798 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200799 * @return LY_ERR value.
800 */
801static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200802lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200803{
804 struct lyd_meta *meta = NULL;
805 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200806 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200807
Michal Vasko9e685082021-01-29 14:49:09 +0100808 for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200809 LY_LIST_FOR(diff_parent->meta, meta) {
810 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200811 str = lyd_get_meta_value(meta);
Michal Vaskod59035b2020-07-08 12:00:06 +0200812 if ((str[0] == 'r') && (diff_parent != diff_node)) {
813 /* we do not care about this operation if it's in our parent */
814 continue;
815 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200816 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200817 break;
818 }
819 }
820 if (meta) {
821 break;
822 }
823 }
Michal Vaskob7be7a82020-08-20 09:09:04 +0200824 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200825
Michal Vaskod59035b2020-07-08 12:00:06 +0200826 return LY_SUCCESS;
827}
828
829/**
830 * @brief Insert a diff node into a data tree.
831 *
832 * @param[in,out] first_node First sibling of the data tree.
833 * @param[in] parent_node Data tree sibling parent node.
834 * @param[in] new_node Node to insert.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200835 * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set,
836 * the user-ordered instance will be inserted at the first position.
Michal Vaskod59035b2020-07-08 12:00:06 +0200837 * @return err_info, NULL on success.
838 */
839static LY_ERR
840lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Michal Vaskoe78faec2021-04-08 17:24:43 +0200841 const char *userord_anchor)
Michal Vaskod59035b2020-07-08 12:00:06 +0200842{
843 LY_ERR ret;
844 struct lyd_node *anchor;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200845 uint32_t pos, anchor_pos;
846 int found;
Michal Vaskod59035b2020-07-08 12:00:06 +0200847
848 assert(new_node);
849
850 if (!*first_node) {
851 if (!parent_node) {
852 /* no parent or siblings */
853 *first_node = new_node;
854 return LY_SUCCESS;
855 }
856
857 /* simply insert into parent, no other children */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200858 if (userord_anchor) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200859 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200860 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200861 return LY_EINVAL;
862 }
Michal Vaskob104f112020-07-17 09:54:54 +0200863 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200864 }
865
Michal Vasko9e685082021-01-29 14:49:09 +0100866 assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200867
Michal Vaskod59035b2020-07-08 12:00:06 +0200868 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200869 /* simple insert */
870 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200871 }
872
Michal Vaskoe78faec2021-04-08 17:24:43 +0200873 if (userord_anchor) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200874 /* find the anchor sibling */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200875 if (lysc_is_dup_inst_list(new_node->schema)) {
876 anchor_pos = atoi(userord_anchor);
877 LY_CHECK_ERR_RET(!anchor_pos, LOGINT(LYD_CTX(new_node)), LY_EINT);
878
879 found = 0;
880 pos = 1;
881 LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) {
882 if (pos == anchor_pos) {
883 found = 1;
884 break;
885 }
886 ++pos;
887 }
888 if (!found) {
889 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
890 new_node->schema->name);
891 return LY_EINVAL;
892 }
893 } else {
894 ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor);
895 if (ret == LY_ENOTFOUND) {
896 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
897 new_node->schema->name);
898 return LY_EINVAL;
899 } else if (ret) {
900 return ret;
901 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200902 }
903
904 /* insert after */
905 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
906 assert(new_node->prev == anchor);
907 if (*first_node == new_node) {
908 *first_node = anchor;
909 }
910 } else {
911 if ((*first_node)->schema->flags & LYS_KEY) {
912 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
913
914 /* find last key */
915 anchor = *first_node;
916 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
917 anchor = anchor->next;
918 }
919 /* insert after the last key */
920 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
921 } else {
922 /* insert at the beginning */
923 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
924 *first_node = new_node;
925 }
926 }
927
928 return LY_SUCCESS;
929}
930
931/**
932 * @brief Apply diff subtree on data tree nodes, recursively.
933 *
934 * @param[in,out] first_node First sibling of the data tree.
935 * @param[in] parent_node Parent of the first sibling.
936 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200937 * @param[in] diff_cb Optional diff callback.
938 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +0200939 * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200940 * @return LY_ERR value.
941 */
942static LY_ERR
943lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Michal Vaskod7c048c2021-05-18 16:12:55 +0200944 lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
Michal Vaskod59035b2020-07-08 12:00:06 +0200945{
946 LY_ERR ret;
947 struct lyd_node *match, *diff_child;
Michal Vaskoe78faec2021-04-08 17:24:43 +0200948 const char *str_val, *meta_str;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200949 enum lyd_diff_op op;
950 struct lyd_meta *meta;
Michal Vaskod7c048c2021-05-18 16:12:55 +0200951 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200952 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200953
954 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200955 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200956
Michal Vaskoe6323f62020-07-09 15:49:02 +0200957 /* handle specific user-ordered (leaf-)lists operations separately */
958 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
959 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200960 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe78faec2021-04-08 17:24:43 +0200961 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
962 LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200963 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200964 /* duplicate the node */
965 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200966 }
967
Michal Vaskoe78faec2021-04-08 17:24:43 +0200968 /* get "key", "value", or "position" metadata string value */
969 if (lysc_is_dup_inst_list(diff_node->schema)) {
970 meta_str = "yang:position";
971 } else if (diff_node->schema->nodetype == LYS_LIST) {
972 meta_str = "yang:key";
973 } else {
974 meta_str = "yang:value";
975 }
976 meta = lyd_find_meta(diff_node->meta, NULL, meta_str);
977 LY_CHECK_ERR_RET(!meta, LOGINT(ctx), LY_EINT);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +0200978 str_val = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +0200979
Michal Vaskod59035b2020-07-08 12:00:06 +0200980 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200981 if (str_val[0]) {
982 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200983 } else {
984 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
985 }
986 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200987 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200988 lyd_free_tree(match);
989 }
990 return ret;
991 }
992
993 goto next_iter_r;
994 }
995
996 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200997 switch (op) {
998 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200999 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001000 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
1001 LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +02001002
1003 if (match->schema->nodetype & LYD_NODE_TERM) {
1004 /* special case of only dflt flag change */
1005 if (diff_node->flags & LYD_DEFAULT) {
1006 match->flags |= LYD_DEFAULT;
1007 } else {
1008 match->flags &= ~LYD_DEFAULT;
1009 }
1010 } else {
1011 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001012 if (!lyd_child_no_keys(diff_node)) {
Michal Vaskod59035b2020-07-08 12:00:06 +02001013 LOGINT_RET(ctx);
1014 }
1015 }
1016 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001017 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001018 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001019 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +02001020
1021 /* insert it at the end */
1022 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +02001023 if (parent_node) {
1024 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +02001025 } else {
Michal Vaskob104f112020-07-17 09:54:54 +02001026 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +02001027 }
1028 if (ret) {
1029 lyd_free_tree(match);
1030 return ret;
1031 }
1032
1033 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001034 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +02001035 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001036 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
1037 LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +02001038
1039 /* remove it */
1040 if ((match == *first_node) && !match->parent) {
1041 assert(!parent_node);
1042 /* we have removed the top-level node */
1043 *first_node = (*first_node)->next;
1044 }
1045 lyd_free_tree(match);
1046
1047 /* we are not going recursively in this case, the whole subtree was already deleted */
1048 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001049 case LYD_DIFF_OP_REPLACE:
Michal Vaskobaba84e2021-02-05 16:33:30 +01001050 LY_CHECK_ERR_RET(!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA)), LOGINT(ctx), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +02001051
1052 /* find the node */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001053 LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match));
1054 LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +02001055
Michal Vaskobaba84e2021-02-05 16:33:30 +01001056 /* update the value */
1057 if (diff_node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001058 ret = lyd_change_term(match, lyd_get_value(diff_node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001059 if (ret && (ret != LY_EEXIST)) {
1060 LOGINT_RET(ctx);
1061 }
1062 } else {
1063 struct lyd_node_any *any = (struct lyd_node_any *)diff_node;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001064 LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type));
Michal Vaskod59035b2020-07-08 12:00:06 +02001065 }
1066
1067 /* with flags */
1068 match->flags = diff_node->flags;
1069 break;
1070 default:
1071 LOGINT_RET(ctx);
1072 }
1073
1074next_iter_r:
1075 if (diff_cb) {
1076 /* call callback */
1077 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
1078 }
1079
1080 /* apply diff recursively */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001081 ret = LY_SUCCESS;
Radek Krejcia1c1e542020-09-29 16:06:52 +02001082 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001083 ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst);
1084 if (ret) {
1085 break;
1086 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001087 }
1088
Michal Vaskod7c048c2021-05-18 16:12:55 +02001089 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001090 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001091}
1092
1093API LY_ERR
1094lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001095 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001096{
1097 const struct lyd_node *root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001098 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001099 LY_ERR ret = LY_SUCCESS;
Michal Vaskod59035b2020-07-08 12:00:06 +02001100
1101 LY_LIST_FOR(diff, root) {
1102 if (mod && (lyd_owner_module(root) != mod)) {
1103 /* skip data nodes from different modules */
1104 continue;
1105 }
1106
1107 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001108 ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst);
1109 if (ret) {
1110 break;
1111 }
Michal Vaskod59035b2020-07-08 12:00:06 +02001112 }
1113
Michal Vaskod7c048c2021-05-18 16:12:55 +02001114 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001115 return ret;
Michal Vaskod59035b2020-07-08 12:00:06 +02001116}
1117
1118API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001119lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001120{
1121 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1122}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001123
1124/**
1125 * @brief Update operations on a diff node when the new operation is NONE.
1126 *
1127 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001128 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001129 * @param[in] src_diff Current source diff node.
1130 * @return LY_ERR value.
1131 */
1132static LY_ERR
1133lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1134{
1135 switch (cur_op) {
1136 case LYD_DIFF_OP_NONE:
1137 case LYD_DIFF_OP_CREATE:
1138 case LYD_DIFF_OP_REPLACE:
1139 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1140 /* NONE on a term means only its dflt flag was changed */
1141 diff_match->flags &= ~LYD_DEFAULT;
1142 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1143 }
1144 break;
1145 default:
1146 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001147 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001148 }
1149
1150 return LY_SUCCESS;
1151}
1152
1153/**
1154 * @brief Remove an attribute from a node.
1155 *
1156 * @param[in] node Node with the metadata.
1157 * @param[in] name Metadata name.
1158 */
1159static void
1160lyd_diff_del_meta(struct lyd_node *node, const char *name)
1161{
1162 struct lyd_meta *meta;
1163
1164 LY_LIST_FOR(node->meta, meta) {
1165 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001166 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001167 return;
1168 }
1169 }
1170
1171 assert(0);
1172}
1173
1174/**
1175 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001176 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001177 *
1178 * @param[in] node Node to change.
1179 * @param[in] op Operation to set.
1180 * @return LY_ERR value.
1181 */
1182static LY_ERR
1183lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1184{
1185 struct lyd_meta *meta;
1186
1187 LY_LIST_FOR(node->meta, meta) {
1188 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001189 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001190 break;
1191 }
1192 }
1193
Michal Vasko871a0252020-11-11 18:35:24 +01001194 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001195}
1196
1197/**
1198 * @brief Update operations on a diff node when the new operation is REPLACE.
1199 *
1200 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001201 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001202 * @param[in] src_diff Current source diff node.
1203 * @return LY_ERR value.
1204 */
1205static LY_ERR
1206lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1207{
1208 LY_ERR ret;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001209 const char *str_val, *meta_name, *orig_meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001210 struct lyd_meta *meta;
1211 const struct lys_module *mod;
1212 const struct lyd_node_any *any;
1213
1214 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001215 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001216 assert(mod);
1217
1218 switch (cur_op) {
1219 case LYD_DIFF_OP_REPLACE:
1220 case LYD_DIFF_OP_CREATE:
1221 switch (diff_match->schema->nodetype) {
1222 case LYS_LIST:
1223 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001224 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001225 * keep orig_key/orig_value (only replace oper) and replace key/value */
1226 assert(lysc_is_userordered(diff_match->schema));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001227 if (lysc_is_dup_inst_list(diff_match->schema)) {
1228 meta_name = "position";
1229 } else if (diff_match->schema->nodetype == LYS_LIST) {
1230 meta_name = "key";
1231 } else {
1232 meta_name = "value";
1233 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001234
1235 lyd_diff_del_meta(diff_match, meta_name);
1236 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001237 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001238 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001239 break;
1240 case LYS_LEAF:
1241 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001242 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001243 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001244 }
1245
Michal Vaskoe6323f62020-07-09 15:49:02 +02001246 /* modify the node value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001247 if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001248 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001249 }
1250
Michal Vasko8caadab2020-11-05 17:38:15 +01001251 if (cur_op == LYD_DIFF_OP_REPLACE) {
1252 /* compare values whether there is any change at all */
1253 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1254 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001255 str_val = lyd_get_meta_value(meta);
Michal Vasko8caadab2020-11-05 17:38:15 +01001256 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1257 if (!ret) {
1258 /* values are the same, remove orig-value meta and set oper to NONE */
1259 lyd_free_meta_single(meta);
1260 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1261 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001262 }
1263
1264 /* modify the default flag */
1265 diff_match->flags &= ~LYD_DEFAULT;
1266 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1267 break;
1268 case LYS_ANYXML:
1269 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001270 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001271 /* replaced with the exact same value, impossible */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001272 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001273 }
1274
1275 /* modify the node value */
1276 any = (struct lyd_node_any *)src_diff;
1277 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1278 break;
1279 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001280 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001281 }
1282 break;
1283 case LYD_DIFF_OP_NONE:
1284 /* it is moved now */
1285 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1286
1287 /* change the operation */
1288 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1289
Michal Vaskoe78faec2021-04-08 17:24:43 +02001290 /* set orig-meta and meta */
1291 if (lysc_is_dup_inst_list(diff_match->schema)) {
1292 meta_name = "position";
1293 orig_meta_name = "orig-position";
1294 } else {
1295 meta_name = "key";
1296 orig_meta_name = "orig-key";
1297 }
1298
1299 meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001300 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001301 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001302
Michal Vaskoe78faec2021-04-08 17:24:43 +02001303 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001304 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001305 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001306 break;
1307 default:
1308 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001309 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001310 }
1311
1312 return LY_SUCCESS;
1313}
1314
1315/**
1316 * @brief Update operations in a diff node when the new operation is CREATE.
1317 *
1318 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001319 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001320 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001321 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001322 * @return LY_ERR value.
1323 */
1324static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001325lyd_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 +02001326{
1327 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001328 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001329 uint32_t trg_flags;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001330 const char *meta_name, *orig_meta_name;
1331 struct lyd_meta *meta, *orig_meta;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001332
1333 switch (cur_op) {
1334 case LYD_DIFF_OP_DELETE:
Michal Vasko871a0252020-11-11 18:35:24 +01001335 /* remember current flags */
1336 trg_flags = diff_match->flags;
1337
Michal Vaskoe78faec2021-04-08 17:24:43 +02001338 if (lysc_is_userordered(diff_match->schema)) {
1339 /* get anchor metadata */
1340 if (lysc_is_dup_inst_list(diff_match->schema)) {
1341 meta_name = "yang:position";
1342 orig_meta_name = "yang:orig-position";
1343 } else if (diff_match->schema->nodetype == LYS_LIST) {
1344 meta_name = "yang:key";
1345 orig_meta_name = "yang:orig-key";
1346 } else {
1347 meta_name = "yang:value";
1348 orig_meta_name = "yang:orig-value";
1349 }
1350 meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
1351 orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
1352 LY_CHECK_ERR_RET(!meta || !orig_meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
1353
1354 /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
1355 * the anchors stored in the metadata */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001356 if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001357 /* deleted + created at another position -> operation REPLACE */
1358 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1359
1360 /* add anchor metadata */
1361 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
1362 } else {
1363 /* deleted + created at the same position -> operation NONE */
1364 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1365
1366 /* delete anchor metadata */
1367 lyd_free_meta_single(orig_meta);
1368 }
1369 } else if (diff_match->schema->nodetype == LYS_LEAF) {
1370 if (options & LYD_DIFF_MERGE_DEFAULTS) {
1371 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
1372 sleaf = (struct lysc_node_leaf *)diff_match->schema;
1373 }
1374
Radek Krejci55c4bd22021-04-26 08:09:04 +02001375 if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
1376 &((struct lyd_node_term *)src_diff)->value)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001377 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1378 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1379 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
1380 /* deleted + created -> operation NONE */
1381 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1382 } else {
1383 /* we deleted it, but it was created with a different value -> operation REPLACE */
1384 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1385
1386 /* current value is the previous one (meta) */
1387 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001388 lyd_get_value(diff_match), 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001389
1390 /* update the value itself */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001391 LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001392 }
1393 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001394 /* deleted + created -> operation NONE */
1395 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001396 }
1397
1398 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001399 /* add orig-dflt metadata */
1400 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1401 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1402
Michal Vaskoe6323f62020-07-09 15:49:02 +02001403 /* update dflt flag itself */
1404 diff_match->flags &= ~LYD_DEFAULT;
1405 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001406 }
1407
1408 /* but the operation of its children should remain DELETE */
1409 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
1410 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001411 }
1412 break;
1413 default:
1414 /* create and replace operations are not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001415 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001416 }
1417
1418 return LY_SUCCESS;
1419}
1420
1421/**
1422 * @brief Update operations on a diff node when the new operation is DELETE.
1423 *
1424 * @param[in] diff_match Node from the diff.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001425 * @param[in] cur_op Current operation of @p diff_match.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001426 * @param[in] src_diff Current source diff node.
1427 * @return LY_ERR value.
1428 */
1429static LY_ERR
1430lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1431{
1432 struct lyd_node *next, *child;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001433 struct lyd_meta *meta;
1434 const char *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001435
1436 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001437 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 +02001438
1439 switch (cur_op) {
1440 case LYD_DIFF_OP_CREATE:
1441 /* it was created, but then deleted -> set NONE operation */
1442 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1443
1444 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1445 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001446 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1447 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe78faec2021-04-08 17:24:43 +02001448 } else if (!lysc_is_dup_inst_list(diff_match->schema)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001449 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001450 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001451 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1452 }
Michal Vaskoe78faec2021-04-08 17:24:43 +02001453 } /* else key-less list, for which all the descendants act as keys */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001454 break;
1455 case LYD_DIFF_OP_REPLACE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001456 /* similar to none operation but also remove the redundant metadata */
1457 if (lysc_is_userordered(diff_match->schema)) {
1458 if (lysc_is_dup_inst_list(diff_match->schema)) {
1459 meta_name = "position";
1460 } else if (diff_match->schema->nodetype == LYS_LIST) {
1461 meta_name = "key";
1462 } else {
1463 meta_name = "value";
1464 }
1465 } else {
1466 assert(diff_match->schema->nodetype == LYS_LEAF);
1467
1468 /* switch value for the original one */
1469 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value");
1470 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001471 if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001472 LOGINT_RET(LYD_CTX(src_diff));
1473 }
1474
1475 /* switch default for the original one, then remove the meta */
1476 meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default");
1477 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
1478 diff_match->flags &= ~LYD_DEFAULT;
1479 if (meta->value.boolean) {
1480 diff_match->flags |= LYD_DEFAULT;
1481 }
1482 lyd_free_meta_single(meta);
1483
1484 meta_name = "orig-value";
1485 }
1486 lyd_diff_del_meta(diff_match, meta_name);
1487
Radek Krejcif13b87b2020-12-01 22:02:17 +01001488 /* fall through */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001489 case LYD_DIFF_OP_NONE:
1490 /* it was not modified, but should be deleted -> set DELETE operation */
1491 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1492
Michal Vasko5632e0d2020-07-31 14:13:37 +02001493 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001494 LY_LIST_FOR_SAFE(lyd_child_no_keys(diff_match), next, child) {
1495 if (lyd_find_sibling_first(lyd_child(src_diff), child, NULL) == LY_ENOTFOUND) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001496 lyd_free_tree(child);
1497 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001498 }
1499 break;
1500 default:
1501 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001502 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001503 }
1504
1505 return LY_SUCCESS;
1506}
1507
1508/**
1509 * @brief Check whether this diff node is redundant (does not change data).
1510 *
1511 * @param[in] diff Diff node.
1512 * @return 0 if not, non-zero if it is.
1513 */
1514static int
1515lyd_diff_is_redundant(struct lyd_node *diff)
1516{
1517 enum lyd_diff_op op;
1518 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1519 struct lyd_node *child;
1520 const struct lys_module *mod;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001521 const char *str, *orig_meta_name, *meta_name;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001522
1523 assert(diff);
1524
Michal Vaskoe78faec2021-04-08 17:24:43 +02001525 if (lysc_is_dup_inst_list(diff->schema)) {
1526 /* all descendants are keys */
1527 child = NULL;
1528 } else {
1529 child = lyd_child_no_keys(diff);
1530 }
Michal Vaskob7be7a82020-08-20 09:09:04 +02001531 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001532 assert(mod);
1533
1534 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001535 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001536
1537 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001538 /* get metadata names */
1539 if (lysc_is_dup_inst_list(diff->schema)) {
1540 meta_name = "position";
1541 orig_meta_name = "orig-position";
1542 } else if (diff->schema->nodetype == LYS_LIST) {
1543 meta_name = "key";
1544 orig_meta_name = "orig-key";
1545 } else {
1546 meta_name = "value";
1547 orig_meta_name = "orig-value";
1548 }
1549
Michal Vaskoe6323f62020-07-09 15:49:02 +02001550 /* check for redundant move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001551 orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name);
1552 val_meta = lyd_find_meta(diff->meta, mod, meta_name);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001553 assert(orig_val_meta && val_meta);
1554
1555 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1556 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001557 lyd_free_meta_single(orig_val_meta);
1558 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001559 if (child) {
1560 /* change operation to NONE, we have siblings */
1561 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1562 return 0;
1563 }
1564
1565 /* redundant node, BUT !!
1566 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1567 * because the data that this is applied on should not change for the diff lifetime.
1568 * However, when we are merging 2 diffs, this conversion is actually lossy because
1569 * if the data change, the move operation can also change its meaning. In this specific
1570 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1571 */
1572 return 1;
1573 }
1574 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1575 /* check whether at least the default flags are different */
1576 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1577 assert(meta);
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001578 str = lyd_get_meta_value(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001579
1580 /* if previous and current dflt flags are the same, this node is redundant */
1581 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1582 return 1;
1583 }
1584 return 0;
1585 }
1586
1587 if (!child && (op == LYD_DIFF_OP_NONE)) {
1588 return 1;
1589 }
1590
1591 return 0;
1592}
1593
1594/**
Michal Vaskoe78faec2021-04-08 17:24:43 +02001595 * @brief Merge sysrepo diff subtree with another diff, recursively.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001596 *
1597 * @param[in] src_diff Source diff node.
1598 * @param[in] diff_parent Current sysrepo diff parent.
1599 * @param[in] diff_cb Optional diff callback.
1600 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoe78faec2021-04-08 17:24:43 +02001601 * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001602 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001603 * @param[in,out] diff Diff root node.
1604 * @return LY_ERR value.
1605 */
1606static LY_ERR
1607lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vaskod7c048c2021-05-18 16:12:55 +02001608 struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001609{
1610 LY_ERR ret = LY_SUCCESS;
1611 struct lyd_node *child, *diff_node = NULL;
1612 enum lyd_diff_op src_op, cur_op;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001613 struct lyd_dup_inst *child_dup_inst = NULL;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001614
1615 /* get source node operation */
1616 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1617
1618 /* find an equal node in the current diff */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001619 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 +02001620
1621 if (diff_node) {
1622 /* get target (current) operation */
1623 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1624
1625 /* merge operations */
1626 switch (src_op) {
1627 case LYD_DIFF_OP_REPLACE:
1628 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1629 break;
1630 case LYD_DIFF_OP_CREATE:
Michal Vasko1dc0a842021-02-04 11:04:57 +01001631 if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +02001632 /* special case of creating duplicate (leaf-)list instances */
Michal Vasko1dc0a842021-02-04 11:04:57 +01001633 goto add_diff;
1634 }
1635
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001636 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001637 break;
1638 case LYD_DIFF_OP_DELETE:
1639 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1640 break;
1641 case LYD_DIFF_OP_NONE:
Michal Vaskoe78faec2021-04-08 17:24:43 +02001642 /* key-less list can never have "none" operation since all its descendants are acting as "keys" */
1643 assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001644 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1645 break;
1646 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001647 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001648 }
1649 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001650 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001651 return ret;
1652 }
1653
1654 if (diff_cb) {
1655 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001656 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001657 }
1658
1659 /* update diff parent */
1660 diff_parent = diff_node;
1661
Michal Vaskoe78faec2021-04-08 17:24:43 +02001662 /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances),
1663 * so there is nothing to merge for these "keys" */
1664 if (!lysc_is_dup_inst_list(src_diff->schema)) {
1665 /* merge src_diff recursively */
1666 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
1667 ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff);
1668 if (ret) {
1669 break;
1670 }
1671 }
Michal Vaskod7c048c2021-05-18 16:12:55 +02001672 lyd_dup_inst_free(child_dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001673 LY_CHECK_RET(ret);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001674 }
1675 } else {
Michal Vasko1dc0a842021-02-04 11:04:57 +01001676add_diff:
Michal Vaskoe6323f62020-07-09 15:49:02 +02001677 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001678 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1679 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001680
1681 /* insert node into diff if not already */
1682 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001683 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001684 }
1685
1686 /* update operation */
1687 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1688
1689 if (diff_cb) {
Michal Vaskoe2af8412020-12-03 14:11:38 +01001690 /* call callback with no source diff node since it was duplicated and just added */
1691 LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001692 }
1693
1694 /* update diff parent */
1695 diff_parent = diff_node;
1696 }
1697
1698 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001699 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001700 if (diff_parent == *diff) {
1701 *diff = (*diff)->next;
1702 }
1703 lyd_free_tree(diff_parent);
1704 }
1705
1706 return LY_SUCCESS;
1707}
1708
1709API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001710lyd_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 +01001711 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001712{
1713 const struct lyd_node *src_root;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001714 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001715 LY_ERR ret = LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001716
1717 LY_LIST_FOR(src_diff, src_root) {
1718 if (mod && (lyd_owner_module(src_root) != mod)) {
1719 /* skip data nodes from different modules */
1720 continue;
1721 }
1722
1723 /* apply relevant nodes from the diff datatree */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001724 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 +02001725 }
1726
Michal Vaskoe78faec2021-04-08 17:24:43 +02001727cleanup:
Michal Vaskod7c048c2021-05-18 16:12:55 +02001728 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001729 return ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001730}
1731
1732API LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001733lyd_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 +01001734 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001735{
Michal Vaskoe78faec2021-04-08 17:24:43 +02001736 LY_ERR ret;
Michal Vaskod7c048c2021-05-18 16:12:55 +02001737 struct lyd_dup_inst *dup_inst = NULL;
Michal Vaskoe78faec2021-04-08 17:24:43 +02001738
Michal Vasko04f85912020-08-07 12:14:58 +02001739 if (!src_sibling) {
1740 return LY_SUCCESS;
1741 }
1742
Michal Vaskoe78faec2021-04-08 17:24:43 +02001743 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 +02001744 lyd_dup_inst_free(dup_inst);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001745 return ret;
Michal Vasko04f85912020-08-07 12:14:58 +02001746}
1747
1748API LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001749lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001750{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001751 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001752}
Michal Vasko4231fb62020-07-13 13:54:47 +02001753
1754static LY_ERR
Michal Vaskobaba84e2021-02-05 16:33:30 +01001755lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod)
Michal Vasko4231fb62020-07-13 13:54:47 +02001756{
1757 LY_ERR ret = LY_SUCCESS;
1758 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001759 const char *val1 = NULL;
1760 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001761 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001762
Michal Vaskobaba84e2021-02-05 16:33:30 +01001763 assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA));
1764
1765 meta = lyd_find_meta(node->meta, mod, "orig-value");
1766 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001767
1768 /* orig-value */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001769 val1 = lyd_get_meta_value(meta);
Michal Vasko4231fb62020-07-13 13:54:47 +02001770
1771 /* current value */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001772 if (node->schema->nodetype == LYS_LEAF) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001773 val2 = strdup(lyd_get_value(node));
Michal Vaskobaba84e2021-02-05 16:33:30 +01001774 } else {
1775 LY_CHECK_RET(lyd_any_value_str(node, &val2));
1776 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001777
1778 /* switch values, keep default flag */
Michal Vaskobaba84e2021-02-05 16:33:30 +01001779 flags = node->flags;
1780 if (node->schema->nodetype == LYS_LEAF) {
1781 LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup);
1782 } else {
1783 union lyd_any_value anyval = {.str = val1};
1784 LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup);
1785 }
1786 node->flags = flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001787 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1788
1789cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001790 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001791 return ret;
1792}
1793
1794static LY_ERR
1795lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1796{
1797 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001798 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001799
1800 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001801 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001802
1803 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001804 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001805 flag1 = LYD_DEFAULT;
1806 } else {
1807 flag1 = 0;
1808 }
1809
1810 /* current default */
1811 flag2 = node->flags & LYD_DEFAULT;
1812
Michal Vasko610e93b2020-11-09 20:58:32 +01001813 if (flag1 == flag2) {
1814 /* no default state change so nothing to reverse */
1815 return LY_SUCCESS;
1816 }
1817
Michal Vasko4231fb62020-07-13 13:54:47 +02001818 /* switch defaults */
1819 node->flags &= ~LYD_DEFAULT;
1820 node->flags |= flag1;
1821 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1822
1823 return LY_SUCCESS;
1824}
1825
1826static LY_ERR
1827lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1828{
1829 LY_ERR ret = LY_SUCCESS;
1830 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001831 const char *val1 = NULL;
1832 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001833
1834 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001835 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001836
1837 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001838 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001839
1840 /* value1 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001841 val1 = lyd_get_meta_value(meta1);
Michal Vasko4231fb62020-07-13 13:54:47 +02001842
1843 /* value2 */
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001844 val2 = strdup(lyd_get_meta_value(meta2));
Michal Vasko4231fb62020-07-13 13:54:47 +02001845
1846 /* switch values */
1847 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1848 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1849
1850cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001851 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001852 return ret;
1853}
1854
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001855/**
1856 * @brief Remove specific operation from all the nodes in a subtree.
1857 *
1858 * @param[in] diff Diff subtree to process.
1859 * @param[in] op Only expected operation.
1860 * @return LY_ERR value.
1861 */
1862static LY_ERR
1863lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op)
1864{
1865 struct lyd_node *elem;
1866 struct lyd_meta *meta;
1867
1868 LYD_TREE_DFS_BEGIN(diff, elem) {
1869 meta = lyd_find_meta(elem->meta, NULL, "yang:operation");
1870 if (meta) {
Radek Krejci6d5ba0c2021-04-26 07:49:59 +02001871 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 +01001872 lyd_free_meta_single(meta);
1873 }
1874
1875 LYD_TREE_DFS_END(diff, elem);
1876 }
1877
1878 return LY_SUCCESS;
1879}
1880
Michal Vasko4231fb62020-07-13 13:54:47 +02001881API LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001882lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001883{
1884 LY_ERR ret = LY_SUCCESS;
1885 const struct lys_module *mod;
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001886 struct lyd_node *root, *elem, *iter;
Michal Vasko4231fb62020-07-13 13:54:47 +02001887 enum lyd_diff_op op;
1888
1889 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1890
1891 if (!src_diff) {
1892 *diff = NULL;
1893 return LY_SUCCESS;
1894 }
1895
1896 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001897 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001898
1899 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001900 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1901 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001902
1903 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001904 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001905 /* skip all keys */
1906 if (!lysc_is_key(elem->schema)) {
1907 /* find operation attribute, if any */
1908 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001909
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001910 switch (op) {
1911 case LYD_DIFF_OP_CREATE:
1912 /* reverse create to delete */
1913 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001914
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001915 /* check all the children for the same operation, nothing else is expected */
1916 LY_LIST_FOR(lyd_child(elem), iter) {
1917 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE);
1918 }
1919
Michal Vasko9e070522021-03-05 14:00:14 +01001920 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001921 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001922 case LYD_DIFF_OP_DELETE:
1923 /* reverse delete to create */
1924 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko9e070522021-03-05 14:00:14 +01001925
Michal Vasko9a7e9d02021-03-09 13:52:25 +01001926 /* check all the children for the same operation, nothing else is expected */
1927 LY_LIST_FOR(lyd_child(elem), iter) {
1928 lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE);
1929 }
1930
Michal Vasko9e070522021-03-05 14:00:14 +01001931 LYD_TREE_DFS_continue = 1;
Michal Vasko4231fb62020-07-13 13:54:47 +02001932 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001933 case LYD_DIFF_OP_REPLACE:
1934 switch (elem->schema->nodetype) {
1935 case LYS_LEAF:
1936 /* leaf value change */
1937 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1938 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1939 break;
Michal Vaskobaba84e2021-02-05 16:33:30 +01001940 case LYS_ANYXML:
1941 case LYS_ANYDATA:
1942 /* any value change */
1943 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1944 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001945 case LYS_LEAFLIST:
1946 /* leaf-list move */
1947 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
Michal Vaskoe78faec2021-04-08 17:24:43 +02001948 if (lysc_is_dup_inst_list(elem->schema)) {
1949 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
1950 } else {
1951 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1952 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001953 break;
1954 case LYS_LIST:
1955 /* list move */
Michal Vaskoe78faec2021-04-08 17:24:43 +02001956 if (lysc_is_dup_inst_list(elem->schema)) {
1957 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup);
1958 } else {
1959 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1960 }
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001961 break;
1962 default:
1963 LOGINT(LYD_CTX(src_diff));
1964 ret = LY_EINT;
1965 goto cleanup;
1966 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001967 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001968 case LYD_DIFF_OP_NONE:
1969 switch (elem->schema->nodetype) {
1970 case LYS_LEAF:
1971 case LYS_LEAFLIST:
1972 /* default flag change */
1973 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1974 break;
1975 default:
1976 /* nothing to do */
1977 break;
1978 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001979 break;
1980 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001981 }
1982
Michal Vasko56daf732020-08-10 10:57:18 +02001983 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02001984 }
1985 }
1986
1987cleanup:
1988 if (ret) {
1989 lyd_free_siblings(*diff);
1990 *diff = NULL;
1991 }
1992 return ret;
1993}