blob: 31090f22a780517fa5dddf696376b3579695e612 [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 *
6 * Copyright (c) 2020 CESNET, z.s.p.o.
7 *
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 */
14#define _XOPEN_SOURCE 500
Radek Krejci33185cb2020-07-11 23:15:22 +020015#define _POSIX_C_SOURCE 200809L
Michal Vaskod59035b2020-07-08 12:00:06 +020016
17#include "diff.h"
18
19#include <assert.h>
20#include <stddef.h>
Radek Krejci47fab892020-11-05 17:02:41 +010021#include <stdlib.h>
Michal Vaskod59035b2020-07-08 12:00:06 +020022#include <string.h>
23
24#include "common.h"
Radek Krejci47fab892020-11-05 17:02:41 +010025#include "context.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020026#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010027#include "plugins_types.h"
28#include "set.h"
29#include "tree.h"
30#include "tree_data.h"
Michal Vaskod59035b2020-07-08 12:00:06 +020031#include "tree_data_internal.h"
32#include "tree_schema.h"
33#include "tree_schema_internal.h"
34
35static const char *
36lyd_diff_op2str(enum lyd_diff_op op)
37{
38 switch (op) {
39 case LYD_DIFF_OP_CREATE:
40 return "create";
41 case LYD_DIFF_OP_DELETE:
42 return "delete";
43 case LYD_DIFF_OP_REPLACE:
44 return "replace";
45 case LYD_DIFF_OP_NONE:
46 return "none";
47 }
48
49 LOGINT(NULL);
50 return NULL;
51}
52
Michal Vaskoe6323f62020-07-09 15:49:02 +020053static enum lyd_diff_op
54lyd_diff_str2op(const char *str)
55{
56 switch (str[0]) {
57 case 'c':
58 assert(!strcmp(str, "create"));
59 return LYD_DIFF_OP_CREATE;
60 case 'd':
61 assert(!strcmp(str, "delete"));
62 return LYD_DIFF_OP_DELETE;
63 case 'r':
64 assert(!strcmp(str, "replace"));
65 return LYD_DIFF_OP_REPLACE;
66 case 'n':
67 assert(!strcmp(str, "none"));
68 return LYD_DIFF_OP_NONE;
69 }
70
71 LOGINT(NULL);
72 return 0;
73}
74
Michal Vaskod59035b2020-07-08 12:00:06 +020075LY_ERR
76lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
Radek Krejci0f969882020-08-21 16:56:47 +020077 const char *key, const char *value, const char *orig_key, struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +020078{
79 struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL;
80 const struct lyd_node *parent = NULL;
81 const struct lys_module *yang_mod;
82
83 assert(diff);
84
Michal Vasko53d48422020-11-13 18:02:29 +010085 /* replace leaf always needs orig-default and orig-value */
86 assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value));
87
88 /* create on userord needs key/value */
89 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) ||
90 key);
91 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
92 (op != LYD_DIFF_OP_CREATE) || value);
93
94 /* move on userord needs both key and orig-key/value and orig-value */
95 assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) ||
96 (key && orig_key));
97 assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) ||
98 (op != LYD_DIFF_OP_REPLACE) || (value && orig_value));
99
Michal Vaskod59035b2020-07-08 12:00:06 +0200100 /* find the first existing parent */
101 siblings = *diff;
102 while (1) {
103 /* find next node parent */
104 parent = node;
105 while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
106 parent = (struct lyd_node *)parent->parent;
107 }
108 if (parent == node) {
109 /* no more parents to find */
110 break;
111 }
112
113 /* check whether it exists in the diff */
114 if (lyd_find_sibling_first(siblings, parent, &match)) {
115 break;
116 }
117
118 /* another parent found */
119 diff_parent = match;
120
121 /* move down in the diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200122 siblings = lyd_child_no_keys(match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200123 }
124
125 /* duplicate the subtree (and connect to the diff if possible) */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200126 LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
Michal Vasko871a0252020-11-11 18:35:24 +0100127 LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
Michal Vaskod59035b2020-07-08 12:00:06 +0200128
129 /* find the first duplicated parent */
130 if (!diff_parent) {
131 diff_parent = (struct lyd_node *)dup->parent;
132 while (diff_parent && diff_parent->parent) {
133 diff_parent = (struct lyd_node *)diff_parent->parent;
134 }
135 } else {
136 diff_parent = (struct lyd_node *)dup;
137 while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
138 diff_parent = (struct lyd_node *)diff_parent->parent;
139 }
140 }
141
142 /* no parent existed, must be manually connected */
143 if (!diff_parent) {
144 /* there actually was no parent to duplicate */
Michal Vaskob104f112020-07-17 09:54:54 +0200145 lyd_insert_sibling(*diff, dup, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200146 } else if (!diff_parent->parent) {
Michal Vaskob104f112020-07-17 09:54:54 +0200147 lyd_insert_sibling(*diff, diff_parent, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200148 }
149
150 /* get module with the operation metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200151 yang_mod = LYD_CTX(node)->list.objs[1];
Michal Vaskod59035b2020-07-08 12:00:06 +0200152 assert(!strcmp(yang_mod->name, "yang"));
153
154 /* add parent operation, if any */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200155 if (diff_parent && (diff_parent != dup)) {
Michal Vasko871a0252020-11-11 18:35:24 +0100156 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200157 }
158
159 /* add subtree operation */
Michal Vasko871a0252020-11-11 18:35:24 +0100160 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 +0200161
162 /* orig-default */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200163 if (orig_default) {
Michal Vasko871a0252020-11-11 18:35:24 +0100164 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 +0200165 }
166
167 /* orig-value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200168 if (orig_value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100169 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 +0200170 }
171
172 /* key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200173 if (key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100174 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200175 }
176
177 /* value */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200178 if (value) {
Michal Vasko871a0252020-11-11 18:35:24 +0100179 LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL));
Michal Vaskod59035b2020-07-08 12:00:06 +0200180 }
181
182 /* orig-key */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200183 if (orig_key) {
Michal Vasko871a0252020-11-11 18:35:24 +0100184 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 +0200185 }
186
187 return LY_SUCCESS;
188}
189
190/**
191 * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet.
192 *
193 * @param[in] first
194 * @param[in] schema Schema node of the list/leaf-list.
195 * @param[in,out] userord Sized array of userord items.
196 * @return Userord item for all the user-ordered list/leaf-list instances.
197 */
198static struct lyd_diff_userord *
199lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord)
200{
201 struct lyd_diff_userord *item;
202 const struct lyd_node *iter, **node;
203 LY_ARRAY_COUNT_TYPE u;
204
205 LY_ARRAY_FOR(*userord, u) {
206 if ((*userord)[u].schema == schema) {
207 return &(*userord)[u];
208 }
209 }
210
211 /* it was not added yet, add it now */
212 LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL);
213
214 item->schema = schema;
215 item->pos = 0;
216 item->inst = NULL;
217
218 /* store all the instance pointers in the current order */
219 if (first) {
220 if (first->parent) {
221 iter = first->parent->child;
222 } else {
Radek Krejci1e008d22020-08-17 11:37:37 +0200223 for (iter = first; iter->prev->next; iter = iter->prev) {}
Michal Vaskod59035b2020-07-08 12:00:06 +0200224 }
Michal Vaskod989ba02020-08-24 10:59:24 +0200225 for ( ; iter; iter = iter->next) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200226 if (iter->schema == first->schema) {
227 LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL);
228 *node = iter;
229 }
230 }
231 }
232
233 return item;
234}
235
236/**
237 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered
238 * lists/leaf-lists.
239 *
240 * @param[in] first Node from the first tree, can be NULL (on create).
241 * @param[in] second Node from the second tree, can be NULL (on delete).
242 * @param[in] options Diff options.
243 * @param[in,out] userord Sized array of userord items for keeping the current node order.
244 * @param[out] op Operation.
245 * @param[out] orig_default Original default metadata.
246 * @param[out] value Value metadata.
247 * @param[out] orig_value Original value metadata
248 * @param[out] key Key metadata.
249 * @param[out] orig_key Original key metadata.
250 * @return LY_SUCCESS on success,
251 * @return LY_ENOT if there is no change to be added into diff,
252 * @return LY_ERR value on other errors.
253 */
254static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200255lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options,
Radek Krejci0f969882020-08-21 16:56:47 +0200256 struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value,
257 char **orig_value, char **key, char **orig_key)
Michal Vaskod59035b2020-07-08 12:00:06 +0200258{
259 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200260 size_t buflen, bufused, first_pos, second_pos;
261 struct lyd_diff_userord *userord_item;
262
263 assert(first || second);
264
265 *orig_default = NULL;
266 *value = NULL;
267 *orig_value = NULL;
268 *key = NULL;
269 *orig_key = NULL;
270
271 schema = first ? first->schema : second->schema;
272 assert(lysc_is_userordered(schema));
273
274 /* get userord entry */
275 userord_item = lyd_diff_userord_get(first, schema, userord);
276 LY_CHECK_RET(!userord_item, LY_EMEM);
277
278 /* prepare position of the next instance */
279 second_pos = userord_item->pos++;
280
281 /* find user-ordered first position */
282 if (first) {
283 for (first_pos = second_pos; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) {
284 if (userord_item->inst[first_pos] == first) {
285 break;
286 }
287 }
288 assert(first_pos < LY_ARRAY_COUNT(userord_item->inst));
289 } else {
290 first_pos = 0;
291 }
292
293 /* learn operation first */
294 if (!second) {
295 *op = LYD_DIFF_OP_DELETE;
296 } else if (!first) {
297 *op = LYD_DIFF_OP_CREATE;
298 } else {
299 assert(schema->nodetype & (LYS_LIST | LYS_LEAFLIST));
Michal Vasko8f359bf2020-07-28 10:41:15 +0200300 if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200301 /* in first, there is a different instance on the second position, we are going to move 'first' node */
302 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200303 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200304 /* default flag change */
305 *op = LYD_DIFF_OP_NONE;
306 } else {
307 /* no changes */
308 return LY_ENOT;
309 }
310 }
311
312 /*
313 * set each attribute correctly based on the operation and node type
314 */
315
316 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100317 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200318 if (first->flags & LYD_DEFAULT) {
319 *orig_default = "true";
320 } else {
321 *orig_default = "false";
322 }
323 }
324
325 /* value */
326 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
327 if (second_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200328 *value = strdup(LYD_CANON_VALUE(userord_item->inst[second_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200329 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200330 } else {
331 *value = strdup("");
332 LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM);
333 }
334 }
335
336 /* orig-value */
337 if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
338 if (first_pos) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200339 *orig_value = strdup(LYD_CANON_VALUE(userord_item->inst[first_pos - 1]));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200340 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200341 } else {
342 *orig_value = strdup("");
343 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
344 }
345 }
346
347 /* key */
Michal Vasko44f3d2c2020-08-24 09:49:38 +0200348 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200349 if (second_pos) {
350 buflen = bufused = 0;
351 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0));
352 } else {
353 *key = strdup("");
354 LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM);
355 }
356 }
357
358 /* orig-key */
359 if ((schema->nodetype == LYS_LIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) {
360 if (first_pos) {
361 buflen = bufused = 0;
362 LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0));
363 } else {
364 *orig_key = strdup("");
365 LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM);
366 }
367 }
368
369 /*
370 * update our instances - apply the change
371 */
372 if (*op == LYD_DIFF_OP_CREATE) {
373 /* insert the instance */
374 LY_ARRAY_RESIZE_ERR_RET(schema->module->ctx, userord_item->inst, LY_ARRAY_COUNT(userord_item->inst) + 1,
Michal Vasko69730152020-10-09 16:30:07 +0200375 ; , LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200376 if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) {
377 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
378 (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst);
379 }
380 LY_ARRAY_INCREMENT(userord_item->inst);
381 userord_item->inst[second_pos] = second;
382
383 } else if (*op == LYD_DIFF_OP_DELETE) {
384 /* remove the instance */
385 if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) {
386 memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1,
387 (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst);
388 }
389 LY_ARRAY_DECREMENT(userord_item->inst);
390
391 } else if (*op == LYD_DIFF_OP_REPLACE) {
392 /* move the instances */
393 memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos,
394 (first_pos - second_pos) * sizeof *userord_item->inst);
395 userord_item->inst[second_pos] = first;
396 }
397
398 return LY_SUCCESS;
399}
400
401/**
402 * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered
403 * lists/leaf-lists.
404 *
405 * @param[in] first Node from the first tree, can be NULL (on create).
406 * @param[in] second Node from the second tree, can be NULL (on delete).
407 * @param[in] options Diff options.
408 * @param[out] op Operation.
409 * @param[out] orig_default Original default metadata.
410 * @param[out] orig_value Original value metadata.
411 * @return LY_SUCCESS on success,
412 * @return LY_ENOT if there is no change to be added into diff,
413 * @return LY_ERR value on other errors.
414 */
415static LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200416lyd_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 +0200417 const char **orig_default, char **orig_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200418{
419 const struct lysc_node *schema;
Michal Vaskod59035b2020-07-08 12:00:06 +0200420
421 assert(first || second);
422
423 *orig_default = NULL;
424 *orig_value = NULL;
425
426 schema = first ? first->schema : second->schema;
427 assert(!lysc_is_userordered(schema));
428
429 /* learn operation first */
430 if (!second) {
431 *op = LYD_DIFF_OP_DELETE;
432 } else if (!first) {
433 *op = LYD_DIFF_OP_CREATE;
434 } else {
435 switch (schema->nodetype) {
436 case LYS_CONTAINER:
437 case LYS_RPC:
438 case LYS_ACTION:
439 case LYS_NOTIF:
440 /* no changes */
441 return LY_ENOT;
442 case LYS_LIST:
443 case LYS_LEAFLIST:
Michal Vasko3a41dff2020-07-15 14:30:28 +0200444 if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200445 /* default flag change */
446 *op = LYD_DIFF_OP_NONE;
447 } else {
448 /* no changes */
449 return LY_ENOT;
450 }
451 break;
452 case LYS_LEAF:
453 case LYS_ANYXML:
454 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +0200455 if (lyd_compare_single(first, second, 0)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200456 /* different values */
457 *op = LYD_DIFF_OP_REPLACE;
Michal Vasko3a41dff2020-07-15 14:30:28 +0200458 } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200459 /* default flag change */
460 *op = LYD_DIFF_OP_NONE;
461 } else {
462 /* no changes */
463 return LY_ENOT;
464 }
465 break;
466 default:
467 LOGINT_RET(schema->module->ctx);
468 }
469 }
470
471 /*
472 * set each attribute correctly based on the operation and node type
473 */
474
475 /* orig-default */
Michal Vasko4b715ca2020-11-11 18:39:57 +0100476 if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200477 if (first->flags & LYD_DEFAULT) {
478 *orig_default = "true";
479 } else {
480 *orig_default = "false";
481 }
482 }
483
484 /* orig-value */
485 if ((schema->nodetype == LYS_LEAF) && (*op == LYD_DIFF_OP_REPLACE)) {
486 /* leaf */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200487 *orig_value = strdup(LYD_CANON_VALUE(first));
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200488 LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM);
Michal Vaskod59035b2020-07-08 12:00:06 +0200489 }
490
491 return LY_SUCCESS;
492}
493
494/**
495 * @brief Perform diff for all siblings at certain depth, recursively.
496 *
497 * For user-ordered lists/leaf-lists a specific structure is used for storing
498 * the current order. The idea is to apply all the generated diff changes
499 * virtually on the first tree so that we can continue to generate correct
500 * changes after some were already generated.
501 *
502 * The algorithm then uses second tree position-based changes with a before
503 * (preceding) item anchor.
504 *
505 * Example:
506 *
507 * Virtual first tree leaf-list order:
508 * 1 2 [3] 4 5
509 *
510 * Second tree leaf-list order:
511 * 1 2 [5] 3 4
512 *
513 * We are at the 3rd node now. We look at whether the nodes on the 3rd position
514 * match - they do not - move nodes so that the 3rd position node is final ->
515 * -> move node 5 to the 3rd position -> move node 5 after node 2.
516 *
517 * Required properties:
518 * Stored operations (move) should not be affected by later operations -
519 * - would cause a redundantly long list of operations, possibly inifinite.
520 *
521 * Implemenation justification:
522 * First, all delete operations and only then move/create operations are stored.
523 * Also, preceding anchor is used and after each iteration another node is
524 * at its final position. That results in the invariant that all preceding
525 * nodes are final and will not be changed by the later operations, meaning
526 * they can safely be used as anchors for the later operations.
527 *
528 * @param[in] first First tree first sibling.
529 * @param[in] second Second tree first sibling.
530 * @param[in] options Diff options.
Michal Vasko3a41dff2020-07-15 14:30:28 +0200531 * @param[in] nosiblings Whether to skip following siblings.
Michal Vaskod59035b2020-07-08 12:00:06 +0200532 * @param[in,out] diff Diff to append to.
533 * @return LY_ERR value.
534 */
535static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200536lyd_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 +0200537 struct lyd_node **diff)
Michal Vaskod59035b2020-07-08 12:00:06 +0200538{
539 LY_ERR ret = LY_SUCCESS;
540 const struct lyd_node *iter_first, *iter_second;
541 struct lyd_node *match_second, *match_first;
Michal Vaskod59035b2020-07-08 12:00:06 +0200542 struct lyd_diff_userord *userord = NULL;
543 LY_ARRAY_COUNT_TYPE u;
544 enum lyd_diff_op op;
545 const char *orig_default;
546 char *orig_value, *key, *value, *orig_key;
547
Michal Vaskod59035b2020-07-08 12:00:06 +0200548 /* compare first tree to the second tree - delete, replace, none */
549 LY_LIST_FOR(first, iter_first) {
550 assert(!(iter_first->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200551 if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200552 /* skip default nodes */
553 continue;
554 }
555
556 if (iter_first->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
557 /* try to find the exact instance */
558 lyd_find_sibling_first(second, iter_first, &match_second);
559 } else {
560 /* try to simply find the node, there cannot be more instances */
561 lyd_find_sibling_val(second, iter_first->schema, NULL, 0, &match_second);
562 }
563
Michal Vasko3a41dff2020-07-15 14:30:28 +0200564 if (match_second && (match_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200565 /* ignore default nodes */
566 match_second = NULL;
567 }
568
569 if (lysc_is_userordered(iter_first->schema)) {
570 if (match_second) {
571 /* we are handling only user-ordered node delete now */
572 continue;
573 }
574
575 /* get all the attributes */
576 LY_CHECK_GOTO(lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200577 &value, &orig_value, &key, &orig_key), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200578
579 /* there must be changes, it is deleted */
580 assert(op == LYD_DIFF_OP_DELETE);
581 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, orig_key, diff);
582
583 free(orig_value);
584 free(key);
585 free(value);
586 free(orig_key);
587 LY_CHECK_GOTO(ret, cleanup);
588 } else {
589 /* get all the attributes */
590 ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value);
591
592 /* add into diff if there are any changes */
593 if (!ret) {
594 if (op == LYD_DIFF_OP_DELETE) {
595 ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, diff);
596 } else {
Michal Vasko3c2dd6c2020-11-06 17:38:55 +0100597 assert(match_second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200598 ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
599 }
600
601 free(orig_value);
602 LY_CHECK_GOTO(ret, cleanup);
603 } else if (ret == LY_ENOT) {
604 ret = LY_SUCCESS;
605 } else {
606 goto cleanup;
607 }
608 }
609
610 /* check descendants, if any, recursively */
611 if (match_second) {
Radek Krejcia1c1e542020-09-29 16:06:52 +0200612 LY_CHECK_GOTO(lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second), options,
Michal Vasko69730152020-10-09 16:30:07 +0200613 0, diff), cleanup);
Michal Vaskod59035b2020-07-08 12:00:06 +0200614 }
615
616 if (nosiblings) {
617 break;
618 }
619 }
620
621 /* reset all cached positions */
622 LY_ARRAY_FOR(userord, u) {
623 userord[u].pos = 0;
624 }
625
626 /* compare second tree to the first tree - create, user-ordered move */
627 LY_LIST_FOR(second, iter_second) {
628 assert(!(iter_second->schema->flags & LYS_KEY));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200629 if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200630 /* skip default nodes */
631 continue;
632 }
633
634 if (iter_second->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
635 lyd_find_sibling_first(first, iter_second, &match_first);
636 } else {
637 lyd_find_sibling_val(first, iter_second->schema, NULL, 0, &match_first);
638 }
639
Michal Vasko3a41dff2020-07-15 14:30:28 +0200640 if (match_first && (match_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200641 /* ignore default nodes */
642 match_first = NULL;
643 }
644
645 if (lysc_is_userordered(iter_second->schema)) {
646 /* get all the attributes */
647 ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default,
Michal Vasko69730152020-10-09 16:30:07 +0200648 &value, &orig_value, &key, &orig_key);
Michal Vaskod59035b2020-07-08 12:00:06 +0200649
650 /* add into diff if there are any changes */
651 if (!ret) {
652 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, orig_key, diff);
653
654 free(orig_value);
655 free(key);
656 free(value);
657 free(orig_key);
658 LY_CHECK_GOTO(ret, cleanup);
659 } else if (ret == LY_ENOT) {
660 ret = LY_SUCCESS;
661 } else {
662 goto cleanup;
663 }
664 } else if (!match_first) {
665 /* get all the attributes */
666 LY_CHECK_GOTO(lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup);
667
668 /* there must be changes, it is created */
669 assert(op == LYD_DIFF_OP_CREATE);
670 ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, diff);
671
672 free(orig_value);
673 LY_CHECK_GOTO(ret, cleanup);
674 } /* else was handled */
675
676 if (nosiblings) {
677 break;
678 }
679 }
680
681cleanup:
682 LY_ARRAY_FOR(userord, u) {
683 LY_ARRAY_FREE(userord[u].inst);
684 }
685 LY_ARRAY_FREE(userord);
686 return ret;
687}
688
Michal Vasko3a41dff2020-07-15 14:30:28 +0200689static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200690lyd_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 +0200691{
692 const struct ly_ctx *ctx;
693
694 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
695
696 if (first) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200697 ctx = LYD_CTX(first);
Michal Vaskod59035b2020-07-08 12:00:06 +0200698 } else if (second) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200699 ctx = LYD_CTX(second);
Michal Vaskod59035b2020-07-08 12:00:06 +0200700 } else {
701 ctx = NULL;
702 }
703
704 if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) {
705 LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__);
706 return LY_EINVAL;
707 }
708
709 *diff = NULL;
710
Michal Vasko3a41dff2020-07-15 14:30:28 +0200711 return lyd_diff_siblings_r(first, second, options, nosiblings, diff);
712}
713
714API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200715lyd_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 +0200716{
717 return lyd_diff(first, second, options, 1, diff);
718}
719
720API LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200721lyd_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 +0200722{
723 return lyd_diff(first, second, options, 0, diff);
Michal Vaskod59035b2020-07-08 12:00:06 +0200724}
725
726/**
727 * @brief Find a matching node in data tree for a diff node.
728 *
729 * @param[in] first_node First sibling in the data tree.
730 * @param[in] diff_node Diff node to match.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200731 * @param[out] match_p Matching node, NULL if no found.
Michal Vaskod59035b2020-07-08 12:00:06 +0200732 */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200733static void
Michal Vaskod59035b2020-07-08 12:00:06 +0200734lyd_diff_find_node(const struct lyd_node *first_node, const struct lyd_node *diff_node, struct lyd_node **match_p)
735{
736 if (diff_node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
737 /* try to find the exact instance */
738 lyd_find_sibling_first(first_node, diff_node, match_p);
739 } else {
740 /* try to simply find the node, there cannot be more instances */
741 lyd_find_sibling_val(first_node, diff_node->schema, NULL, 0, match_p);
742 }
Michal Vaskod59035b2020-07-08 12:00:06 +0200743}
744
745/**
746 * @brief Learn operation of a diff node.
747 *
748 * @param[in] diff_node Diff node.
749 * @param[out] op Operation.
Michal Vaskod59035b2020-07-08 12:00:06 +0200750 * @return LY_ERR value.
751 */
752static LY_ERR
Michal Vaskoe6323f62020-07-09 15:49:02 +0200753lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
Michal Vaskod59035b2020-07-08 12:00:06 +0200754{
755 struct lyd_meta *meta = NULL;
756 const struct lyd_node *diff_parent;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200757 const char *str;
Michal Vaskod59035b2020-07-08 12:00:06 +0200758
759 for (diff_parent = diff_node; diff_parent; diff_parent = (struct lyd_node *)diff_parent->parent) {
760 LY_LIST_FOR(diff_parent->meta, meta) {
761 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200762 str = meta->value.canonical;
Michal Vaskod59035b2020-07-08 12:00:06 +0200763 if ((str[0] == 'r') && (diff_parent != diff_node)) {
764 /* we do not care about this operation if it's in our parent */
765 continue;
766 }
Michal Vaskoe6323f62020-07-09 15:49:02 +0200767 *op = lyd_diff_str2op(str);
Michal Vaskod59035b2020-07-08 12:00:06 +0200768 break;
769 }
770 }
771 if (meta) {
772 break;
773 }
774 }
Michal Vaskob7be7a82020-08-20 09:09:04 +0200775 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskod59035b2020-07-08 12:00:06 +0200776
Michal Vaskod59035b2020-07-08 12:00:06 +0200777 return LY_SUCCESS;
778}
779
780/**
781 * @brief Insert a diff node into a data tree.
782 *
783 * @param[in,out] first_node First sibling of the data tree.
784 * @param[in] parent_node Data tree sibling parent node.
785 * @param[in] new_node Node to insert.
786 * @param[in] keys_or_value Optional predicate of relative (leaf-)list instance. If not set, the user-ordered
787 * instance will be inserted at the first position.
788 * @return err_info, NULL on success.
789 */
790static LY_ERR
791lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200792 const char *key_or_value)
Michal Vaskod59035b2020-07-08 12:00:06 +0200793{
794 LY_ERR ret;
795 struct lyd_node *anchor;
796
797 assert(new_node);
798
799 if (!*first_node) {
800 if (!parent_node) {
801 /* no parent or siblings */
802 *first_node = new_node;
803 return LY_SUCCESS;
804 }
805
806 /* simply insert into parent, no other children */
807 if (key_or_value) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200808 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200809 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200810 return LY_EINVAL;
811 }
Michal Vaskob104f112020-07-17 09:54:54 +0200812 return lyd_insert_child(parent_node, new_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200813 }
814
815 assert(!(*first_node)->parent || ((struct lyd_node *)(*first_node)->parent == parent_node));
816
Michal Vaskod59035b2020-07-08 12:00:06 +0200817 if (!lysc_is_userordered(new_node->schema)) {
Michal Vaskob104f112020-07-17 09:54:54 +0200818 /* simple insert */
819 return lyd_insert_sibling(*first_node, new_node, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200820 }
821
822 if (key_or_value) {
823 /* find the anchor sibling */
824 ret = lyd_find_sibling_val(*first_node, new_node->schema, key_or_value, 0, &anchor);
825 if (ret == LY_ENOTFOUND) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200826 LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.",
Michal Vasko69730152020-10-09 16:30:07 +0200827 new_node->schema->name);
Michal Vaskod59035b2020-07-08 12:00:06 +0200828 return LY_EINVAL;
829 } else if (ret) {
830 return ret;
831 }
832
833 /* insert after */
834 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
835 assert(new_node->prev == anchor);
836 if (*first_node == new_node) {
837 *first_node = anchor;
838 }
839 } else {
840 if ((*first_node)->schema->flags & LYS_KEY) {
841 assert(parent_node && (parent_node->schema->nodetype == LYS_LIST));
842
843 /* find last key */
844 anchor = *first_node;
845 while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) {
846 anchor = anchor->next;
847 }
848 /* insert after the last key */
849 LY_CHECK_RET(lyd_insert_after(anchor, new_node));
850 } else {
851 /* insert at the beginning */
852 LY_CHECK_RET(lyd_insert_before(*first_node, new_node));
853 *first_node = new_node;
854 }
855 }
856
857 return LY_SUCCESS;
858}
859
860/**
861 * @brief Apply diff subtree on data tree nodes, recursively.
862 *
863 * @param[in,out] first_node First sibling of the data tree.
864 * @param[in] parent_node Parent of the first sibling.
865 * @param[in] diff_node Current diff node.
Michal Vaskoe6323f62020-07-09 15:49:02 +0200866 * @param[in] diff_cb Optional diff callback.
867 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskod59035b2020-07-08 12:00:06 +0200868 * @return LY_ERR value.
869 */
870static LY_ERR
871lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
Radek Krejci0f969882020-08-21 16:56:47 +0200872 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +0200873{
874 LY_ERR ret;
875 struct lyd_node *match, *diff_child;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200876 const char *str_val;
877 enum lyd_diff_op op;
878 struct lyd_meta *meta;
Michal Vaskob7be7a82020-08-20 09:09:04 +0200879 const struct ly_ctx *ctx = LYD_CTX(diff_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200880
881 /* read all the valid attributes */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200882 LY_CHECK_RET(lyd_diff_get_op(diff_node, &op));
Michal Vaskod59035b2020-07-08 12:00:06 +0200883
Michal Vaskoe6323f62020-07-09 15:49:02 +0200884 /* handle specific user-ordered (leaf-)lists operations separately */
885 if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) {
886 if (op == LYD_DIFF_OP_REPLACE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200887 /* find the node (we must have some siblings because the node was only moved) */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200888 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200889 } else {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200890 /* duplicate the node */
891 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200892 }
893
Michal Vaskoe6323f62020-07-09 15:49:02 +0200894 /* get "key" or "value" metadata string value */
895 meta = lyd_find_meta(diff_node->meta, NULL, diff_node->schema->nodetype == LYS_LIST ? "yang:key" : "yang:value");
Michal Vaskob7be7a82020-08-20 09:09:04 +0200896 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT);
Michal Vaskoba99a3e2020-08-18 15:50:05 +0200897 str_val = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200898
Michal Vaskod59035b2020-07-08 12:00:06 +0200899 /* insert/move the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200900 if (str_val[0]) {
901 ret = lyd_diff_insert(first_node, parent_node, match, str_val);
Michal Vaskod59035b2020-07-08 12:00:06 +0200902 } else {
903 ret = lyd_diff_insert(first_node, parent_node, match, NULL);
904 }
905 if (ret) {
Michal Vaskoe6323f62020-07-09 15:49:02 +0200906 if (op == LYD_DIFF_OP_CREATE) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200907 lyd_free_tree(match);
908 }
909 return ret;
910 }
911
912 goto next_iter_r;
913 }
914
915 /* apply operation */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200916 switch (op) {
917 case LYD_DIFF_OP_NONE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200918 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200919 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200920
921 if (match->schema->nodetype & LYD_NODE_TERM) {
922 /* special case of only dflt flag change */
923 if (diff_node->flags & LYD_DEFAULT) {
924 match->flags |= LYD_DEFAULT;
925 } else {
926 match->flags &= ~LYD_DEFAULT;
927 }
928 } else {
929 /* none operation on nodes without children is redundant and hence forbidden */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200930 if (!lyd_child_no_keys(diff_node)) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200931 LOGINT_RET(ctx);
932 }
933 }
934 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200935 case LYD_DIFF_OP_CREATE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200936 /* duplicate the node */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200937 LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match));
Michal Vaskod59035b2020-07-08 12:00:06 +0200938
939 /* insert it at the end */
940 ret = 0;
Michal Vaskob104f112020-07-17 09:54:54 +0200941 if (parent_node) {
942 ret = lyd_insert_child(parent_node, match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200943 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200944 ret = lyd_insert_sibling(*first_node, match, first_node);
Michal Vaskod59035b2020-07-08 12:00:06 +0200945 }
946 if (ret) {
947 lyd_free_tree(match);
948 return ret;
949 }
950
951 break;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200952 case LYD_DIFF_OP_DELETE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200953 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200954 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200955
956 /* remove it */
957 if ((match == *first_node) && !match->parent) {
958 assert(!parent_node);
959 /* we have removed the top-level node */
960 *first_node = (*first_node)->next;
961 }
962 lyd_free_tree(match);
963
964 /* we are not going recursively in this case, the whole subtree was already deleted */
965 return LY_SUCCESS;
Michal Vaskoe6323f62020-07-09 15:49:02 +0200966 case LYD_DIFF_OP_REPLACE:
Michal Vaskod59035b2020-07-08 12:00:06 +0200967 LY_CHECK_ERR_RET(diff_node->schema->nodetype != LYS_LEAF, LOGINT(ctx), LY_EINT);
968
969 /* find the node */
Michal Vaskoe6323f62020-07-09 15:49:02 +0200970 lyd_diff_find_node(*first_node, diff_node, &match);
Michal Vaskod59035b2020-07-08 12:00:06 +0200971
972 /* update its value */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200973 ret = lyd_change_term(match, LYD_CANON_VALUE(diff_node));
Michal Vaskod59035b2020-07-08 12:00:06 +0200974 if (ret && (ret != LY_EEXIST)) {
975 LOGINT_RET(ctx);
976 }
977
978 /* with flags */
979 match->flags = diff_node->flags;
980 break;
981 default:
982 LOGINT_RET(ctx);
983 }
984
985next_iter_r:
986 if (diff_cb) {
987 /* call callback */
988 LY_CHECK_RET(diff_cb(diff_node, match, cb_data));
989 }
990
991 /* apply diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +0200992 LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
Michal Vaskod59035b2020-07-08 12:00:06 +0200993 LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
994 }
995
996 return LY_SUCCESS;
997}
998
999API LY_ERR
1000lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod,
Radek Krejci0f969882020-08-21 16:56:47 +02001001 lyd_diff_cb diff_cb, void *cb_data)
Michal Vaskod59035b2020-07-08 12:00:06 +02001002{
1003 const struct lyd_node *root;
1004
1005 LY_LIST_FOR(diff, root) {
1006 if (mod && (lyd_owner_module(root) != mod)) {
1007 /* skip data nodes from different modules */
1008 continue;
1009 }
1010
1011 /* apply relevant nodes from the diff datatree */
1012 LY_CHECK_RET(lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data));
1013 }
1014
1015 return LY_SUCCESS;
1016}
1017
1018API LY_ERR
Michal Vasko3a41dff2020-07-15 14:30:28 +02001019lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff)
Michal Vaskod59035b2020-07-08 12:00:06 +02001020{
1021 return lyd_diff_apply_module(data, diff, NULL, NULL, NULL);
1022}
Michal Vaskoe6323f62020-07-09 15:49:02 +02001023
1024/**
1025 * @brief Update operations on a diff node when the new operation is NONE.
1026 *
1027 * @param[in] diff_match Node from the diff.
1028 * @param[in] cur_op Current operation of the diff node.
1029 * @param[in] src_diff Current source diff node.
1030 * @return LY_ERR value.
1031 */
1032static LY_ERR
1033lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1034{
1035 switch (cur_op) {
1036 case LYD_DIFF_OP_NONE:
1037 case LYD_DIFF_OP_CREATE:
1038 case LYD_DIFF_OP_REPLACE:
1039 if (src_diff->schema->nodetype & LYD_NODE_TERM) {
1040 /* NONE on a term means only its dflt flag was changed */
1041 diff_match->flags &= ~LYD_DEFAULT;
1042 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1043 }
1044 break;
1045 default:
1046 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001047 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001048 }
1049
1050 return LY_SUCCESS;
1051}
1052
1053/**
1054 * @brief Remove an attribute from a node.
1055 *
1056 * @param[in] node Node with the metadata.
1057 * @param[in] name Metadata name.
1058 */
1059static void
1060lyd_diff_del_meta(struct lyd_node *node, const char *name)
1061{
1062 struct lyd_meta *meta;
1063
1064 LY_LIST_FOR(node->meta, meta) {
1065 if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001066 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001067 return;
1068 }
1069 }
1070
1071 assert(0);
1072}
1073
1074/**
1075 * @brief Set a specific operation of a node. Delete the previous operation, if any.
Michal Vasko871a0252020-11-11 18:35:24 +01001076 * Does not change the default flag.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001077 *
1078 * @param[in] node Node to change.
1079 * @param[in] op Operation to set.
1080 * @return LY_ERR value.
1081 */
1082static LY_ERR
1083lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
1084{
1085 struct lyd_meta *meta;
1086
1087 LY_LIST_FOR(node->meta, meta) {
1088 if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
Michal Vasko3a41dff2020-07-15 14:30:28 +02001089 lyd_free_meta_single(meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001090 break;
1091 }
1092 }
1093
Michal Vasko871a0252020-11-11 18:35:24 +01001094 return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001095}
1096
1097/**
1098 * @brief Update operations on a diff node when the new operation is REPLACE.
1099 *
1100 * @param[in] diff_match Node from the diff.
1101 * @param[in] cur_op Current operation of the diff node.
1102 * @param[in] src_diff Current source diff node.
1103 * @return LY_ERR value.
1104 */
1105static LY_ERR
1106lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1107{
1108 LY_ERR ret;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001109 const char *str_val, *meta_name;
1110 struct lyd_meta *meta;
1111 const struct lys_module *mod;
1112 const struct lyd_node_any *any;
1113
1114 /* get "yang" module for the metadata */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001115 mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001116 assert(mod);
1117
1118 switch (cur_op) {
1119 case LYD_DIFF_OP_REPLACE:
1120 case LYD_DIFF_OP_CREATE:
1121 switch (diff_match->schema->nodetype) {
1122 case LYS_LIST:
1123 case LYS_LEAFLIST:
Michal Vasko4231fb62020-07-13 13:54:47 +02001124 /* it was created/moved somewhere, but now it will be created/moved somewhere else,
Michal Vaskoe6323f62020-07-09 15:49:02 +02001125 * keep orig_key/orig_value (only replace oper) and replace key/value */
1126 assert(lysc_is_userordered(diff_match->schema));
1127 meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
1128
1129 lyd_diff_del_meta(diff_match, meta_name);
1130 meta = lyd_find_meta(src_diff->meta, mod, meta_name);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001131 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001132 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001133 break;
1134 case LYS_LEAF:
1135 /* replaced with the exact same value, impossible */
Michal Vasko8f359bf2020-07-28 10:41:15 +02001136 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001137 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001138 }
1139
Michal Vaskoe6323f62020-07-09 15:49:02 +02001140 /* modify the node value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001141 if (lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff))) {
1142 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001143 }
1144
Michal Vasko8caadab2020-11-05 17:38:15 +01001145 if (cur_op == LYD_DIFF_OP_REPLACE) {
1146 /* compare values whether there is any change at all */
1147 meta = lyd_find_meta(diff_match->meta, mod, "orig-value");
1148 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT);
1149 str_val = meta->value.canonical;
1150 ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val));
1151 if (!ret) {
1152 /* values are the same, remove orig-value meta and set oper to NONE */
1153 lyd_free_meta_single(meta);
1154 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1155 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001156 }
1157
1158 /* modify the default flag */
1159 diff_match->flags &= ~LYD_DEFAULT;
1160 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1161 break;
1162 case LYS_ANYXML:
1163 case LYS_ANYDATA:
Michal Vasko8f359bf2020-07-28 10:41:15 +02001164 if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001165 /* replaced with the exact same value, impossible */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001166 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001167 }
1168
1169 /* modify the node value */
1170 any = (struct lyd_node_any *)src_diff;
1171 LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type));
1172 break;
1173 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001174 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001175 }
1176 break;
1177 case LYD_DIFF_OP_NONE:
1178 /* it is moved now */
1179 assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
1180
1181 /* change the operation */
1182 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1183
1184 /* set orig-key and key metadata */
1185 meta = lyd_find_meta(src_diff->meta, mod, "orig-key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001186 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001187 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001188
1189 meta = lyd_find_meta(src_diff->meta, mod, "key");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001190 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT);
Michal Vasko3a41dff2020-07-15 14:30:28 +02001191 LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001192 break;
1193 default:
1194 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001195 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001196 }
1197
1198 return LY_SUCCESS;
1199}
1200
1201/**
1202 * @brief Update operations in a diff node when the new operation is CREATE.
1203 *
1204 * @param[in] diff_match Node from the diff.
1205 * @param[in] cur_op Current operation of the diff node.
1206 * @param[in] src_diff Current source diff node.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001207 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001208 * @return LY_ERR value.
1209 */
1210static LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001211lyd_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 +02001212{
1213 struct lyd_node *child;
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001214 const struct lysc_node_leaf *sleaf = NULL;
Michal Vasko871a0252020-11-11 18:35:24 +01001215 uint32_t trg_flags;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001216
1217 switch (cur_op) {
1218 case LYD_DIFF_OP_DELETE:
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001219 if ((options & LYD_DIFF_MERGE_DEFAULTS) && (diff_match->schema->nodetype == LYS_LEAF)) {
1220 /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
Michal Vasko5632e0d2020-07-31 14:13:37 +02001221 sleaf = (struct lysc_node_leaf *)diff_match->schema;
Michal Vasko5632e0d2020-07-31 14:13:37 +02001222 }
1223
Michal Vasko871a0252020-11-11 18:35:24 +01001224 /* remember current flags */
1225 trg_flags = diff_match->flags;
1226
Michal Vasko69730152020-10-09 16:30:07 +02001227 if (sleaf && sleaf->dflt &&
1228 !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, &((struct lyd_node_term *)src_diff)->value)) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001229 /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
1230 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vasko5632e0d2020-07-31 14:13:37 +02001231 } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001232 /* deleted + created -> operation NONE */
1233 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001234 } else {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001235 /* we deleted it, but it was created with a different value -> operation REPLACE */
1236 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
1237
1238 /* current value is the previous one (meta) */
Michal Vasko871a0252020-11-11 18:35:24 +01001239 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
1240 LYD_CANON_VALUE(diff_match), 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001241
1242 /* update the value itself */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001243 LY_CHECK_RET(lyd_change_term(diff_match, LYD_CANON_VALUE(src_diff)));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001244 }
1245
1246 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
Michal Vasko4b715ca2020-11-11 18:39:57 +01001247 /* add orig-dflt metadata */
1248 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1249 trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
1250
Michal Vaskoe6323f62020-07-09 15:49:02 +02001251 /* update dflt flag itself */
1252 diff_match->flags &= ~LYD_DEFAULT;
1253 diff_match->flags |= src_diff->flags & LYD_DEFAULT;
1254 } else {
1255 /* but the operation of its children should remain DELETE */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001256 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001257 LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
1258 }
1259 }
1260 break;
1261 default:
1262 /* create and replace operations are not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001263 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001264 }
1265
1266 return LY_SUCCESS;
1267}
1268
1269/**
1270 * @brief Update operations on a diff node when the new operation is DELETE.
1271 *
1272 * @param[in] diff_match Node from the diff.
1273 * @param[in] cur_op Current operation of the diff node.
1274 * @param[in] src_diff Current source diff node.
1275 * @return LY_ERR value.
1276 */
1277static LY_ERR
1278lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff)
1279{
1280 struct lyd_node *next, *child;
1281
1282 /* we can delete only exact existing nodes */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001283 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 +02001284
1285 switch (cur_op) {
1286 case LYD_DIFF_OP_CREATE:
1287 /* it was created, but then deleted -> set NONE operation */
1288 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
1289
1290 if (diff_match->schema->nodetype & LYD_NODE_TERM) {
1291 /* add orig-default meta because it is expected */
Michal Vasko871a0252020-11-11 18:35:24 +01001292 LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
1293 diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001294 } else {
1295 /* keep operation for all descendants (for now) */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001296 LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001297 LY_CHECK_RET(lyd_diff_change_op(child, cur_op));
1298 }
1299 }
1300 break;
1301 case LYD_DIFF_OP_REPLACE:
1302 /* similar to none operation but also remove the redundant attribute */
1303 lyd_diff_del_meta(diff_match, "orig-value");
Radek Krejci0f969882020-08-21 16:56:47 +02001304 /* fallthrough */
Michal Vaskoe6323f62020-07-09 15:49:02 +02001305 case LYD_DIFF_OP_NONE:
1306 /* it was not modified, but should be deleted -> set DELETE operation */
1307 LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE));
1308
Michal Vasko5632e0d2020-07-31 14:13:37 +02001309 /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001310 LY_LIST_FOR_SAFE(lyd_child_no_keys(diff_match), next, child) {
1311 if (lyd_find_sibling_first(lyd_child(src_diff), child, NULL) == LY_ENOTFOUND) {
Michal Vasko5632e0d2020-07-31 14:13:37 +02001312 lyd_free_tree(child);
1313 }
Michal Vaskoe6323f62020-07-09 15:49:02 +02001314 }
1315 break;
1316 default:
1317 /* delete operation is not valid */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001318 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001319 }
1320
1321 return LY_SUCCESS;
1322}
1323
1324/**
1325 * @brief Check whether this diff node is redundant (does not change data).
1326 *
1327 * @param[in] diff Diff node.
1328 * @return 0 if not, non-zero if it is.
1329 */
1330static int
1331lyd_diff_is_redundant(struct lyd_node *diff)
1332{
1333 enum lyd_diff_op op;
1334 struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL;
1335 struct lyd_node *child;
1336 const struct lys_module *mod;
1337 const char *str;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001338
1339 assert(diff);
1340
Radek Krejcia1c1e542020-09-29 16:06:52 +02001341 child = lyd_child_no_keys(diff);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001342 mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang");
Michal Vaskoe6323f62020-07-09 15:49:02 +02001343 assert(mod);
1344
1345 /* get node operation */
Michal Vasko53bf6f22020-07-14 08:23:40 +02001346 LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001347
1348 if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) {
1349 /* check for redundant move */
1350 orig_val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "orig-key" : "orig-value"));
1351 val_meta = lyd_find_meta(diff->meta, mod, (diff->schema->nodetype == LYS_LIST ? "key" : "value"));
1352 assert(orig_val_meta && val_meta);
1353
1354 if (!lyd_compare_meta(orig_val_meta, val_meta)) {
1355 /* there is actually no move */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001356 lyd_free_meta_single(orig_val_meta);
1357 lyd_free_meta_single(val_meta);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001358 if (child) {
1359 /* change operation to NONE, we have siblings */
1360 lyd_diff_change_op(diff, LYD_DIFF_OP_NONE);
1361 return 0;
1362 }
1363
1364 /* redundant node, BUT !!
1365 * In diff the move operation is always converted to be INSERT_AFTER, which is fine
1366 * because the data that this is applied on should not change for the diff lifetime.
1367 * However, when we are merging 2 diffs, this conversion is actually lossy because
1368 * if the data change, the move operation can also change its meaning. In this specific
1369 * case the move operation will be lost. But it can be considered a feature, it is not supported.
1370 */
1371 return 1;
1372 }
1373 } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
1374 /* check whether at least the default flags are different */
1375 meta = lyd_find_meta(diff->meta, mod, "orig-default");
1376 assert(meta);
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001377 str = meta->value.canonical;
Michal Vaskoe6323f62020-07-09 15:49:02 +02001378
1379 /* if previous and current dflt flags are the same, this node is redundant */
1380 if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
1381 return 1;
1382 }
1383 return 0;
1384 }
1385
1386 if (!child && (op == LYD_DIFF_OP_NONE)) {
1387 return 1;
1388 }
1389
1390 return 0;
1391}
1392
1393/**
1394 * @brief Merge sysrepo diff with another diff, recursively.
1395 *
1396 * @param[in] src_diff Source diff node.
1397 * @param[in] diff_parent Current sysrepo diff parent.
1398 * @param[in] diff_cb Optional diff callback.
1399 * @param[in] cb_data User data for @p diff_cb.
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001400 * @param[in] options Diff merge options.
Michal Vaskoe6323f62020-07-09 15:49:02 +02001401 * @param[in,out] diff Diff root node.
1402 * @return LY_ERR value.
1403 */
1404static LY_ERR
1405lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001406 uint16_t options, struct lyd_node **diff)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001407{
1408 LY_ERR ret = LY_SUCCESS;
1409 struct lyd_node *child, *diff_node = NULL;
1410 enum lyd_diff_op src_op, cur_op;
1411
1412 /* get source node operation */
1413 LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
1414
1415 /* find an equal node in the current diff */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001416 lyd_diff_find_node(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, &diff_node);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001417
1418 if (diff_node) {
1419 /* get target (current) operation */
1420 LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op));
1421
1422 /* merge operations */
1423 switch (src_op) {
1424 case LYD_DIFF_OP_REPLACE:
1425 ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff);
1426 break;
1427 case LYD_DIFF_OP_CREATE:
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001428 ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001429 break;
1430 case LYD_DIFF_OP_DELETE:
1431 ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
1432 break;
1433 case LYD_DIFF_OP_NONE:
1434 ret = lyd_diff_merge_none(diff_node, cur_op, src_diff);
1435 break;
1436 default:
Michal Vaskob7be7a82020-08-20 09:09:04 +02001437 LOGINT_RET(LYD_CTX(src_diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001438 }
1439 if (ret) {
Michal Vaskob7be7a82020-08-20 09:09:04 +02001440 LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001441 return ret;
1442 }
1443
1444 if (diff_cb) {
1445 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001446 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001447 }
1448
1449 /* update diff parent */
1450 diff_parent = diff_node;
1451
1452 /* merge src_diff recursively */
Radek Krejcia1c1e542020-09-29 16:06:52 +02001453 LY_LIST_FOR(lyd_child_no_keys(src_diff), child) {
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001454 LY_CHECK_RET(lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001455 }
1456 } else {
1457 /* add new diff node with all descendants */
Michal Vasko871a0252020-11-11 18:35:24 +01001458 LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS,
1459 &diff_node));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001460
1461 /* insert node into diff if not already */
1462 if (!diff_parent) {
Michal Vaskob104f112020-07-17 09:54:54 +02001463 lyd_insert_sibling(*diff, diff_node, diff);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001464 }
1465
1466 /* update operation */
1467 LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op));
1468
1469 if (diff_cb) {
1470 /* call callback */
Michal Vaskobc5fba92020-08-07 12:14:39 +02001471 LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001472 }
1473
1474 /* update diff parent */
1475 diff_parent = diff_node;
1476 }
1477
1478 /* remove any redundant nodes */
Michal Vaskob98d7082020-07-15 16:38:36 +02001479 if (lyd_diff_is_redundant(diff_parent)) {
Michal Vaskoe6323f62020-07-09 15:49:02 +02001480 if (diff_parent == *diff) {
1481 *diff = (*diff)->next;
1482 }
1483 lyd_free_tree(diff_parent);
1484 }
1485
1486 return LY_SUCCESS;
1487}
1488
1489API LY_ERR
Michal Vaskofb737aa2020-08-06 13:53:53 +02001490lyd_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 +01001491 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001492{
1493 const struct lyd_node *src_root;
1494
1495 LY_LIST_FOR(src_diff, src_root) {
1496 if (mod && (lyd_owner_module(src_root) != mod)) {
1497 /* skip data nodes from different modules */
1498 continue;
1499 }
1500
1501 /* apply relevant nodes from the diff datatree */
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001502 LY_CHECK_RET(lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, options, diff));
Michal Vaskoe6323f62020-07-09 15:49:02 +02001503 }
1504
1505 return LY_SUCCESS;
1506}
1507
1508API LY_ERR
Michal Vasko04f85912020-08-07 12:14:58 +02001509lyd_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 +01001510 lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
Michal Vasko04f85912020-08-07 12:14:58 +02001511{
1512 if (!src_sibling) {
1513 return LY_SUCCESS;
1514 }
1515
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001516 return lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, options, diff_first);
Michal Vasko04f85912020-08-07 12:14:58 +02001517}
1518
1519API LY_ERR
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001520lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options)
Michal Vaskoe6323f62020-07-09 15:49:02 +02001521{
Michal Vaskoc0e58e82020-11-11 19:04:33 +01001522 return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options);
Michal Vaskoe6323f62020-07-09 15:49:02 +02001523}
Michal Vasko4231fb62020-07-13 13:54:47 +02001524
1525static LY_ERR
1526lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
1527{
1528 LY_ERR ret = LY_SUCCESS;
1529 struct lyd_meta *meta;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001530 const char *val1 = NULL;
1531 char *val2;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001532 uint32_t flags;
Michal Vasko4231fb62020-07-13 13:54:47 +02001533
1534 meta = lyd_find_meta(leaf->meta, mod, "orig-value");
Michal Vaskob7be7a82020-08-20 09:09:04 +02001535 LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(leaf)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001536
1537 /* orig-value */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001538 val1 = meta->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001539
1540 /* current value */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001541 val2 = strdup(LYD_CANON_VALUE(leaf));
Michal Vasko4231fb62020-07-13 13:54:47 +02001542
1543 /* switch values, keep default flag */
1544 flags = leaf->flags;
1545 LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
1546 leaf->flags = flags;
1547 LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
1548
1549cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001550 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001551 return ret;
1552}
1553
1554static LY_ERR
1555lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
1556{
1557 struct lyd_meta *meta;
Radek Krejci1deb5be2020-08-26 16:43:36 +02001558 uint32_t flag1, flag2;
Michal Vasko4231fb62020-07-13 13:54:47 +02001559
1560 meta = lyd_find_meta(node->meta, mod, "orig-default");
Michal Vasko610e93b2020-11-09 20:58:32 +01001561 LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001562
1563 /* orig-default */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001564 if (meta->value.boolean) {
Michal Vasko4231fb62020-07-13 13:54:47 +02001565 flag1 = LYD_DEFAULT;
1566 } else {
1567 flag1 = 0;
1568 }
1569
1570 /* current default */
1571 flag2 = node->flags & LYD_DEFAULT;
1572
Michal Vasko610e93b2020-11-09 20:58:32 +01001573 if (flag1 == flag2) {
1574 /* no default state change so nothing to reverse */
1575 return LY_SUCCESS;
1576 }
1577
Michal Vasko4231fb62020-07-13 13:54:47 +02001578 /* switch defaults */
1579 node->flags &= ~LYD_DEFAULT;
1580 node->flags |= flag1;
1581 LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
1582
1583 return LY_SUCCESS;
1584}
1585
1586static LY_ERR
1587lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
1588{
1589 LY_ERR ret = LY_SUCCESS;
1590 struct lyd_meta *meta1, *meta2;
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001591 const char *val1 = NULL;
1592 char *val2 = NULL;
Michal Vasko4231fb62020-07-13 13:54:47 +02001593
1594 meta1 = lyd_find_meta(node->meta, mod, name1);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001595 LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001596
1597 meta2 = lyd_find_meta(node->meta, mod, name2);
Michal Vaskob7be7a82020-08-20 09:09:04 +02001598 LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_CTX(node)), LY_EINT);
Michal Vasko4231fb62020-07-13 13:54:47 +02001599
1600 /* value1 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001601 val1 = meta1->value.canonical;
Michal Vasko4231fb62020-07-13 13:54:47 +02001602
1603 /* value2 */
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001604 val2 = strdup(meta2->value.canonical);
Michal Vasko4231fb62020-07-13 13:54:47 +02001605
1606 /* switch values */
1607 LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
1608 LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
1609
1610cleanup:
Michal Vaskoba99a3e2020-08-18 15:50:05 +02001611 free(val2);
Michal Vasko4231fb62020-07-13 13:54:47 +02001612 return ret;
1613}
1614
1615API LY_ERR
Michal Vasko66535812020-08-11 08:44:22 +02001616lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff)
Michal Vasko4231fb62020-07-13 13:54:47 +02001617{
1618 LY_ERR ret = LY_SUCCESS;
1619 const struct lys_module *mod;
Michal Vasko56daf732020-08-10 10:57:18 +02001620 struct lyd_node *root, *elem;
Michal Vasko4231fb62020-07-13 13:54:47 +02001621 enum lyd_diff_op op;
1622
1623 LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
1624
1625 if (!src_diff) {
1626 *diff = NULL;
1627 return LY_SUCCESS;
1628 }
1629
1630 /* duplicate diff */
Michal Vasko3a41dff2020-07-15 14:30:28 +02001631 LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff));
Michal Vasko4231fb62020-07-13 13:54:47 +02001632
1633 /* find module with metadata needed for later */
Michal Vaskob7be7a82020-08-20 09:09:04 +02001634 mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang");
1635 LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001636
1637 LY_LIST_FOR(*diff, root) {
Michal Vasko56daf732020-08-10 10:57:18 +02001638 LYD_TREE_DFS_BEGIN(root, elem) {
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001639 /* skip all keys */
1640 if (!lysc_is_key(elem->schema)) {
1641 /* find operation attribute, if any */
1642 LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001643
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001644 switch (op) {
1645 case LYD_DIFF_OP_CREATE:
1646 /* reverse create to delete */
1647 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001648 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001649 case LYD_DIFF_OP_DELETE:
1650 /* reverse delete to create */
1651 LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
Michal Vasko4231fb62020-07-13 13:54:47 +02001652 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001653 case LYD_DIFF_OP_REPLACE:
1654 switch (elem->schema->nodetype) {
1655 case LYS_LEAF:
1656 /* leaf value change */
1657 LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
1658 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1659 break;
1660 case LYS_LEAFLIST:
1661 /* leaf-list move */
1662 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1663 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
1664 break;
1665 case LYS_LIST:
1666 /* list move */
1667 LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
1668 break;
1669 default:
1670 LOGINT(LYD_CTX(src_diff));
1671 ret = LY_EINT;
1672 goto cleanup;
1673 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001674 break;
Michal Vasko0f5c6a52020-11-06 17:18:03 +01001675 case LYD_DIFF_OP_NONE:
1676 switch (elem->schema->nodetype) {
1677 case LYS_LEAF:
1678 case LYS_LEAFLIST:
1679 /* default flag change */
1680 LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
1681 break;
1682 default:
1683 /* nothing to do */
1684 break;
1685 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001686 break;
1687 }
Michal Vasko4231fb62020-07-13 13:54:47 +02001688 }
1689
Michal Vasko56daf732020-08-10 10:57:18 +02001690 LYD_TREE_DFS_END(root, elem);
Michal Vasko4231fb62020-07-13 13:54:47 +02001691 }
1692 }
1693
1694cleanup:
1695 if (ret) {
1696 lyd_free_siblings(*diff);
1697 *diff = NULL;
1698 }
1699 return ret;
1700}