Line | Count | Source |
1 | | /** |
2 | | * @file diff.c |
3 | | * @author Michal Vasko <mvasko@cesnet.cz> |
4 | | * @brief diff functions |
5 | | * |
6 | | * Copyright (c) 2020 - 2021 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 _GNU_SOURCE /* asprintf, strdup */ |
15 | | #include <sys/cdefs.h> |
16 | | |
17 | | #include "diff.h" |
18 | | |
19 | | #include <assert.h> |
20 | | #include <stddef.h> |
21 | | #include <stdint.h> |
22 | | #include <stdio.h> |
23 | | #include <stdlib.h> |
24 | | #include <string.h> |
25 | | |
26 | | #include "common.h" |
27 | | #include "compat.h" |
28 | | #include "context.h" |
29 | | #include "log.h" |
30 | | #include "plugins_types.h" |
31 | | #include "set.h" |
32 | | #include "tree.h" |
33 | | #include "tree_data.h" |
34 | | #include "tree_data_internal.h" |
35 | | #include "tree_edit.h" |
36 | | #include "tree_schema.h" |
37 | | #include "tree_schema_internal.h" |
38 | | |
39 | | static const char * |
40 | | lyd_diff_op2str(enum lyd_diff_op op) |
41 | 0 | { |
42 | 0 | switch (op) { |
43 | 0 | case LYD_DIFF_OP_CREATE: |
44 | 0 | return "create"; |
45 | 0 | case LYD_DIFF_OP_DELETE: |
46 | 0 | return "delete"; |
47 | 0 | case LYD_DIFF_OP_REPLACE: |
48 | 0 | return "replace"; |
49 | 0 | case LYD_DIFF_OP_NONE: |
50 | 0 | return "none"; |
51 | 0 | } |
52 | | |
53 | 0 | LOGINT(NULL); |
54 | 0 | return NULL; |
55 | 0 | } |
56 | | |
57 | | static enum lyd_diff_op |
58 | | lyd_diff_str2op(const char *str) |
59 | 0 | { |
60 | 0 | switch (str[0]) { |
61 | 0 | case 'c': |
62 | 0 | assert(!strcmp(str, "create")); |
63 | 0 | return LYD_DIFF_OP_CREATE; |
64 | 0 | case 'd': |
65 | 0 | assert(!strcmp(str, "delete")); |
66 | 0 | return LYD_DIFF_OP_DELETE; |
67 | 0 | case 'r': |
68 | 0 | assert(!strcmp(str, "replace")); |
69 | 0 | return LYD_DIFF_OP_REPLACE; |
70 | 0 | case 'n': |
71 | 0 | assert(!strcmp(str, "none")); |
72 | 0 | return LYD_DIFF_OP_NONE; |
73 | 0 | } |
74 | | |
75 | 0 | LOGINT(NULL); |
76 | 0 | return 0; |
77 | 0 | } |
78 | | |
79 | | LY_ERR |
80 | | lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value, |
81 | | const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position, |
82 | | struct lyd_node **diff) |
83 | 0 | { |
84 | 0 | struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL; |
85 | 0 | const struct lyd_node *parent = NULL; |
86 | 0 | const struct lys_module *yang_mod; |
87 | |
|
88 | 0 | assert(diff); |
89 | | |
90 | | /* replace leaf always needs orig-default and orig-value */ |
91 | 0 | assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value)); |
92 | | |
93 | | /* create on userord needs key/value */ |
94 | 0 | assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) || |
95 | 0 | (lysc_is_dup_inst_list(node->schema) && position) || key); |
96 | 0 | assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) || |
97 | 0 | (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value); |
98 | | |
99 | | /* move on userord needs both key and orig-key/value and orig-value */ |
100 | 0 | assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) || |
101 | 0 | (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key)); |
102 | 0 | assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) || |
103 | 0 | (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) || |
104 | 0 | (value && orig_value)); |
105 | | |
106 | | /* find the first existing parent */ |
107 | 0 | siblings = *diff; |
108 | 0 | while (1) { |
109 | | /* find next node parent */ |
110 | 0 | parent = node; |
111 | 0 | while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) { |
112 | 0 | parent = lyd_parent(parent); |
113 | 0 | } |
114 | 0 | if (parent == node) { |
115 | | /* no more parents to find */ |
116 | 0 | break; |
117 | 0 | } |
118 | | |
119 | | /* check whether it exists in the diff */ |
120 | 0 | if (lyd_find_sibling_first(siblings, parent, &match)) { |
121 | 0 | break; |
122 | 0 | } |
123 | | |
124 | | /* another parent found */ |
125 | 0 | diff_parent = match; |
126 | | |
127 | | /* move down in the diff */ |
128 | 0 | siblings = lyd_child_no_keys(match); |
129 | 0 | } |
130 | | |
131 | | /* duplicate the subtree (and connect to the diff if possible) */ |
132 | 0 | LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent, |
133 | 0 | LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup)); |
134 | | |
135 | | /* find the first duplicated parent */ |
136 | 0 | if (!diff_parent) { |
137 | 0 | diff_parent = lyd_parent(dup); |
138 | 0 | while (diff_parent && diff_parent->parent) { |
139 | 0 | diff_parent = lyd_parent(diff_parent); |
140 | 0 | } |
141 | 0 | } else { |
142 | 0 | diff_parent = dup; |
143 | 0 | while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) { |
144 | 0 | diff_parent = lyd_parent(diff_parent); |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | /* no parent existed, must be manually connected */ |
149 | 0 | if (!diff_parent) { |
150 | | /* there actually was no parent to duplicate */ |
151 | 0 | lyd_insert_sibling(*diff, dup, diff); |
152 | 0 | } else if (!diff_parent->parent) { |
153 | 0 | lyd_insert_sibling(*diff, diff_parent, diff); |
154 | 0 | } |
155 | | |
156 | | /* get module with the operation metadata */ |
157 | 0 | yang_mod = LYD_CTX(node)->list.objs[1]; |
158 | 0 | assert(!strcmp(yang_mod->name, "yang")); |
159 | | |
160 | | /* add parent operation, if any */ |
161 | 0 | if (diff_parent && (diff_parent != dup)) { |
162 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), diff_parent, yang_mod, "operation", "none", 0, NULL)); |
163 | 0 | } |
164 | | |
165 | | /* add subtree operation */ |
166 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "operation", lyd_diff_op2str(op), 0, NULL)); |
167 | | |
168 | | /* orig-default */ |
169 | 0 | if (orig_default) { |
170 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-default", orig_default, 0, NULL)); |
171 | 0 | } |
172 | | |
173 | | /* orig-value */ |
174 | 0 | if (orig_value) { |
175 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-value", orig_value, 0, NULL)); |
176 | 0 | } |
177 | | |
178 | | /* key */ |
179 | 0 | if (key) { |
180 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "key", key, 0, NULL)); |
181 | 0 | } |
182 | | |
183 | | /* value */ |
184 | 0 | if (value) { |
185 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "value", value, 0, NULL)); |
186 | 0 | } |
187 | | |
188 | | /* position */ |
189 | 0 | if (position) { |
190 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "position", position, 0, NULL)); |
191 | 0 | } |
192 | | |
193 | | /* orig-key */ |
194 | 0 | if (orig_key) { |
195 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-key", orig_key, 0, NULL)); |
196 | 0 | } |
197 | | |
198 | | /* orig-position */ |
199 | 0 | if (orig_position) { |
200 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), dup, yang_mod, "orig-position", orig_position, 0, NULL)); |
201 | 0 | } |
202 | | |
203 | 0 | return LY_SUCCESS; |
204 | 0 | } |
205 | | |
206 | | /** |
207 | | * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet. |
208 | | * |
209 | | * @param[in] first Node from the first tree, can be NULL (on create). |
210 | | * @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 | | */ |
214 | | static struct lyd_diff_userord * |
215 | | lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord) |
216 | 0 | { |
217 | 0 | struct lyd_diff_userord *item; |
218 | 0 | struct lyd_node *iter; |
219 | 0 | const struct lyd_node **node; |
220 | 0 | LY_ARRAY_COUNT_TYPE u; |
221 | |
|
222 | 0 | LY_ARRAY_FOR(*userord, u) { |
223 | 0 | if ((*userord)[u].schema == schema) { |
224 | 0 | return &(*userord)[u]; |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | /* it was not added yet, add it now */ |
229 | 0 | LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL); |
230 | |
|
231 | 0 | item->schema = schema; |
232 | 0 | item->pos = 0; |
233 | 0 | item->inst = NULL; |
234 | | |
235 | | /* store all the instance pointers in the current order */ |
236 | 0 | if (first) { |
237 | 0 | LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) { |
238 | 0 | LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL); |
239 | 0 | *node = iter; |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | 0 | return item; |
244 | 0 | } |
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. |
260 | | * @param[out] position Position metadata. |
261 | | * @param[out] orig_position Original position metadata. |
262 | | * @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 | | */ |
266 | | static LY_ERR |
267 | | lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, |
268 | | struct lyd_diff_userord **userord, enum lyd_diff_op *op, const char **orig_default, char **value, |
269 | | char **orig_value, char **key, char **orig_key, char **position, char **orig_position) |
270 | 0 | { |
271 | 0 | const struct lysc_node *schema; |
272 | 0 | size_t buflen, bufused; |
273 | 0 | uint32_t first_pos, second_pos; |
274 | 0 | struct lyd_diff_userord *userord_item; |
275 | |
|
276 | 0 | assert(first || second); |
277 | |
|
278 | 0 | *orig_default = NULL; |
279 | 0 | *value = NULL; |
280 | 0 | *orig_value = NULL; |
281 | 0 | *key = NULL; |
282 | 0 | *orig_key = NULL; |
283 | 0 | *position = NULL; |
284 | 0 | *orig_position = NULL; |
285 | |
|
286 | 0 | schema = first ? first->schema : second->schema; |
287 | 0 | assert(lysc_is_userordered(schema)); |
288 | | |
289 | | /* get userord entry */ |
290 | 0 | userord_item = lyd_diff_userord_get(first, schema, userord); |
291 | 0 | LY_CHECK_RET(!userord_item, LY_EMEM); |
292 | | |
293 | | /* find user-ordered first position */ |
294 | 0 | if (first) { |
295 | 0 | for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) { |
296 | 0 | if (userord_item->inst[first_pos] == first) { |
297 | 0 | break; |
298 | 0 | } |
299 | 0 | } |
300 | 0 | assert(first_pos < LY_ARRAY_COUNT(userord_item->inst)); |
301 | 0 | } else { |
302 | 0 | first_pos = 0; |
303 | 0 | } |
304 | | |
305 | | /* prepare position of the next instance */ |
306 | 0 | second_pos = userord_item->pos++; |
307 | | |
308 | | /* learn operation first */ |
309 | 0 | if (!second) { |
310 | 0 | *op = LYD_DIFF_OP_DELETE; |
311 | 0 | } else if (!first) { |
312 | 0 | *op = LYD_DIFF_OP_CREATE; |
313 | 0 | } else { |
314 | 0 | if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) { |
315 | | /* in first, there is a different instance on the second position, we are going to move 'first' node */ |
316 | 0 | *op = LYD_DIFF_OP_REPLACE; |
317 | 0 | } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { |
318 | | /* default flag change */ |
319 | 0 | *op = LYD_DIFF_OP_NONE; |
320 | 0 | } else { |
321 | | /* no changes */ |
322 | 0 | return LY_ENOT; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | /* |
327 | | * set each attribute correctly based on the operation and node type |
328 | | */ |
329 | | |
330 | | /* orig-default */ |
331 | 0 | if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) { |
332 | 0 | if (first->flags & LYD_DEFAULT) { |
333 | 0 | *orig_default = "true"; |
334 | 0 | } else { |
335 | 0 | *orig_default = "false"; |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | | /* value */ |
340 | 0 | if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) && |
341 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) { |
342 | 0 | if (second_pos) { |
343 | 0 | *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1])); |
344 | 0 | LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM); |
345 | 0 | } else { |
346 | 0 | *value = strdup(""); |
347 | 0 | LY_CHECK_ERR_RET(!*value, LOGMEM(schema->module->ctx), LY_EMEM); |
348 | 0 | } |
349 | 0 | } |
350 | | |
351 | | /* orig-value */ |
352 | 0 | if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) && |
353 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) { |
354 | 0 | if (first_pos) { |
355 | 0 | *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1])); |
356 | 0 | LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM); |
357 | 0 | } else { |
358 | 0 | *orig_value = strdup(""); |
359 | 0 | LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM); |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | | /* key */ |
364 | 0 | if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) && |
365 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) { |
366 | 0 | if (second_pos) { |
367 | 0 | buflen = bufused = 0; |
368 | 0 | LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0)); |
369 | 0 | } else { |
370 | 0 | *key = strdup(""); |
371 | 0 | LY_CHECK_ERR_RET(!*key, LOGMEM(schema->module->ctx), LY_EMEM); |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | | /* orig-key */ |
376 | 0 | if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) && |
377 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) { |
378 | 0 | if (first_pos) { |
379 | 0 | buflen = bufused = 0; |
380 | 0 | LY_CHECK_RET(lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0)); |
381 | 0 | } else { |
382 | 0 | *orig_key = strdup(""); |
383 | 0 | LY_CHECK_ERR_RET(!*orig_key, LOGMEM(schema->module->ctx), LY_EMEM); |
384 | 0 | } |
385 | 0 | } |
386 | | |
387 | | /* position */ |
388 | 0 | if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) { |
389 | 0 | if (second_pos) { |
390 | 0 | if (asprintf(position, "%" PRIu32, second_pos) == -1) { |
391 | 0 | LOGMEM(schema->module->ctx); |
392 | 0 | return LY_EMEM; |
393 | 0 | } |
394 | 0 | } else { |
395 | 0 | *position = strdup(""); |
396 | 0 | LY_CHECK_ERR_RET(!*position, LOGMEM(schema->module->ctx), LY_EMEM); |
397 | 0 | } |
398 | 0 | } |
399 | | |
400 | | /* orig-position */ |
401 | 0 | if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) { |
402 | 0 | if (first_pos) { |
403 | 0 | if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) { |
404 | 0 | LOGMEM(schema->module->ctx); |
405 | 0 | return LY_EMEM; |
406 | 0 | } |
407 | 0 | } else { |
408 | 0 | *orig_position = strdup(""); |
409 | 0 | LY_CHECK_ERR_RET(!*orig_position, LOGMEM(schema->module->ctx), LY_EMEM); |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | | /* |
414 | | * update our instances - apply the change |
415 | | */ |
416 | 0 | if (*op == LYD_DIFF_OP_CREATE) { |
417 | | /* insert the instance */ |
418 | 0 | LY_ARRAY_CREATE_RET(schema->module->ctx, userord_item->inst, 1, LY_EMEM); |
419 | 0 | if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) { |
420 | 0 | memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos, |
421 | 0 | (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst); |
422 | 0 | } |
423 | 0 | LY_ARRAY_INCREMENT(userord_item->inst); |
424 | 0 | userord_item->inst[second_pos] = second; |
425 | |
|
426 | 0 | } else if (*op == LYD_DIFF_OP_DELETE) { |
427 | | /* remove the instance */ |
428 | 0 | if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) { |
429 | 0 | memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1, |
430 | 0 | (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst); |
431 | 0 | } |
432 | 0 | LY_ARRAY_DECREMENT(userord_item->inst); |
433 | |
|
434 | 0 | } else if (*op == LYD_DIFF_OP_REPLACE) { |
435 | | /* move the instances */ |
436 | 0 | memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos, |
437 | 0 | (first_pos - second_pos) * sizeof *userord_item->inst); |
438 | 0 | userord_item->inst[second_pos] = first; |
439 | 0 | } |
440 | | |
441 | 0 | return LY_SUCCESS; |
442 | 0 | } |
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 | | */ |
458 | | static LY_ERR |
459 | | lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, enum lyd_diff_op *op, |
460 | | const char **orig_default, char **orig_value) |
461 | 0 | { |
462 | 0 | const struct lysc_node *schema; |
463 | |
|
464 | 0 | assert(first || second); |
465 | |
|
466 | 0 | *orig_default = NULL; |
467 | 0 | *orig_value = NULL; |
468 | |
|
469 | 0 | schema = first ? first->schema : second->schema; |
470 | 0 | assert(!lysc_is_userordered(schema)); |
471 | | |
472 | | /* learn operation first */ |
473 | 0 | if (!second) { |
474 | 0 | *op = LYD_DIFF_OP_DELETE; |
475 | 0 | } else if (!first) { |
476 | 0 | *op = LYD_DIFF_OP_CREATE; |
477 | 0 | } else { |
478 | 0 | switch (schema->nodetype) { |
479 | 0 | case LYS_CONTAINER: |
480 | 0 | case LYS_RPC: |
481 | 0 | case LYS_ACTION: |
482 | 0 | case LYS_NOTIF: |
483 | | /* no changes */ |
484 | 0 | return LY_ENOT; |
485 | 0 | case LYS_LIST: |
486 | 0 | case LYS_LEAFLIST: |
487 | 0 | if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { |
488 | | /* default flag change */ |
489 | 0 | *op = LYD_DIFF_OP_NONE; |
490 | 0 | } else { |
491 | | /* no changes */ |
492 | 0 | return LY_ENOT; |
493 | 0 | } |
494 | 0 | break; |
495 | 0 | case LYS_LEAF: |
496 | 0 | case LYS_ANYXML: |
497 | 0 | case LYS_ANYDATA: |
498 | 0 | if (lyd_compare_single(first, second, 0)) { |
499 | | /* different values */ |
500 | 0 | *op = LYD_DIFF_OP_REPLACE; |
501 | 0 | } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { |
502 | | /* default flag change */ |
503 | 0 | *op = LYD_DIFF_OP_NONE; |
504 | 0 | } else { |
505 | | /* no changes */ |
506 | 0 | return LY_ENOT; |
507 | 0 | } |
508 | 0 | break; |
509 | 0 | default: |
510 | 0 | LOGINT_RET(schema->module->ctx); |
511 | 0 | } |
512 | 0 | } |
513 | | |
514 | | /* |
515 | | * set each attribute correctly based on the operation and node type |
516 | | */ |
517 | | |
518 | | /* orig-default */ |
519 | 0 | if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) { |
520 | 0 | if (first->flags & LYD_DEFAULT) { |
521 | 0 | *orig_default = "true"; |
522 | 0 | } else { |
523 | 0 | *orig_default = "false"; |
524 | 0 | } |
525 | 0 | } |
526 | | |
527 | | /* orig-value */ |
528 | 0 | if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) { |
529 | 0 | if (schema->nodetype == LYS_LEAF) { |
530 | 0 | *orig_value = strdup(lyd_get_value(first)); |
531 | 0 | LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM); |
532 | 0 | } else { |
533 | 0 | LY_CHECK_RET(lyd_any_value_str(first, orig_value)); |
534 | 0 | } |
535 | 0 | } |
536 | | |
537 | 0 | return LY_SUCCESS; |
538 | 0 | } |
539 | | |
540 | | /** |
541 | | * @brief Find an entry in duplicate instance cache for an instance. Create it if it does not exist. |
542 | | * |
543 | | * @param[in] first_inst Instance of the cache entry. |
544 | | * @param[in,out] dup_inst_cache Duplicate instance cache. |
545 | | * @return Instance cache entry. |
546 | | */ |
547 | | static struct lyd_diff_dup_inst * |
548 | | lyd_diff_dup_inst_get(const struct lyd_node *first_inst, struct lyd_diff_dup_inst **dup_inst_cache) |
549 | 0 | { |
550 | 0 | struct lyd_diff_dup_inst *item; |
551 | 0 | LY_ARRAY_COUNT_TYPE u; |
552 | |
|
553 | 0 | LY_ARRAY_FOR(*dup_inst_cache, u) { |
554 | 0 | if ((*dup_inst_cache)[u].inst_set->dnodes[0] == first_inst) { |
555 | 0 | return &(*dup_inst_cache)[u]; |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | | /* it was not added yet, add it now */ |
560 | 0 | LY_ARRAY_NEW_RET(LYD_CTX(first_inst), *dup_inst_cache, item, NULL); |
561 | |
|
562 | 0 | return item; |
563 | 0 | } |
564 | | |
565 | | /** |
566 | | * @brief Free duplicate instance cache. |
567 | | * |
568 | | * @param[in] dup_inst Duplicate instance cache to free. |
569 | | */ |
570 | | static void |
571 | | lyd_diff_dup_inst_free(struct lyd_diff_dup_inst *dup_inst) |
572 | 0 | { |
573 | 0 | LY_ARRAY_COUNT_TYPE u; |
574 | |
|
575 | 0 | LY_ARRAY_FOR(dup_inst, u) { |
576 | 0 | ly_set_free(dup_inst[u].inst_set, NULL); |
577 | 0 | } |
578 | 0 | LY_ARRAY_FREE(dup_inst); |
579 | 0 | } |
580 | | |
581 | | /** |
582 | | * @brief Find a matching instance of a node in a data tree. |
583 | | * |
584 | | * @param[in] siblings Siblings to search in. |
585 | | * @param[in] target Target node to search for. |
586 | | * @param[in] defaults Whether to consider (or ignore) default values. |
587 | | * @param[in,out] dup_inst_cache Duplicate instance cache. |
588 | | * @param[out] match Found match, NULL if no matching node found. |
589 | | * @return LY_ERR value. |
590 | | */ |
591 | | static LY_ERR |
592 | | lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults, |
593 | | struct lyd_diff_dup_inst **dup_inst_cache, struct lyd_node **match) |
594 | 0 | { |
595 | 0 | struct lyd_diff_dup_inst *dup_inst; |
596 | |
|
597 | 0 | if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) { |
598 | | /* try to find the exact instance */ |
599 | 0 | lyd_find_sibling_first(siblings, target, match); |
600 | 0 | } else { |
601 | | /* try to simply find the node, there cannot be more instances */ |
602 | 0 | lyd_find_sibling_val(siblings, target->schema, NULL, 0, match); |
603 | 0 | } |
604 | |
|
605 | 0 | if (*match && lysc_is_dup_inst_list(target->schema)) { |
606 | | /* there can be more exact same instances and we must make sure we do not match a single node more times */ |
607 | 0 | dup_inst = lyd_diff_dup_inst_get(*match, dup_inst_cache); |
608 | 0 | LY_CHECK_ERR_RET(!dup_inst, LOGMEM(LYD_CTX(target)), LY_EMEM); |
609 | |
|
610 | 0 | if (!dup_inst->used) { |
611 | | /* we did not cache these instances yet, do so */ |
612 | 0 | lyd_find_sibling_dup_inst_set(siblings, target, &dup_inst->inst_set); |
613 | 0 | assert(dup_inst->inst_set->count && (dup_inst->inst_set->dnodes[0] == *match)); |
614 | 0 | } |
615 | |
|
616 | 0 | if (dup_inst->used == dup_inst->inst_set->count) { |
617 | | /* we have used all the instances */ |
618 | 0 | *match = NULL; |
619 | 0 | } else { |
620 | 0 | assert(dup_inst->used < dup_inst->inst_set->count); |
621 | | |
622 | | /* use another instance */ |
623 | 0 | *match = dup_inst->inst_set->dnodes[dup_inst->used]; |
624 | 0 | ++dup_inst->used; |
625 | 0 | } |
626 | 0 | } |
627 | | |
628 | 0 | if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) { |
629 | | /* ignore default nodes */ |
630 | 0 | *match = NULL; |
631 | 0 | } |
632 | 0 | return LY_SUCCESS; |
633 | 0 | } |
634 | | |
635 | | /** |
636 | | * @brief Perform diff for all siblings at certain depth, recursively. |
637 | | * |
638 | | * For user-ordered lists/leaf-lists a specific structure is used for storing |
639 | | * the current order. The idea is to apply all the generated diff changes |
640 | | * virtually on the first tree so that we can continue to generate correct |
641 | | * changes after some were already generated. |
642 | | * |
643 | | * The algorithm then uses second tree position-based changes with a before |
644 | | * (preceding) item anchor. |
645 | | * |
646 | | * Example: |
647 | | * |
648 | | * Virtual first tree leaf-list order: |
649 | | * 1 2 [3] 4 5 |
650 | | * |
651 | | * Second tree leaf-list order: |
652 | | * 1 2 [5] 3 4 |
653 | | * |
654 | | * We are at the 3rd node now. We look at whether the nodes on the 3rd position |
655 | | * match - they do not - move nodes so that the 3rd position node is final -> |
656 | | * -> move node 5 to the 3rd position -> move node 5 after node 2. |
657 | | * |
658 | | * Required properties: |
659 | | * Stored operations (move) should not be affected by later operations - |
660 | | * - would cause a redundantly long list of operations, possibly inifinite. |
661 | | * |
662 | | * Implemenation justification: |
663 | | * First, all delete operations and only then move/create operations are stored. |
664 | | * Also, preceding anchor is used and after each iteration another node is |
665 | | * at its final position. That results in the invariant that all preceding |
666 | | * nodes are final and will not be changed by the later operations, meaning |
667 | | * they can safely be used as anchors for the later operations. |
668 | | * |
669 | | * @param[in] first First tree first sibling. |
670 | | * @param[in] second Second tree first sibling. |
671 | | * @param[in] options Diff options. |
672 | | * @param[in] nosiblings Whether to skip following siblings. |
673 | | * @param[in,out] diff Diff to append to. |
674 | | * @return LY_ERR value. |
675 | | */ |
676 | | static LY_ERR |
677 | | lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, |
678 | | struct lyd_node **diff) |
679 | 0 | { |
680 | 0 | LY_ERR ret = LY_SUCCESS; |
681 | 0 | const struct lyd_node *iter_first, *iter_second; |
682 | 0 | struct lyd_node *match_second, *match_first; |
683 | 0 | struct lyd_diff_userord *userord = NULL; |
684 | 0 | struct lyd_diff_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL; |
685 | 0 | LY_ARRAY_COUNT_TYPE u; |
686 | 0 | enum lyd_diff_op op; |
687 | 0 | const char *orig_default; |
688 | 0 | char *orig_value, *key, *value, *position, *orig_key, *orig_position; |
689 | | |
690 | | /* compare first tree to the second tree - delete, replace, none */ |
691 | 0 | LY_LIST_FOR(first, iter_first) { |
692 | 0 | assert(!(iter_first->schema->flags & LYS_KEY)); |
693 | 0 | if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) { |
694 | | /* skip default nodes */ |
695 | 0 | continue; |
696 | 0 | } |
697 | | |
698 | | /* find a match in the second tree */ |
699 | 0 | LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second, |
700 | 0 | &match_second), cleanup); |
701 | |
|
702 | 0 | if (lysc_is_userordered(iter_first->schema)) { |
703 | | /* we are handling only user-ordered node delete now */ |
704 | 0 | if (!match_second) { |
705 | | /* get all the attributes */ |
706 | 0 | LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, &userord, &op, &orig_default, |
707 | 0 | &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup); |
708 | | |
709 | | /* there must be changes, it is deleted */ |
710 | 0 | assert(op == LYD_DIFF_OP_DELETE); |
711 | 0 | ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key, orig_position, diff); |
712 | |
|
713 | 0 | free(orig_value); |
714 | 0 | free(key); |
715 | 0 | free(value); |
716 | 0 | free(position); |
717 | 0 | free(orig_key); |
718 | 0 | free(orig_position); |
719 | 0 | LY_CHECK_GOTO(ret, cleanup); |
720 | 0 | } |
721 | 0 | } else { |
722 | | /* get all the attributes */ |
723 | 0 | ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value); |
724 | | |
725 | | /* add into diff if there are any changes */ |
726 | 0 | if (!ret) { |
727 | 0 | if (op == LYD_DIFF_OP_DELETE) { |
728 | 0 | ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff); |
729 | 0 | } else { |
730 | 0 | assert(match_second); |
731 | 0 | ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff); |
732 | 0 | } |
733 | |
|
734 | 0 | free(orig_value); |
735 | 0 | LY_CHECK_GOTO(ret, cleanup); |
736 | 0 | } else if (ret == LY_ENOT) { |
737 | 0 | ret = LY_SUCCESS; |
738 | 0 | } else { |
739 | 0 | goto cleanup; |
740 | 0 | } |
741 | 0 | } |
742 | | |
743 | | /* check descendants, if any, recursively */ |
744 | 0 | if (match_second) { |
745 | 0 | LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second), |
746 | 0 | options, 0, diff), cleanup); |
747 | 0 | } |
748 | | |
749 | 0 | if (nosiblings) { |
750 | 0 | break; |
751 | 0 | } |
752 | 0 | } |
753 | | |
754 | | /* reset all cached positions */ |
755 | 0 | LY_ARRAY_FOR(userord, u) { |
756 | 0 | userord[u].pos = 0; |
757 | 0 | } |
758 | | |
759 | | /* compare second tree to the first tree - create, user-ordered move */ |
760 | 0 | LY_LIST_FOR(second, iter_second) { |
761 | 0 | assert(!(iter_second->schema->flags & LYS_KEY)); |
762 | 0 | if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) { |
763 | | /* skip default nodes */ |
764 | 0 | continue; |
765 | 0 | } |
766 | | |
767 | | /* find a match in the first tree */ |
768 | 0 | LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first, |
769 | 0 | &match_first), cleanup); |
770 | |
|
771 | 0 | if (lysc_is_userordered(iter_second->schema)) { |
772 | | /* get all the attributes */ |
773 | 0 | ret = lyd_diff_userord_attrs(match_first, iter_second, options, &userord, &op, &orig_default, |
774 | 0 | &value, &orig_value, &key, &orig_key, &position, &orig_position); |
775 | | |
776 | | /* add into diff if there are any changes */ |
777 | 0 | if (!ret) { |
778 | 0 | ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key, |
779 | 0 | orig_position, diff); |
780 | |
|
781 | 0 | free(orig_value); |
782 | 0 | free(key); |
783 | 0 | free(value); |
784 | 0 | free(position); |
785 | 0 | free(orig_key); |
786 | 0 | free(orig_position); |
787 | 0 | LY_CHECK_GOTO(ret, cleanup); |
788 | 0 | } else if (ret == LY_ENOT) { |
789 | 0 | ret = LY_SUCCESS; |
790 | 0 | } else { |
791 | 0 | goto cleanup; |
792 | 0 | } |
793 | 0 | } else if (!match_first) { |
794 | | /* get all the attributes */ |
795 | 0 | LY_CHECK_GOTO(ret = lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup); |
796 | | |
797 | | /* there must be changes, it is created */ |
798 | 0 | assert(op == LYD_DIFF_OP_CREATE); |
799 | 0 | ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff); |
800 | |
|
801 | 0 | free(orig_value); |
802 | 0 | LY_CHECK_GOTO(ret, cleanup); |
803 | 0 | } /* else was handled */ |
804 | | |
805 | 0 | if (nosiblings) { |
806 | 0 | break; |
807 | 0 | } |
808 | 0 | } |
809 | | |
810 | 0 | cleanup: |
811 | 0 | lyd_diff_dup_inst_free(dup_inst_first); |
812 | 0 | lyd_diff_dup_inst_free(dup_inst_second); |
813 | 0 | LY_ARRAY_FOR(userord, u) { |
814 | 0 | LY_ARRAY_FREE(userord[u].inst); |
815 | 0 | } |
816 | 0 | LY_ARRAY_FREE(userord); |
817 | 0 | return ret; |
818 | 0 | } |
819 | | |
820 | | static LY_ERR |
821 | | lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, struct lyd_node **diff) |
822 | 0 | { |
823 | 0 | const struct ly_ctx *ctx; |
824 | |
|
825 | 0 | LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL); |
826 | |
|
827 | 0 | if (first) { |
828 | 0 | ctx = LYD_CTX(first); |
829 | 0 | } else if (second) { |
830 | 0 | ctx = LYD_CTX(second); |
831 | 0 | } else { |
832 | 0 | ctx = NULL; |
833 | 0 | } |
834 | |
|
835 | 0 | if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) { |
836 | 0 | LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__); |
837 | 0 | return LY_EINVAL; |
838 | 0 | } |
839 | | |
840 | 0 | *diff = NULL; |
841 | |
|
842 | 0 | return lyd_diff_siblings_r(first, second, options, nosiblings, diff); |
843 | 0 | } |
844 | | |
845 | | API LY_ERR |
846 | | lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff) |
847 | 0 | { |
848 | 0 | return lyd_diff(first, second, options, 1, diff); |
849 | 0 | } |
850 | | |
851 | | API LY_ERR |
852 | | lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff) |
853 | 0 | { |
854 | 0 | return lyd_diff(first, second, options, 0, diff); |
855 | 0 | } |
856 | | |
857 | | /** |
858 | | * @brief Learn operation of a diff node. |
859 | | * |
860 | | * @param[in] diff_node Diff node. |
861 | | * @param[out] op Operation. |
862 | | * @return LY_ERR value. |
863 | | */ |
864 | | static LY_ERR |
865 | | lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op) |
866 | 0 | { |
867 | 0 | struct lyd_meta *meta = NULL; |
868 | 0 | const struct lyd_node *diff_parent; |
869 | 0 | const char *str; |
870 | |
|
871 | 0 | for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) { |
872 | 0 | LY_LIST_FOR(diff_parent->meta, meta) { |
873 | 0 | if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) { |
874 | 0 | str = lyd_get_meta_value(meta); |
875 | 0 | if ((str[0] == 'r') && (diff_parent != diff_node)) { |
876 | | /* we do not care about this operation if it's in our parent */ |
877 | 0 | continue; |
878 | 0 | } |
879 | 0 | *op = lyd_diff_str2op(str); |
880 | 0 | break; |
881 | 0 | } |
882 | 0 | } |
883 | 0 | if (meta) { |
884 | 0 | break; |
885 | 0 | } |
886 | 0 | } |
887 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_node)), LY_EINT); |
888 | |
|
889 | 0 | return LY_SUCCESS; |
890 | 0 | } |
891 | | |
892 | | /** |
893 | | * @brief Insert a diff node into a data tree. |
894 | | * |
895 | | * @param[in,out] first_node First sibling of the data tree. |
896 | | * @param[in] parent_node Data tree sibling parent node. |
897 | | * @param[in] new_node Node to insert. |
898 | | * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set, |
899 | | * the user-ordered instance will be inserted at the first position. |
900 | | * @return err_info, NULL on success. |
901 | | */ |
902 | | static LY_ERR |
903 | | lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node, |
904 | | const char *userord_anchor) |
905 | 0 | { |
906 | 0 | LY_ERR ret; |
907 | 0 | struct lyd_node *anchor; |
908 | 0 | uint32_t pos, anchor_pos; |
909 | 0 | int found; |
910 | |
|
911 | 0 | assert(new_node); |
912 | |
|
913 | 0 | if (!*first_node) { |
914 | 0 | if (!parent_node) { |
915 | | /* no parent or siblings */ |
916 | 0 | *first_node = new_node; |
917 | 0 | return LY_SUCCESS; |
918 | 0 | } |
919 | | |
920 | | /* simply insert into parent, no other children */ |
921 | 0 | if (userord_anchor) { |
922 | 0 | LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.", |
923 | 0 | new_node->schema->name); |
924 | 0 | return LY_EINVAL; |
925 | 0 | } |
926 | 0 | return lyd_insert_child(parent_node, new_node); |
927 | 0 | } |
928 | | |
929 | 0 | assert(!(*first_node)->parent || (lyd_parent(*first_node) == parent_node)); |
930 | |
|
931 | 0 | if (!lysc_is_userordered(new_node->schema)) { |
932 | | /* simple insert */ |
933 | 0 | return lyd_insert_sibling(*first_node, new_node, first_node); |
934 | 0 | } |
935 | | |
936 | 0 | if (userord_anchor) { |
937 | | /* find the anchor sibling */ |
938 | 0 | if (lysc_is_dup_inst_list(new_node->schema)) { |
939 | 0 | anchor_pos = atoi(userord_anchor); |
940 | 0 | LY_CHECK_ERR_RET(!anchor_pos, LOGINT(LYD_CTX(new_node)), LY_EINT); |
941 | |
|
942 | 0 | found = 0; |
943 | 0 | pos = 1; |
944 | 0 | LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) { |
945 | 0 | if (pos == anchor_pos) { |
946 | 0 | found = 1; |
947 | 0 | break; |
948 | 0 | } |
949 | 0 | ++pos; |
950 | 0 | } |
951 | 0 | if (!found) { |
952 | 0 | LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.", |
953 | 0 | new_node->schema->name); |
954 | 0 | return LY_EINVAL; |
955 | 0 | } |
956 | 0 | } else { |
957 | 0 | ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor); |
958 | 0 | if (ret == LY_ENOTFOUND) { |
959 | 0 | LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.", |
960 | 0 | new_node->schema->name); |
961 | 0 | return LY_EINVAL; |
962 | 0 | } else if (ret) { |
963 | 0 | return ret; |
964 | 0 | } |
965 | 0 | } |
966 | | |
967 | | /* insert after */ |
968 | 0 | LY_CHECK_RET(lyd_insert_after(anchor, new_node)); |
969 | 0 | assert(new_node->prev == anchor); |
970 | 0 | if (*first_node == new_node) { |
971 | 0 | *first_node = anchor; |
972 | 0 | } |
973 | 0 | } else { |
974 | 0 | if ((*first_node)->schema->flags & LYS_KEY) { |
975 | 0 | assert(parent_node && (parent_node->schema->nodetype == LYS_LIST)); |
976 | | |
977 | | /* find last key */ |
978 | 0 | anchor = *first_node; |
979 | 0 | while (anchor->next && (anchor->next->schema->flags & LYS_KEY)) { |
980 | 0 | anchor = anchor->next; |
981 | 0 | } |
982 | | /* insert after the last key */ |
983 | 0 | LY_CHECK_RET(lyd_insert_after(anchor, new_node)); |
984 | 0 | } else { |
985 | | /* insert at the beginning */ |
986 | 0 | LY_CHECK_RET(lyd_insert_before(*first_node, new_node)); |
987 | 0 | *first_node = new_node; |
988 | 0 | } |
989 | 0 | } |
990 | | |
991 | 0 | return LY_SUCCESS; |
992 | 0 | } |
993 | | |
994 | | /** |
995 | | * @brief Apply diff subtree on data tree nodes, recursively. |
996 | | * |
997 | | * @param[in,out] first_node First sibling of the data tree. |
998 | | * @param[in] parent_node Parent of the first sibling. |
999 | | * @param[in] diff_node Current diff node. |
1000 | | * @param[in] diff_cb Optional diff callback. |
1001 | | * @param[in] cb_data User data for @p diff_cb. |
1002 | | * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings. |
1003 | | * @return LY_ERR value. |
1004 | | */ |
1005 | | static LY_ERR |
1006 | | lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node, |
1007 | | lyd_diff_cb diff_cb, void *cb_data, struct lyd_diff_dup_inst **dup_inst) |
1008 | 0 | { |
1009 | 0 | LY_ERR ret; |
1010 | 0 | struct lyd_node *match, *diff_child; |
1011 | 0 | const char *str_val, *meta_str; |
1012 | 0 | enum lyd_diff_op op; |
1013 | 0 | struct lyd_meta *meta; |
1014 | 0 | struct lyd_diff_dup_inst *child_dup_inst = NULL; |
1015 | 0 | const struct ly_ctx *ctx = LYD_CTX(diff_node); |
1016 | | |
1017 | | /* read all the valid attributes */ |
1018 | 0 | LY_CHECK_RET(lyd_diff_get_op(diff_node, &op)); |
1019 | | |
1020 | | /* handle specific user-ordered (leaf-)lists operations separately */ |
1021 | 0 | if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) { |
1022 | 0 | if (op == LYD_DIFF_OP_REPLACE) { |
1023 | | /* find the node (we must have some siblings because the node was only moved) */ |
1024 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1025 | 0 | LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT); |
1026 | 0 | } else { |
1027 | | /* duplicate the node */ |
1028 | 0 | LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match)); |
1029 | 0 | } |
1030 | | |
1031 | | /* get "key", "value", or "position" metadata string value */ |
1032 | 0 | if (lysc_is_dup_inst_list(diff_node->schema)) { |
1033 | 0 | meta_str = "yang:position"; |
1034 | 0 | } else if (diff_node->schema->nodetype == LYS_LIST) { |
1035 | 0 | meta_str = "yang:key"; |
1036 | 0 | } else { |
1037 | 0 | meta_str = "yang:value"; |
1038 | 0 | } |
1039 | 0 | meta = lyd_find_meta(diff_node->meta, NULL, meta_str); |
1040 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(ctx), LY_EINT); |
1041 | 0 | str_val = lyd_get_meta_value(meta); |
1042 | | |
1043 | | /* insert/move the node */ |
1044 | 0 | if (str_val[0]) { |
1045 | 0 | ret = lyd_diff_insert(first_node, parent_node, match, str_val); |
1046 | 0 | } else { |
1047 | 0 | ret = lyd_diff_insert(first_node, parent_node, match, NULL); |
1048 | 0 | } |
1049 | 0 | if (ret) { |
1050 | 0 | if (op == LYD_DIFF_OP_CREATE) { |
1051 | 0 | lyd_free_tree(match); |
1052 | 0 | } |
1053 | 0 | return ret; |
1054 | 0 | } |
1055 | | |
1056 | 0 | goto next_iter_r; |
1057 | 0 | } |
1058 | | |
1059 | | /* apply operation */ |
1060 | 0 | switch (op) { |
1061 | 0 | case LYD_DIFF_OP_NONE: |
1062 | | /* find the node */ |
1063 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1064 | 0 | LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT); |
1065 | |
|
1066 | 0 | if (match->schema->nodetype & LYD_NODE_TERM) { |
1067 | | /* special case of only dflt flag change */ |
1068 | 0 | if (diff_node->flags & LYD_DEFAULT) { |
1069 | 0 | match->flags |= LYD_DEFAULT; |
1070 | 0 | } else { |
1071 | 0 | match->flags &= ~LYD_DEFAULT; |
1072 | 0 | } |
1073 | 0 | } else { |
1074 | | /* none operation on nodes without children is redundant and hence forbidden */ |
1075 | 0 | if (!lyd_child_no_keys(diff_node)) { |
1076 | 0 | LOGINT_RET(ctx); |
1077 | 0 | } |
1078 | 0 | } |
1079 | 0 | break; |
1080 | 0 | case LYD_DIFF_OP_CREATE: |
1081 | | /* duplicate the node */ |
1082 | 0 | LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match)); |
1083 | | |
1084 | | /* insert it at the end */ |
1085 | 0 | ret = 0; |
1086 | 0 | if (parent_node) { |
1087 | 0 | ret = lyd_insert_child(parent_node, match); |
1088 | 0 | } else { |
1089 | 0 | ret = lyd_insert_sibling(*first_node, match, first_node); |
1090 | 0 | } |
1091 | 0 | if (ret) { |
1092 | 0 | lyd_free_tree(match); |
1093 | 0 | return ret; |
1094 | 0 | } |
1095 | | |
1096 | 0 | break; |
1097 | 0 | case LYD_DIFF_OP_DELETE: |
1098 | | /* find the node */ |
1099 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1100 | 0 | LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT); |
1101 | | |
1102 | | /* remove it */ |
1103 | 0 | if ((match == *first_node) && !match->parent) { |
1104 | 0 | assert(!parent_node); |
1105 | | /* we have removed the top-level node */ |
1106 | 0 | *first_node = (*first_node)->next; |
1107 | 0 | } |
1108 | 0 | lyd_free_tree(match); |
1109 | | |
1110 | | /* we are not going recursively in this case, the whole subtree was already deleted */ |
1111 | 0 | return LY_SUCCESS; |
1112 | 0 | case LYD_DIFF_OP_REPLACE: |
1113 | 0 | LY_CHECK_ERR_RET(!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA)), LOGINT(ctx), LY_EINT); |
1114 | | |
1115 | | /* find the node */ |
1116 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1117 | 0 | LY_CHECK_ERR_RET(!match, LOGINT(ctx), LY_EINT); |
1118 | | |
1119 | | /* update the value */ |
1120 | 0 | if (diff_node->schema->nodetype == LYS_LEAF) { |
1121 | 0 | ret = lyd_change_term(match, lyd_get_value(diff_node)); |
1122 | 0 | if (ret && (ret != LY_EEXIST)) { |
1123 | 0 | LOGINT_RET(ctx); |
1124 | 0 | } |
1125 | 0 | } else { |
1126 | 0 | struct lyd_node_any *any = (struct lyd_node_any *)diff_node; |
1127 | 0 | LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type)); |
1128 | 0 | } |
1129 | | |
1130 | | /* with flags */ |
1131 | 0 | match->flags = diff_node->flags; |
1132 | 0 | break; |
1133 | 0 | default: |
1134 | 0 | LOGINT_RET(ctx); |
1135 | 0 | } |
1136 | | |
1137 | 0 | next_iter_r: |
1138 | 0 | if (diff_cb) { |
1139 | | /* call callback */ |
1140 | 0 | LY_CHECK_RET(diff_cb(diff_node, match, cb_data)); |
1141 | 0 | } |
1142 | | |
1143 | | /* apply diff recursively */ |
1144 | 0 | ret = LY_SUCCESS; |
1145 | 0 | LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) { |
1146 | 0 | ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst); |
1147 | 0 | if (ret) { |
1148 | 0 | break; |
1149 | 0 | } |
1150 | 0 | } |
1151 | |
|
1152 | 0 | lyd_diff_dup_inst_free(child_dup_inst); |
1153 | 0 | return ret; |
1154 | 0 | } |
1155 | | |
1156 | | API LY_ERR |
1157 | | lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod, |
1158 | | lyd_diff_cb diff_cb, void *cb_data) |
1159 | 0 | { |
1160 | 0 | const struct lyd_node *root; |
1161 | 0 | struct lyd_diff_dup_inst *dup_inst = NULL; |
1162 | 0 | LY_ERR ret = LY_SUCCESS; |
1163 | |
|
1164 | 0 | LY_LIST_FOR(diff, root) { |
1165 | 0 | if (mod && (lyd_owner_module(root) != mod)) { |
1166 | | /* skip data nodes from different modules */ |
1167 | 0 | continue; |
1168 | 0 | } |
1169 | | |
1170 | | /* apply relevant nodes from the diff datatree */ |
1171 | 0 | ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst); |
1172 | 0 | if (ret) { |
1173 | 0 | break; |
1174 | 0 | } |
1175 | 0 | } |
1176 | |
|
1177 | 0 | lyd_diff_dup_inst_free(dup_inst); |
1178 | 0 | return ret; |
1179 | 0 | } |
1180 | | |
1181 | | API LY_ERR |
1182 | | lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff) |
1183 | 0 | { |
1184 | 0 | return lyd_diff_apply_module(data, diff, NULL, NULL, NULL); |
1185 | 0 | } |
1186 | | |
1187 | | /** |
1188 | | * @brief Update operations on a diff node when the new operation is NONE. |
1189 | | * |
1190 | | * @param[in] diff_match Node from the diff. |
1191 | | * @param[in] cur_op Current operation of @p diff_match. |
1192 | | * @param[in] src_diff Current source diff node. |
1193 | | * @return LY_ERR value. |
1194 | | */ |
1195 | | static LY_ERR |
1196 | | lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff) |
1197 | 0 | { |
1198 | 0 | switch (cur_op) { |
1199 | 0 | case LYD_DIFF_OP_NONE: |
1200 | 0 | case LYD_DIFF_OP_CREATE: |
1201 | 0 | case LYD_DIFF_OP_REPLACE: |
1202 | 0 | if (src_diff->schema->nodetype & LYD_NODE_TERM) { |
1203 | | /* NONE on a term means only its dflt flag was changed */ |
1204 | 0 | diff_match->flags &= ~LYD_DEFAULT; |
1205 | 0 | diff_match->flags |= src_diff->flags & LYD_DEFAULT; |
1206 | 0 | } |
1207 | 0 | break; |
1208 | 0 | default: |
1209 | | /* delete operation is not valid */ |
1210 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1211 | 0 | } |
1212 | | |
1213 | 0 | return LY_SUCCESS; |
1214 | 0 | } |
1215 | | |
1216 | | /** |
1217 | | * @brief Remove an attribute from a node. |
1218 | | * |
1219 | | * @param[in] node Node with the metadata. |
1220 | | * @param[in] name Metadata name. |
1221 | | */ |
1222 | | static void |
1223 | | lyd_diff_del_meta(struct lyd_node *node, const char *name) |
1224 | 0 | { |
1225 | 0 | struct lyd_meta *meta; |
1226 | |
|
1227 | 0 | LY_LIST_FOR(node->meta, meta) { |
1228 | 0 | if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) { |
1229 | 0 | lyd_free_meta_single(meta); |
1230 | 0 | return; |
1231 | 0 | } |
1232 | 0 | } |
1233 | | |
1234 | 0 | assert(0); |
1235 | 0 | } |
1236 | | |
1237 | | /** |
1238 | | * @brief Set a specific operation of a node. Delete the previous operation, if any. |
1239 | | * Does not change the default flag. |
1240 | | * |
1241 | | * @param[in] node Node to change. |
1242 | | * @param[in] op Operation to set. |
1243 | | * @return LY_ERR value. |
1244 | | */ |
1245 | | static LY_ERR |
1246 | | lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op) |
1247 | 0 | { |
1248 | 0 | struct lyd_meta *meta; |
1249 | |
|
1250 | 0 | LY_LIST_FOR(node->meta, meta) { |
1251 | 0 | if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) { |
1252 | 0 | lyd_free_meta_single(meta); |
1253 | 0 | break; |
1254 | 0 | } |
1255 | 0 | } |
1256 | |
|
1257 | 0 | return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL); |
1258 | 0 | } |
1259 | | |
1260 | | /** |
1261 | | * @brief Update operations on a diff node when the new operation is REPLACE. |
1262 | | * |
1263 | | * @param[in] diff_match Node from the diff. |
1264 | | * @param[in] cur_op Current operation of @p diff_match. |
1265 | | * @param[in] src_diff Current source diff node. |
1266 | | * @return LY_ERR value. |
1267 | | */ |
1268 | | static LY_ERR |
1269 | | lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff) |
1270 | 0 | { |
1271 | 0 | LY_ERR ret; |
1272 | 0 | const char *str_val, *meta_name, *orig_meta_name; |
1273 | 0 | struct lyd_meta *meta; |
1274 | 0 | const struct lys_module *mod; |
1275 | 0 | const struct lyd_node_any *any; |
1276 | | |
1277 | | /* get "yang" module for the metadata */ |
1278 | 0 | mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang"); |
1279 | 0 | assert(mod); |
1280 | |
|
1281 | 0 | switch (cur_op) { |
1282 | 0 | case LYD_DIFF_OP_REPLACE: |
1283 | 0 | case LYD_DIFF_OP_CREATE: |
1284 | 0 | switch (diff_match->schema->nodetype) { |
1285 | 0 | case LYS_LIST: |
1286 | 0 | case LYS_LEAFLIST: |
1287 | | /* it was created/moved somewhere, but now it will be created/moved somewhere else, |
1288 | | * keep orig_key/orig_value (only replace oper) and replace key/value */ |
1289 | 0 | assert(lysc_is_userordered(diff_match->schema)); |
1290 | 0 | if (lysc_is_dup_inst_list(diff_match->schema)) { |
1291 | 0 | meta_name = "position"; |
1292 | 0 | } else if (diff_match->schema->nodetype == LYS_LIST) { |
1293 | 0 | meta_name = "key"; |
1294 | 0 | } else { |
1295 | 0 | meta_name = "value"; |
1296 | 0 | } |
1297 | |
|
1298 | 0 | lyd_diff_del_meta(diff_match, meta_name); |
1299 | 0 | meta = lyd_find_meta(src_diff->meta, mod, meta_name); |
1300 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT); |
1301 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL)); |
1302 | 0 | break; |
1303 | 0 | case LYS_LEAF: |
1304 | | /* replaced with the exact same value, impossible */ |
1305 | 0 | if (!lyd_compare_single(diff_match, src_diff, 0)) { |
1306 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1307 | 0 | } |
1308 | | |
1309 | | /* modify the node value */ |
1310 | 0 | if (lyd_change_term(diff_match, lyd_get_value(src_diff))) { |
1311 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1312 | 0 | } |
1313 | | |
1314 | 0 | if (cur_op == LYD_DIFF_OP_REPLACE) { |
1315 | | /* compare values whether there is any change at all */ |
1316 | 0 | meta = lyd_find_meta(diff_match->meta, mod, "orig-value"); |
1317 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(diff_match)), LY_EINT); |
1318 | 0 | str_val = lyd_get_meta_value(meta); |
1319 | 0 | ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val)); |
1320 | 0 | if (!ret) { |
1321 | | /* values are the same, remove orig-value meta and set oper to NONE */ |
1322 | 0 | lyd_free_meta_single(meta); |
1323 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
1324 | 0 | } |
1325 | 0 | } |
1326 | | |
1327 | | /* modify the default flag */ |
1328 | 0 | diff_match->flags &= ~LYD_DEFAULT; |
1329 | 0 | diff_match->flags |= src_diff->flags & LYD_DEFAULT; |
1330 | 0 | break; |
1331 | 0 | case LYS_ANYXML: |
1332 | 0 | case LYS_ANYDATA: |
1333 | 0 | if (!lyd_compare_single(diff_match, src_diff, 0)) { |
1334 | | /* replaced with the exact same value, impossible */ |
1335 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1336 | 0 | } |
1337 | | |
1338 | | /* modify the node value */ |
1339 | 0 | any = (struct lyd_node_any *)src_diff; |
1340 | 0 | LY_CHECK_RET(lyd_any_copy_value(diff_match, &any->value, any->value_type)); |
1341 | 0 | break; |
1342 | 0 | default: |
1343 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1344 | 0 | } |
1345 | 0 | break; |
1346 | 0 | case LYD_DIFF_OP_NONE: |
1347 | | /* it is moved now */ |
1348 | 0 | assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST)); |
1349 | | |
1350 | | /* change the operation */ |
1351 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE)); |
1352 | | |
1353 | | /* set orig-meta and meta */ |
1354 | 0 | if (lysc_is_dup_inst_list(diff_match->schema)) { |
1355 | 0 | meta_name = "position"; |
1356 | 0 | orig_meta_name = "orig-position"; |
1357 | 0 | } else { |
1358 | 0 | meta_name = "key"; |
1359 | 0 | orig_meta_name = "orig-key"; |
1360 | 0 | } |
1361 | |
|
1362 | 0 | meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name); |
1363 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT); |
1364 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL)); |
1365 | |
|
1366 | 0 | meta = lyd_find_meta(src_diff->meta, mod, meta_name); |
1367 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT); |
1368 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL)); |
1369 | 0 | break; |
1370 | 0 | default: |
1371 | | /* delete operation is not valid */ |
1372 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1373 | 0 | } |
1374 | | |
1375 | 0 | return LY_SUCCESS; |
1376 | 0 | } |
1377 | | |
1378 | | /** |
1379 | | * @brief Update operations in a diff node when the new operation is CREATE. |
1380 | | * |
1381 | | * @param[in] diff_match Node from the diff. |
1382 | | * @param[in] cur_op Current operation of @p diff_match. |
1383 | | * @param[in] src_diff Current source diff node. |
1384 | | * @param[in] options Diff merge options. |
1385 | | * @return LY_ERR value. |
1386 | | */ |
1387 | | static LY_ERR |
1388 | | lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options) |
1389 | 0 | { |
1390 | 0 | struct lyd_node *child; |
1391 | 0 | const struct lysc_node_leaf *sleaf = NULL; |
1392 | 0 | uint32_t trg_flags; |
1393 | 0 | const char *meta_name, *orig_meta_name; |
1394 | 0 | struct lyd_meta *meta, *orig_meta; |
1395 | |
|
1396 | 0 | switch (cur_op) { |
1397 | 0 | case LYD_DIFF_OP_DELETE: |
1398 | | /* remember current flags */ |
1399 | 0 | trg_flags = diff_match->flags; |
1400 | |
|
1401 | 0 | if (lysc_is_userordered(diff_match->schema)) { |
1402 | | /* get anchor metadata */ |
1403 | 0 | if (lysc_is_dup_inst_list(diff_match->schema)) { |
1404 | 0 | meta_name = "yang:position"; |
1405 | 0 | orig_meta_name = "yang:orig-position"; |
1406 | 0 | } else if (diff_match->schema->nodetype == LYS_LIST) { |
1407 | 0 | meta_name = "yang:key"; |
1408 | 0 | orig_meta_name = "yang:orig-key"; |
1409 | 0 | } else { |
1410 | 0 | meta_name = "yang:value"; |
1411 | 0 | orig_meta_name = "yang:orig-value"; |
1412 | 0 | } |
1413 | 0 | meta = lyd_find_meta(src_diff->meta, NULL, meta_name); |
1414 | 0 | orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name); |
1415 | 0 | LY_CHECK_ERR_RET(!meta || !orig_meta, LOGINT(LYD_CTX(src_diff)), LY_EINT); |
1416 | | |
1417 | | /* the (incorrect) assumption made here is that there are no previous diff nodes that would affect |
1418 | | * the anchors stored in the metadata */ |
1419 | 0 | if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) { |
1420 | | /* deleted + created at another position -> operation REPLACE */ |
1421 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE)); |
1422 | | |
1423 | | /* add anchor metadata */ |
1424 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL)); |
1425 | 0 | } else { |
1426 | | /* deleted + created at the same position -> operation NONE */ |
1427 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
1428 | | |
1429 | | /* delete anchor metadata */ |
1430 | 0 | lyd_free_meta_single(orig_meta); |
1431 | 0 | } |
1432 | 0 | } else if (diff_match->schema->nodetype == LYS_LEAF) { |
1433 | 0 | if (options & LYD_DIFF_MERGE_DEFAULTS) { |
1434 | | /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */ |
1435 | 0 | sleaf = (struct lysc_node_leaf *)diff_match->schema; |
1436 | 0 | } |
1437 | |
|
1438 | 0 | if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt, |
1439 | 0 | &((struct lyd_node_term *)src_diff)->value)) { |
1440 | | /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */ |
1441 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
1442 | 0 | } else if (!lyd_compare_single(diff_match, src_diff, 0)) { |
1443 | | /* deleted + created -> operation NONE */ |
1444 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
1445 | 0 | } else { |
1446 | | /* we deleted it, but it was created with a different value -> operation REPLACE */ |
1447 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE)); |
1448 | | |
1449 | | /* current value is the previous one (meta) */ |
1450 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value", |
1451 | 0 | lyd_get_value(diff_match), 0, NULL)); |
1452 | | |
1453 | | /* update the value itself */ |
1454 | 0 | LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff))); |
1455 | 0 | } |
1456 | 0 | } else { |
1457 | | /* deleted + created -> operation NONE */ |
1458 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
1459 | 0 | } |
1460 | | |
1461 | 0 | if (diff_match->schema->nodetype & LYD_NODE_TERM) { |
1462 | | /* add orig-dflt metadata */ |
1463 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default", |
1464 | 0 | trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL)); |
1465 | | |
1466 | | /* update dflt flag itself */ |
1467 | 0 | diff_match->flags &= ~LYD_DEFAULT; |
1468 | 0 | diff_match->flags |= src_diff->flags & LYD_DEFAULT; |
1469 | 0 | } |
1470 | | |
1471 | | /* but the operation of its children should remain DELETE */ |
1472 | 0 | LY_LIST_FOR(lyd_child_no_keys(diff_match), child) { |
1473 | 0 | LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE)); |
1474 | 0 | } |
1475 | 0 | break; |
1476 | 0 | default: |
1477 | | /* create and replace operations are not valid */ |
1478 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1479 | 0 | } |
1480 | | |
1481 | 0 | return LY_SUCCESS; |
1482 | 0 | } |
1483 | | |
1484 | | /** |
1485 | | * @brief Update operations on a diff node when the new operation is DELETE. |
1486 | | * |
1487 | | * @param[in] diff_match Node from the diff. |
1488 | | * @param[in] cur_op Current operation of @p diff_match. |
1489 | | * @param[in] src_diff Current source diff node. |
1490 | | * @return LY_ERR value. |
1491 | | */ |
1492 | | static LY_ERR |
1493 | | lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff) |
1494 | 0 | { |
1495 | 0 | struct lyd_node *next, *child; |
1496 | 0 | struct lyd_meta *meta; |
1497 | 0 | const char *meta_name; |
1498 | | |
1499 | | /* we can delete only exact existing nodes */ |
1500 | 0 | LY_CHECK_ERR_RET(lyd_compare_single(diff_match, src_diff, 0), LOGINT(LYD_CTX(src_diff)), LY_EINT); |
1501 | |
|
1502 | 0 | switch (cur_op) { |
1503 | 0 | case LYD_DIFF_OP_CREATE: |
1504 | | /* it was created, but then deleted -> set NONE operation */ |
1505 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
1506 | |
|
1507 | 0 | if (diff_match->schema->nodetype & LYD_NODE_TERM) { |
1508 | | /* add orig-default meta because it is expected */ |
1509 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default", |
1510 | 0 | diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL)); |
1511 | 0 | } else if (!lysc_is_dup_inst_list(diff_match->schema)) { |
1512 | | /* keep operation for all descendants (for now) */ |
1513 | 0 | LY_LIST_FOR(lyd_child_no_keys(diff_match), child) { |
1514 | 0 | LY_CHECK_RET(lyd_diff_change_op(child, cur_op)); |
1515 | 0 | } |
1516 | 0 | } /* else key-less list, for which all the descendants act as keys */ |
1517 | 0 | break; |
1518 | 0 | case LYD_DIFF_OP_REPLACE: |
1519 | | /* similar to none operation but also remove the redundant metadata */ |
1520 | 0 | if (lysc_is_userordered(diff_match->schema)) { |
1521 | 0 | if (lysc_is_dup_inst_list(diff_match->schema)) { |
1522 | 0 | meta_name = "position"; |
1523 | 0 | } else if (diff_match->schema->nodetype == LYS_LIST) { |
1524 | 0 | meta_name = "key"; |
1525 | 0 | } else { |
1526 | 0 | meta_name = "value"; |
1527 | 0 | } |
1528 | 0 | } else { |
1529 | 0 | assert(diff_match->schema->nodetype == LYS_LEAF); |
1530 | | |
1531 | | /* switch value for the original one */ |
1532 | 0 | meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value"); |
1533 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT); |
1534 | 0 | if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) { |
1535 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1536 | 0 | } |
1537 | | |
1538 | | /* switch default for the original one, then remove the meta */ |
1539 | 0 | meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default"); |
1540 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(src_diff)), LY_EINT); |
1541 | 0 | diff_match->flags &= ~LYD_DEFAULT; |
1542 | 0 | if (meta->value.boolean) { |
1543 | 0 | diff_match->flags |= LYD_DEFAULT; |
1544 | 0 | } |
1545 | 0 | lyd_free_meta_single(meta); |
1546 | |
|
1547 | 0 | meta_name = "orig-value"; |
1548 | 0 | } |
1549 | 0 | lyd_diff_del_meta(diff_match, meta_name); |
1550 | | |
1551 | | /* fall through */ |
1552 | 0 | case LYD_DIFF_OP_NONE: |
1553 | | /* it was not modified, but should be deleted -> set DELETE operation */ |
1554 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE)); |
1555 | | |
1556 | | /* all descendants not in the diff will be deleted and redundant in the diff, so remove them */ |
1557 | 0 | LY_LIST_FOR_SAFE(lyd_child_no_keys(diff_match), next, child) { |
1558 | 0 | if (lyd_find_sibling_first(lyd_child(src_diff), child, NULL) == LY_ENOTFOUND) { |
1559 | 0 | lyd_free_tree(child); |
1560 | 0 | } |
1561 | 0 | } |
1562 | 0 | break; |
1563 | 0 | default: |
1564 | | /* delete operation is not valid */ |
1565 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1566 | 0 | } |
1567 | | |
1568 | 0 | return LY_SUCCESS; |
1569 | 0 | } |
1570 | | |
1571 | | /** |
1572 | | * @brief Check whether this diff node is redundant (does not change data). |
1573 | | * |
1574 | | * @param[in] diff Diff node. |
1575 | | * @return 0 if not, non-zero if it is. |
1576 | | */ |
1577 | | static int |
1578 | | lyd_diff_is_redundant(struct lyd_node *diff) |
1579 | 0 | { |
1580 | 0 | enum lyd_diff_op op; |
1581 | 0 | struct lyd_meta *meta, *orig_val_meta = NULL, *val_meta = NULL; |
1582 | 0 | struct lyd_node *child; |
1583 | 0 | const struct lys_module *mod; |
1584 | 0 | const char *str, *orig_meta_name, *meta_name; |
1585 | |
|
1586 | 0 | assert(diff); |
1587 | |
|
1588 | 0 | if (lysc_is_dup_inst_list(diff->schema)) { |
1589 | | /* all descendants are keys */ |
1590 | 0 | child = NULL; |
1591 | 0 | } else { |
1592 | 0 | child = lyd_child_no_keys(diff); |
1593 | 0 | } |
1594 | 0 | mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang"); |
1595 | 0 | assert(mod); |
1596 | | |
1597 | | /* get node operation */ |
1598 | 0 | LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0); |
1599 | |
|
1600 | 0 | if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) { |
1601 | | /* get metadata names */ |
1602 | 0 | if (lysc_is_dup_inst_list(diff->schema)) { |
1603 | 0 | meta_name = "position"; |
1604 | 0 | orig_meta_name = "orig-position"; |
1605 | 0 | } else if (diff->schema->nodetype == LYS_LIST) { |
1606 | 0 | meta_name = "key"; |
1607 | 0 | orig_meta_name = "orig-key"; |
1608 | 0 | } else { |
1609 | 0 | meta_name = "value"; |
1610 | 0 | orig_meta_name = "orig-value"; |
1611 | 0 | } |
1612 | | |
1613 | | /* check for redundant move */ |
1614 | 0 | orig_val_meta = lyd_find_meta(diff->meta, mod, orig_meta_name); |
1615 | 0 | val_meta = lyd_find_meta(diff->meta, mod, meta_name); |
1616 | 0 | assert(orig_val_meta && val_meta); |
1617 | |
|
1618 | 0 | if (!lyd_compare_meta(orig_val_meta, val_meta)) { |
1619 | | /* there is actually no move */ |
1620 | 0 | lyd_free_meta_single(orig_val_meta); |
1621 | 0 | lyd_free_meta_single(val_meta); |
1622 | 0 | if (child) { |
1623 | | /* change operation to NONE, we have siblings */ |
1624 | 0 | lyd_diff_change_op(diff, LYD_DIFF_OP_NONE); |
1625 | 0 | return 0; |
1626 | 0 | } |
1627 | | |
1628 | | /* redundant node, BUT !! |
1629 | | * In diff the move operation is always converted to be INSERT_AFTER, which is fine |
1630 | | * because the data that this is applied on should not change for the diff lifetime. |
1631 | | * However, when we are merging 2 diffs, this conversion is actually lossy because |
1632 | | * if the data change, the move operation can also change its meaning. In this specific |
1633 | | * case the move operation will be lost. But it can be considered a feature, it is not supported. |
1634 | | */ |
1635 | 0 | return 1; |
1636 | 0 | } |
1637 | 0 | } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) { |
1638 | | /* check whether at least the default flags are different */ |
1639 | 0 | meta = lyd_find_meta(diff->meta, mod, "orig-default"); |
1640 | 0 | assert(meta); |
1641 | 0 | str = lyd_get_meta_value(meta); |
1642 | | |
1643 | | /* if previous and current dflt flags are the same, this node is redundant */ |
1644 | 0 | if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) { |
1645 | 0 | return 1; |
1646 | 0 | } |
1647 | 0 | return 0; |
1648 | 0 | } |
1649 | | |
1650 | 0 | if (!child && (op == LYD_DIFF_OP_NONE)) { |
1651 | 0 | return 1; |
1652 | 0 | } |
1653 | | |
1654 | 0 | return 0; |
1655 | 0 | } |
1656 | | |
1657 | | /** |
1658 | | * @brief Merge sysrepo diff subtree with another diff, recursively. |
1659 | | * |
1660 | | * @param[in] src_diff Source diff node. |
1661 | | * @param[in] diff_parent Current sysrepo diff parent. |
1662 | | * @param[in] diff_cb Optional diff callback. |
1663 | | * @param[in] cb_data User data for @p diff_cb. |
1664 | | * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings. |
1665 | | * @param[in] options Diff merge options. |
1666 | | * @param[in,out] diff Diff root node. |
1667 | | * @return LY_ERR value. |
1668 | | */ |
1669 | | static LY_ERR |
1670 | | lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data, |
1671 | | struct lyd_diff_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff) |
1672 | 0 | { |
1673 | 0 | LY_ERR ret = LY_SUCCESS; |
1674 | 0 | struct lyd_node *child, *diff_node = NULL; |
1675 | 0 | enum lyd_diff_op src_op, cur_op; |
1676 | 0 | struct lyd_diff_dup_inst *child_dup_inst = NULL; |
1677 | | |
1678 | | /* get source node operation */ |
1679 | 0 | LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op)); |
1680 | | |
1681 | | /* find an equal node in the current diff */ |
1682 | 0 | LY_CHECK_RET(lyd_diff_find_match(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, 1, dup_inst, &diff_node)); |
1683 | |
|
1684 | 0 | if (diff_node) { |
1685 | | /* get target (current) operation */ |
1686 | 0 | LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op)); |
1687 | | |
1688 | | /* merge operations */ |
1689 | 0 | switch (src_op) { |
1690 | 0 | case LYD_DIFF_OP_REPLACE: |
1691 | 0 | ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff); |
1692 | 0 | break; |
1693 | 0 | case LYD_DIFF_OP_CREATE: |
1694 | 0 | if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) { |
1695 | | /* special case of creating duplicate (leaf-)list instances */ |
1696 | 0 | goto add_diff; |
1697 | 0 | } |
1698 | | |
1699 | 0 | ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options); |
1700 | 0 | break; |
1701 | 0 | case LYD_DIFF_OP_DELETE: |
1702 | 0 | ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff); |
1703 | 0 | break; |
1704 | 0 | case LYD_DIFF_OP_NONE: |
1705 | | /* key-less list can never have "none" operation since all its descendants are acting as "keys" */ |
1706 | 0 | assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema)); |
1707 | 0 | ret = lyd_diff_merge_none(diff_node, cur_op, src_diff); |
1708 | 0 | break; |
1709 | 0 | default: |
1710 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
1711 | 0 | } |
1712 | 0 | if (ret) { |
1713 | 0 | LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op)); |
1714 | 0 | return ret; |
1715 | 0 | } |
1716 | | |
1717 | 0 | if (diff_cb) { |
1718 | | /* call callback */ |
1719 | 0 | LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data)); |
1720 | 0 | } |
1721 | | |
1722 | | /* update diff parent */ |
1723 | 0 | diff_parent = diff_node; |
1724 | | |
1725 | | /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances), |
1726 | | * so there is nothing to merge for these "keys" */ |
1727 | 0 | if (!lysc_is_dup_inst_list(src_diff->schema)) { |
1728 | | /* merge src_diff recursively */ |
1729 | 0 | LY_LIST_FOR(lyd_child_no_keys(src_diff), child) { |
1730 | 0 | ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff); |
1731 | 0 | if (ret) { |
1732 | 0 | break; |
1733 | 0 | } |
1734 | 0 | } |
1735 | 0 | lyd_diff_dup_inst_free(child_dup_inst); |
1736 | 0 | LY_CHECK_RET(ret); |
1737 | 0 | } |
1738 | 0 | } else { |
1739 | 0 | add_diff: |
1740 | | /* add new diff node with all descendants */ |
1741 | 0 | LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, |
1742 | 0 | &diff_node)); |
1743 | | |
1744 | | /* insert node into diff if not already */ |
1745 | 0 | if (!diff_parent) { |
1746 | 0 | lyd_insert_sibling(*diff, diff_node, diff); |
1747 | 0 | } |
1748 | | |
1749 | | /* update operation */ |
1750 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op)); |
1751 | |
|
1752 | 0 | if (diff_cb) { |
1753 | | /* call callback with no source diff node since it was duplicated and just added */ |
1754 | 0 | LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data)); |
1755 | 0 | } |
1756 | | |
1757 | | /* update diff parent */ |
1758 | 0 | diff_parent = diff_node; |
1759 | 0 | } |
1760 | | |
1761 | | /* remove any redundant nodes */ |
1762 | 0 | if (lyd_diff_is_redundant(diff_parent)) { |
1763 | 0 | if (diff_parent == *diff) { |
1764 | 0 | *diff = (*diff)->next; |
1765 | 0 | } |
1766 | 0 | lyd_free_tree(diff_parent); |
1767 | 0 | } |
1768 | |
|
1769 | 0 | return LY_SUCCESS; |
1770 | 0 | } |
1771 | | |
1772 | | API LY_ERR |
1773 | | lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod, |
1774 | | lyd_diff_cb diff_cb, void *cb_data, uint16_t options) |
1775 | 0 | { |
1776 | 0 | const struct lyd_node *src_root; |
1777 | 0 | struct lyd_diff_dup_inst *dup_inst = NULL; |
1778 | 0 | LY_ERR ret = LY_SUCCESS; |
1779 | |
|
1780 | 0 | LY_LIST_FOR(src_diff, src_root) { |
1781 | 0 | if (mod && (lyd_owner_module(src_root) != mod)) { |
1782 | | /* skip data nodes from different modules */ |
1783 | 0 | continue; |
1784 | 0 | } |
1785 | | |
1786 | | /* apply relevant nodes from the diff datatree */ |
1787 | 0 | LY_CHECK_GOTO(ret = lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, &dup_inst, options, diff), cleanup); |
1788 | 0 | } |
1789 | | |
1790 | 0 | cleanup: |
1791 | 0 | lyd_diff_dup_inst_free(dup_inst); |
1792 | 0 | return ret; |
1793 | 0 | } |
1794 | | |
1795 | | API LY_ERR |
1796 | | lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent, const struct lyd_node *src_sibling, |
1797 | | lyd_diff_cb diff_cb, void *cb_data, uint16_t options) |
1798 | 0 | { |
1799 | 0 | LY_ERR ret; |
1800 | 0 | struct lyd_diff_dup_inst *dup_inst = NULL; |
1801 | |
|
1802 | 0 | if (!src_sibling) { |
1803 | 0 | return LY_SUCCESS; |
1804 | 0 | } |
1805 | | |
1806 | 0 | ret = lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, &dup_inst, options, diff_first); |
1807 | 0 | lyd_diff_dup_inst_free(dup_inst); |
1808 | 0 | return ret; |
1809 | 0 | } |
1810 | | |
1811 | | API LY_ERR |
1812 | | lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options) |
1813 | 0 | { |
1814 | 0 | return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options); |
1815 | 0 | } |
1816 | | |
1817 | | static LY_ERR |
1818 | | lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod) |
1819 | 0 | { |
1820 | 0 | LY_ERR ret = LY_SUCCESS; |
1821 | 0 | struct lyd_meta *meta; |
1822 | 0 | const char *val1 = NULL; |
1823 | 0 | char *val2; |
1824 | 0 | uint32_t flags; |
1825 | |
|
1826 | 0 | assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA)); |
1827 | |
|
1828 | 0 | meta = lyd_find_meta(node->meta, mod, "orig-value"); |
1829 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(LYD_CTX(node)), LY_EINT); |
1830 | | |
1831 | | /* orig-value */ |
1832 | 0 | val1 = lyd_get_meta_value(meta); |
1833 | | |
1834 | | /* current value */ |
1835 | 0 | if (node->schema->nodetype == LYS_LEAF) { |
1836 | 0 | val2 = strdup(lyd_get_value(node)); |
1837 | 0 | } else { |
1838 | 0 | LY_CHECK_RET(lyd_any_value_str(node, &val2)); |
1839 | 0 | } |
1840 | | |
1841 | | /* switch values, keep default flag */ |
1842 | 0 | flags = node->flags; |
1843 | 0 | if (node->schema->nodetype == LYS_LEAF) { |
1844 | 0 | LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup); |
1845 | 0 | } else { |
1846 | 0 | union lyd_any_value anyval = {.str = val1}; |
1847 | 0 | LY_CHECK_GOTO(ret = lyd_any_copy_value(node, &anyval, LYD_ANYDATA_STRING), cleanup); |
1848 | 0 | } |
1849 | 0 | node->flags = flags; |
1850 | 0 | LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup); |
1851 | |
|
1852 | 0 | cleanup: |
1853 | 0 | free(val2); |
1854 | 0 | return ret; |
1855 | 0 | } |
1856 | | |
1857 | | static LY_ERR |
1858 | | lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod) |
1859 | 0 | { |
1860 | 0 | struct lyd_meta *meta; |
1861 | 0 | uint32_t flag1, flag2; |
1862 | |
|
1863 | 0 | meta = lyd_find_meta(node->meta, mod, "orig-default"); |
1864 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT); |
1865 | | |
1866 | | /* orig-default */ |
1867 | 0 | if (meta->value.boolean) { |
1868 | 0 | flag1 = LYD_DEFAULT; |
1869 | 0 | } else { |
1870 | 0 | flag1 = 0; |
1871 | 0 | } |
1872 | | |
1873 | | /* current default */ |
1874 | 0 | flag2 = node->flags & LYD_DEFAULT; |
1875 | |
|
1876 | 0 | if (flag1 == flag2) { |
1877 | | /* no default state change so nothing to reverse */ |
1878 | 0 | return LY_SUCCESS; |
1879 | 0 | } |
1880 | | |
1881 | | /* switch defaults */ |
1882 | 0 | node->flags &= ~LYD_DEFAULT; |
1883 | 0 | node->flags |= flag1; |
1884 | 0 | LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false")); |
1885 | |
|
1886 | 0 | return LY_SUCCESS; |
1887 | 0 | } |
1888 | | |
1889 | | static LY_ERR |
1890 | | lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2) |
1891 | 0 | { |
1892 | 0 | LY_ERR ret = LY_SUCCESS; |
1893 | 0 | struct lyd_meta *meta1, *meta2; |
1894 | 0 | const char *val1 = NULL; |
1895 | 0 | char *val2 = NULL; |
1896 | |
|
1897 | 0 | meta1 = lyd_find_meta(node->meta, mod, name1); |
1898 | 0 | LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_CTX(node)), LY_EINT); |
1899 | |
|
1900 | 0 | meta2 = lyd_find_meta(node->meta, mod, name2); |
1901 | 0 | LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_CTX(node)), LY_EINT); |
1902 | | |
1903 | | /* value1 */ |
1904 | 0 | val1 = lyd_get_meta_value(meta1); |
1905 | | |
1906 | | /* value2 */ |
1907 | 0 | val2 = strdup(lyd_get_meta_value(meta2)); |
1908 | | |
1909 | | /* switch values */ |
1910 | 0 | LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup); |
1911 | 0 | LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup); |
1912 | |
|
1913 | 0 | cleanup: |
1914 | 0 | free(val2); |
1915 | 0 | return ret; |
1916 | 0 | } |
1917 | | |
1918 | | /** |
1919 | | * @brief Remove specific operation from all the nodes in a subtree. |
1920 | | * |
1921 | | * @param[in] diff Diff subtree to process. |
1922 | | * @param[in] op Only expected operation. |
1923 | | * @return LY_ERR value. |
1924 | | */ |
1925 | | static LY_ERR |
1926 | | lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op) |
1927 | 0 | { |
1928 | 0 | struct lyd_node *elem; |
1929 | 0 | struct lyd_meta *meta; |
1930 | |
|
1931 | 0 | LYD_TREE_DFS_BEGIN(diff, elem) { |
1932 | 0 | meta = lyd_find_meta(elem->meta, NULL, "yang:operation"); |
1933 | 0 | if (meta) { |
1934 | 0 | LY_CHECK_ERR_RET(lyd_diff_str2op(lyd_get_meta_value(meta)) != op, LOGINT(LYD_CTX(diff)), LY_EINT); |
1935 | 0 | lyd_free_meta_single(meta); |
1936 | 0 | } |
1937 | | |
1938 | 0 | LYD_TREE_DFS_END(diff, elem); |
1939 | 0 | } |
1940 | | |
1941 | 0 | return LY_SUCCESS; |
1942 | 0 | } |
1943 | | |
1944 | | API LY_ERR |
1945 | | lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) |
1946 | 0 | { |
1947 | 0 | LY_ERR ret = LY_SUCCESS; |
1948 | 0 | const struct lys_module *mod; |
1949 | 0 | struct lyd_node *root, *elem, *iter; |
1950 | 0 | enum lyd_diff_op op; |
1951 | |
|
1952 | 0 | LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL); |
1953 | |
|
1954 | 0 | if (!src_diff) { |
1955 | 0 | *diff = NULL; |
1956 | 0 | return LY_SUCCESS; |
1957 | 0 | } |
1958 | | |
1959 | | /* duplicate diff */ |
1960 | 0 | LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff)); |
1961 | | |
1962 | | /* find module with metadata needed for later */ |
1963 | 0 | mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang"); |
1964 | 0 | LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup); |
1965 | |
|
1966 | 0 | LY_LIST_FOR(*diff, root) { |
1967 | 0 | LYD_TREE_DFS_BEGIN(root, elem) { |
1968 | | /* skip all keys */ |
1969 | 0 | if (!lysc_is_key(elem->schema)) { |
1970 | | /* find operation attribute, if any */ |
1971 | 0 | LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup); |
1972 | |
|
1973 | 0 | switch (op) { |
1974 | 0 | case LYD_DIFF_OP_CREATE: |
1975 | | /* reverse create to delete */ |
1976 | 0 | LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup); |
1977 | | |
1978 | | /* check all the children for the same operation, nothing else is expected */ |
1979 | 0 | LY_LIST_FOR(lyd_child(elem), iter) { |
1980 | 0 | lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_CREATE); |
1981 | 0 | } |
1982 | |
|
1983 | 0 | LYD_TREE_DFS_continue = 1; |
1984 | 0 | break; |
1985 | 0 | case LYD_DIFF_OP_DELETE: |
1986 | | /* reverse delete to create */ |
1987 | 0 | LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup); |
1988 | | |
1989 | | /* check all the children for the same operation, nothing else is expected */ |
1990 | 0 | LY_LIST_FOR(lyd_child(elem), iter) { |
1991 | 0 | lyd_diff_reverse_remove_op_r(iter, LYD_DIFF_OP_DELETE); |
1992 | 0 | } |
1993 | |
|
1994 | 0 | LYD_TREE_DFS_continue = 1; |
1995 | 0 | break; |
1996 | 0 | case LYD_DIFF_OP_REPLACE: |
1997 | 0 | switch (elem->schema->nodetype) { |
1998 | 0 | case LYS_LEAF: |
1999 | | /* leaf value change */ |
2000 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup); |
2001 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup); |
2002 | 0 | break; |
2003 | 0 | case LYS_ANYXML: |
2004 | 0 | case LYS_ANYDATA: |
2005 | | /* any value change */ |
2006 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup); |
2007 | 0 | break; |
2008 | 0 | case LYS_LEAFLIST: |
2009 | | /* leaf-list move */ |
2010 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup); |
2011 | 0 | if (lysc_is_dup_inst_list(elem->schema)) { |
2012 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup); |
2013 | 0 | } else { |
2014 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup); |
2015 | 0 | } |
2016 | 0 | break; |
2017 | 0 | case LYS_LIST: |
2018 | | /* list move */ |
2019 | 0 | if (lysc_is_dup_inst_list(elem->schema)) { |
2020 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup); |
2021 | 0 | } else { |
2022 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup); |
2023 | 0 | } |
2024 | 0 | break; |
2025 | 0 | default: |
2026 | 0 | LOGINT(LYD_CTX(src_diff)); |
2027 | 0 | ret = LY_EINT; |
2028 | 0 | goto cleanup; |
2029 | 0 | } |
2030 | 0 | break; |
2031 | 0 | case LYD_DIFF_OP_NONE: |
2032 | 0 | switch (elem->schema->nodetype) { |
2033 | 0 | case LYS_LEAF: |
2034 | 0 | case LYS_LEAFLIST: |
2035 | | /* default flag change */ |
2036 | 0 | LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup); |
2037 | 0 | break; |
2038 | 0 | default: |
2039 | | /* nothing to do */ |
2040 | 0 | break; |
2041 | 0 | } |
2042 | 0 | break; |
2043 | 0 | } |
2044 | 0 | } |
2045 | | |
2046 | 0 | LYD_TREE_DFS_END(root, elem); |
2047 | 0 | } |
2048 | 0 | } |
2049 | | |
2050 | 0 | cleanup: |
2051 | 0 | if (ret) { |
2052 | 0 | lyd_free_siblings(*diff); |
2053 | | *diff = NULL; |
2054 | 0 | } |
2055 | 0 | return ret; |
2056 | 0 | } |